"""Generate and validate the dockerfile."""

from dataclasses import dataclass
from pathlib import Path

from homeassistant import core
from homeassistant.util import executor, thread

from .model import Config, Integration

# Don't forget to update also Dockerfile.dev when updating this.
_DOCKERFILE_SYNTAX_SHA = (
    "2780b5c3bab67f1f76c781860de469442999ed1a0d7992a5efdf2cffc0e3d769"  # 1.23.0
)

_GO2RTC_SHA = (
    "675c318b23c06fd862a61d262240c9a63436b4050d177ffc68a32710d9e05bae"  # 1.9.14
)

DOCKERFILE_TEMPLATE = r"""# syntax=docker/dockerfile@sha256:{dockerfile_syntax}
# Automatically generated by hassfest.
#
# To update, run python3 -m script.hassfest -p docker
ARG BUILD_FROM
FROM ${{BUILD_FROM}}

LABEL \
    io.hass.type="core" \
    org.opencontainers.image.authors="The Home Assistant Authors" \
    org.opencontainers.image.description="Open-source home automation platform running on Python 3" \
    org.opencontainers.image.documentation="https://www.home-assistant.io/docs/" \
    org.opencontainers.image.licenses="Apache-2.0" \
    org.opencontainers.image.title="Home Assistant" \
    org.opencontainers.image.url="https://www.home-assistant.io/"

# Synchronize with homeassistant/core.py:async_stop
ENV \
    S6_SERVICES_GRACETIME={timeout} \
    UV_SYSTEM_PYTHON=true \
    UV_NO_CACHE=true

WORKDIR /usr/src

# Home Assistant S6-Overlay
COPY rootfs /

# Add go2rtc binary
COPY --from=ghcr.io/alexxit/go2rtc@sha256:{go2rtc} /usr/local/bin/go2rtc /bin/go2rtc

## Setup Home Assistant Core dependencies
COPY --parents requirements.txt homeassistant/package_constraints.txt homeassistant/
RUN \
    # Verify go2rtc can be executed
    go2rtc --version \
    # Install uv at the version pinned in the requirements file
    && pip3 install --no-cache-dir "uv==$(awk -F'==' '/^uv==/{{print $2}}' homeassistant/requirements.txt)" \
    && uv pip install \
        --no-build \
        -r homeassistant/requirements.txt

COPY requirements_all.txt home_assistant_frontend-* home_assistant_intents-* homeassistant/
RUN \
    if ls homeassistant/home_assistant_*.whl 1> /dev/null 2>&1; then \
        uv pip install homeassistant/home_assistant_*.whl; \
    fi \
    && uv pip install \
        --no-build \
        -r homeassistant/requirements_all.txt

## Setup Home Assistant Core
COPY --parents LICENSE* README* homeassistant/ pyproject.toml homeassistant/
RUN \
    uv pip install \
        -e ./homeassistant \
    && python3 -m compileall \
        homeassistant/homeassistant

WORKDIR /config
"""


@dataclass(frozen=True)
class _MachineConfig:
    """Machine-specific Dockerfile configuration."""

    arch: str
    packages: tuple[str, ...] = ()


_MACHINES = {
    "generic-x86-64": _MachineConfig(arch="amd64", packages=("libva-intel-driver",)),
    "green": _MachineConfig(arch="aarch64"),
    "khadas-vim3": _MachineConfig(arch="aarch64"),
    "odroid-c2": _MachineConfig(arch="aarch64"),
    "odroid-c4": _MachineConfig(arch="aarch64"),
    "odroid-m1": _MachineConfig(arch="aarch64"),
    "odroid-n2": _MachineConfig(arch="aarch64"),
    "qemuarm-64": _MachineConfig(arch="aarch64"),
    "qemux86-64": _MachineConfig(arch="amd64"),
    "raspberrypi3-64": _MachineConfig(arch="aarch64", packages=("raspberrypi-utils",)),
    "raspberrypi4-64": _MachineConfig(arch="aarch64", packages=("raspberrypi-utils",)),
    "raspberrypi5-64": _MachineConfig(arch="aarch64", packages=("raspberrypi-utils",)),
    "yellow": _MachineConfig(arch="aarch64", packages=("raspberrypi-utils",)),
}

_MACHINE_DOCKERFILE_TEMPLATE = r"""# syntax=docker/dockerfile@sha256:{dockerfile_syntax}
# Automatically generated by hassfest.
#
# To update, run python3 -m script.hassfest -p docker
ARG BUILD_FROM=ghcr.io/home-assistant/{arch}-homeassistant:latest
FROM ${{BUILD_FROM}}
{extra_packages}
LABEL io.hass.machine="{machine}"
"""


def _generate_machine_dockerfile(
    machine_name: str, machine_config: _MachineConfig
) -> str:
    """Generate a machine Dockerfile from configuration."""
    if machine_config.packages:
        pkg_lines = " \\\n    ".join(machine_config.packages)
        extra_packages = f"\nRUN apk --no-cache add \\\n    {pkg_lines}\n"
    else:
        extra_packages = ""

    return _MACHINE_DOCKERFILE_TEMPLATE.format(
        dockerfile_syntax=_DOCKERFILE_SYNTAX_SHA,
        arch=machine_config.arch,
        extra_packages=extra_packages,
        machine=machine_name,
    )


_HASSFEST_TEMPLATE = r"""# syntax=docker/dockerfile@sha256:{dockerfile_syntax}
# Automatically generated by hassfest.
#
# To update, run python3 -m script.hassfest -p docker
FROM python:{python_version}-alpine

ENV \
    UV_SYSTEM_PYTHON=true \
    UV_EXTRA_INDEX_URL="https://wheels.home-assistant.io/musllinux-index/"

SHELL ["/bin/sh", "-o", "pipefail", "-c"]
ENTRYPOINT ["/usr/src/homeassistant/script/hassfest/docker/entrypoint.sh"]
WORKDIR "/github/workspace"

COPY --parents requirements.txt homeassistant/ script /usr/src/homeassistant/

# Uv creates a lock file in /tmp
RUN --mount=type=tmpfs,target=/tmp \
    --mount=type=bind,source=requirements_test.txt,target=/tmp/requirements_test.txt,readonly \
    --mount=type=bind,source=requirements_test_pre_commit.txt,target=/tmp/requirements_test_pre_commit.txt,readonly \
    # Required for PyTurboJPEG
    apk add --no-cache libturbojpeg \
    # Install uv at the version pinned in the requirements file
    && pip install --no-cache-dir "uv==$(awk -F'==' '/^uv==/{{print $2}}' /usr/src/homeassistant/requirements.txt)" \
    && uv pip install \
        --no-build \
        --no-cache \
        -c /usr/src/homeassistant/homeassistant/package_constraints.txt \
        -r /usr/src/homeassistant/requirements.txt \
        "pipdeptree==$(awk -F'==' '/^pipdeptree==/{{print $2}}' /tmp/requirements_test.txt)" \
        "tqdm==$(awk -F'==' '/^tqdm==/{{print $2}}' /tmp/requirements_test.txt)" \
        "ruff==$(awk -F'==' '/^ruff==/{{print $2}}' /tmp/requirements_test_pre_commit.txt)"

LABEL "name"="hassfest"
LABEL "maintainer"="Home Assistant <hello@home-assistant.io>"

LABEL "com.github.actions.name"="hassfest"
LABEL "com.github.actions.description"="Run hassfest to validate standalone integration repositories"
LABEL "com.github.actions.icon"="terminal"
LABEL "com.github.actions.color"="gray-dark"
"""


def _get_python_version(root: Path) -> str:
    """Extract the Python version from .python-version."""
    return (root / ".python-version").read_text(encoding="UTF-8").strip()


@dataclass
class File:
    """File."""

    content: str
    path: Path


def _generate_files(config: Config) -> list[File]:
    timeout = (
        core.STOPPING_STAGE_SHUTDOWN_TIMEOUT
        + core.STOP_STAGE_SHUTDOWN_TIMEOUT
        + core.FINAL_WRITE_STAGE_SHUTDOWN_TIMEOUT
        + core.CLOSE_STAGE_SHUTDOWN_TIMEOUT
        + executor.EXECUTOR_SHUTDOWN_TIMEOUT
        + thread.THREADING_SHUTDOWN_TIMEOUT
        + 10
    ) * 1000

    files = [
        File(
            DOCKERFILE_TEMPLATE.format(
                dockerfile_syntax=_DOCKERFILE_SYNTAX_SHA,
                timeout=timeout,
                go2rtc=_GO2RTC_SHA,
            ),
            config.root / "Dockerfile",
        ),
        File(
            _HASSFEST_TEMPLATE.format(
                dockerfile_syntax=_DOCKERFILE_SYNTAX_SHA,
                python_version=_get_python_version(config.root),
            ),
            config.root / "script/hassfest/docker/Dockerfile",
        ),
    ]

    for machine_name, machine_config in sorted(_MACHINES.items()):
        files.append(
            File(
                _generate_machine_dockerfile(machine_name, machine_config),
                config.root / "machine" / machine_name,
            )
        )

    return files


def validate(integrations: dict[str, Integration], config: Config) -> None:
    """Validate dockerfile."""
    docker_files = _generate_files(config)
    config.cache["docker"] = docker_files

    for file in docker_files:
        if file.content != file.path.read_text():
            config.add_error(
                "docker",
                f"File {file.path} is not up to date. Run python3 -m script.hassfest",
                fixable=True,
            )


def generate(integrations: dict[str, Integration], config: Config) -> None:
    """Generate dockerfile."""
    for file in _generate_files(config):
        file.path.write_text(file.content)
