"""Docker Engine installation and configuration helpers."""
from __future__ import annotations

from k4s.core.executor import Executor
from k4s.core.executor import ExecutorError
from k4s.core.products import Step
from k4s.recipes.common.linux import (
    apt_install,
    ensure_apt_updated,
    ensure_user_in_group,
)
from k4s.recipes.common.run import check, q, run
from k4s.ui.ui import Ui


def install_docker(
    ui: Ui,
    ex: Executor,
    *,
    data_root: str | None = None,
    docker_group_user: str | None = None,
) -> None:
    """Install Docker Engine from Docker's official Ubuntu repository.

    Optionally configures a custom data-root and adds a user to the
    docker group.
    """
    ui.log("Installing Docker Engine from Docker's Ubuntu repository.")
    ensure_apt_updated(ex)
    apt_install(ex, ["ca-certificates", "curl", "gnupg", "lsb-release"])
    check(ex, "sudo -n install -m 0755 -d /etc/apt/keyrings")
    check(
        ex,
        "curl -fsSL https://download.docker.com/linux/ubuntu/gpg "
        "| gpg --dearmor | sudo -n tee /etc/apt/keyrings/docker.gpg > /dev/null",
    )
    check(ex, "sudo -n chmod a+r /etc/apt/keyrings/docker.gpg")
    check(
        ex,
        'OS_CODENAME=$(lsb_release -cs) && '
        'echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] '
        'https://download.docker.com/linux/ubuntu $OS_CODENAME stable" | '
        "sudo -n tee /etc/apt/sources.list.d/docker.list > /dev/null",
    )
    ensure_apt_updated(ex)
    apt_install(
        ex,
        ["docker-ce", "docker-ce-cli", "containerd.io", "docker-buildx-plugin", "docker-compose-plugin"],
    )

    if data_root:
        ui.log(f"Configuring Docker data-root to {data_root}.")
        check(ex, "sudo -n systemctl stop docker.service || true")
        check(ex, "sudo -n systemctl stop docker.socket || true")
        check(ex, f"sudo -n mkdir -p {q(data_root)}")
        _merge_daemon_json_data_root(ex, data_root)
        check(ex, "sudo -n systemctl start docker")

    if docker_group_user:
        # This is optional (Docker can be used with sudo). If specified, ensure the user exists
        # and can use docker without sudo by adding it to the docker group.
        rc, _, _ = run(ex, f"id -u {q(docker_group_user)} >/dev/null 2>&1")
        if rc != 0:
            raise ExecutorError(
                f"User '{docker_group_user}' does not exist on the target host.\n"
                "Either create the user first or omit --docker-group-user."
            )
        ensure_user_in_group(ex, user=docker_group_user, group="docker")


def _merge_daemon_json_data_root(ex: Executor, data_root: str) -> None:
    """Merge Docker daemon.json and set the data-root key.

    Preserves any existing configuration keys. Creates daemon.json if missing.
    Fails if the existing file is invalid JSON.
    """
    check(ex, "sudo -n install -m 0755 -d /etc/docker")
    cmd = (
        "sudo -n python3 - <<'PY'\n"
        "import json, os, sys\n"
        "path = '/etc/docker/daemon.json'\n"
        f"data_root = {data_root!r}\n"
        "data = {}\n"
        "if os.path.exists(path):\n"
        "    with open(path, 'r', encoding='utf-8') as f:\n"
        "        raw = f.read().strip()\n"
        "    if raw:\n"
        "        try:\n"
        "            data = json.loads(raw)\n"
        "        except Exception as e:\n"
        "            print('Invalid JSON in /etc/docker/daemon.json. Fix it or remove it, then retry.', file=sys.stderr)\n"
        "            raise\n"
        "if not isinstance(data, dict):\n"
        "    raise SystemExit('daemon.json must be a JSON object')\n"
        "data['data-root'] = data_root\n"
        "tmp = path + '.k4s.tmp'\n"
        "with open(tmp, 'w', encoding='utf-8') as f:\n"
        "    json.dump(data, f, indent=2, sort_keys=True)\n"
        "    f.write('\\n')\n"
        "os.replace(tmp, path)\n"
        "os.chmod(path, 0o644)\n"
        "PY"
    )
    check(ex, cmd)


def build_docker_steps(
    ui: Ui,
    ex: Executor,
    *,
    data_root: str | None = None,
    docker_group_user: str | None = None,
) -> list[Step]:
    """Return a list of steps that install and configure Docker."""

    def _run() -> None:
        install_docker(
            ui,
            ex,
            data_root=data_root,
            docker_group_user=docker_group_user,
        )

    return [Step(title="Install and configure Docker", run=_run)]
