from __future__ import annotations

import os
import shutil
import subprocess
from pathlib import Path
from typing import Annotated, List, Optional

import typer

from iac.cli.formatting import build_table, emit_structured, ensure_output_format
from iac.runbook.deps import detect_platform_target
from iac.runbook.discovery import parse_book_dirs, resolve_book
from iac.runbook.loader import load_book
from iac.runbook.model import Requirement

app = typer.Typer(
    add_completion=False, no_args_is_help=True, help="Dependency installation helpers"
)


def _book_dirs() -> List[Path]:
    v = os.environ.get("IAC_BOOK_DIRS")
    return parse_book_dirs(v)


def _probe_py_package(name: str) -> bool:
    spec_expr = repr(name)
    try:
        p = subprocess.run(
            ["python3", "-m", "pip", "show", name],
            capture_output=True,
            text=True,
            timeout=8,
        )
    except Exception:
        p = None
    if p is not None and p.returncode == 0:
        return True
    try:
        ip = subprocess.run(
            [
                "python3",
                "-c",
                f"import importlib.util; print(bool(importlib.util.find_spec({spec_expr})))",
            ],
            capture_output=True,
            text=True,
            timeout=8,
        )
        return ip.returncode == 0 and (ip.stdout or "").strip().lower() == "true"
    except Exception:
        return False


def _resolve_install_hint(req: Requirement) -> tuple[str, str, list[str]]:
    if not req.install_cmd:
        return ("", "", [])
    target = "pip" if req.req_type == "py" else "rocky10"
    return (target, req.install_cmd, [])


@app.command("install")
def deps_install(
    ref: Annotated[str, typer.Argument(help="Book name or path (.yaml/.yml).")],
    output: Annotated[
        str, typer.Option("--output", help="Output format: table|json|yaml")
    ] = "table",
    apply: Annotated[
        bool, typer.Option("--apply", help="Execute installation commands")
    ] = False,
) -> None:
    output_fmt = ensure_output_format(output)
    dirs = _book_dirs()
    bp = resolve_book(ref, dirs)
    b = load_book(bp)

    rows: list[dict] = []
    os_name, distro_id, distro_like = detect_platform_target()
    failures = 0

    for req in b.requires:
        if req.req_type == "py":
            present = _probe_py_package(req.name)
        else:
            present = shutil.which(req.name) is not None
        target, install_cmd, tried = _resolve_install_hint(req)
        action = "none"
        rc = 0
        notes = ""
        if present:
            action = "already_present"
        elif install_cmd and apply:
            action = "install"
            p = subprocess.run(["bash", "-lc", install_cmd], text=True)
            rc = int(p.returncode)
            if rc != 0:
                failures += 1
                notes = "install command failed"
        elif install_cmd:
            action = "dry_run"
        else:
            action = "missing_no_install_hint"
            failures += 1
            notes = "no install command for this host"

        rows.append(
            {
                "requirement": req.name,
                "type": req.req_type,
                "present": present,
                "action": action,
                "exit_code": rc,
                "install_target": target,
                "install_cmd": install_cmd,
                "install_known_targets": [],
                "install_tried_targets": tried,
                "notes": notes,
            }
        )

    payload = {
        "book": str(bp),
        "host": {"os": os_name, "distro": distro_id, "distro_like": distro_like},
        "apply": apply,
        "results": rows,
        "summary": {"total": len(rows), "failures": failures},
    }
    code = 1 if failures > 0 else 0

    if output_fmt in ("json", "yaml"):
        emit_structured(payload, output_fmt)
        raise typer.Exit(code)

    table = build_table(
        [
            "REQUIREMENT",
            "TYPE",
            "PRESENT",
            "ACTION",
            "INSTALL_TARGET",
            "INSTALL_CMD",
            "EXIT",
            "NOTES",
        ],
        [
            [
                r["requirement"],
                r["type"],
                r["present"],
                r["action"],
                r["install_target"],
                r["install_cmd"],
                r["exit_code"],
                r["notes"],
            ]
            for r in rows
        ],
    )
    typer.echo(f"Deps Install: {bp}")
    typer.echo(table.get_string())
    typer.echo(
        f"Summary: total={len(rows)} failures={failures} apply={str(apply).lower()}"
    )
    raise typer.Exit(code)
