"""Doctor check orchestrator."""

from __future__ import annotations

from typing import TYPE_CHECKING

from ilum.doctor.checks import (
    CheckResult,
    CheckStatus,
    check_cluster_connectivity,
    check_health_endpoints,
    check_helm_release,
    check_helm_repo,
    check_namespace,
    check_pod_health,
    check_pvc_status,
    check_rbac_permissions,
    check_resources,
    check_service_endpoints,
    check_storage_class,
    check_version_compatibility,
    get_all_binary_checks,
)

if TYPE_CHECKING:
    from ilum.cli.output import IlumConsole
    from ilum.core.helm import HelmClient
    from ilum.core.kubernetes import KubeClient

_STATUS_ICONS = {
    CheckStatus.OK: "[green]✓[/green]",
    CheckStatus.WARN: "[yellow]![/yellow]",
    CheckStatus.FAIL: "[red]✗[/red]",
    CheckStatus.SKIP: "[dim]–[/dim]",
}


class DoctorRunner:
    def __init__(
        self,
        helm: HelmClient,
        k8s: KubeClient,
        console: IlumConsole,
        namespace: str = "default",
        release: str = "ilum",
        modules: list[str] | None = None,
    ) -> None:
        self._helm = helm
        self._k8s = k8s
        self._console = console
        self._namespace = namespace
        self._release = release
        self._modules = modules or []

    def run_all(self, *, render: bool = True) -> list[CheckResult]:
        results: list[CheckResult] = []

        # Phase 1: Binary checks (always run)
        results.extend(get_all_binary_checks())

        # Phase 2: Helm repo check
        results.append(check_helm_repo(self._helm))

        # Phase 3: Cluster connectivity (gate for downstream checks)
        cluster_result = check_cluster_connectivity(self._k8s)
        results.append(cluster_result)

        if cluster_result.status == CheckStatus.FAIL:
            # Skip all cluster-dependent checks
            for name in (
                "namespace",
                "pods",
                "pvcs",
                "rbac",
                "release",
                "compatibility",
                "storage-class",
                "resources",
                "service-endpoints",
                "health-endpoints",
            ):
                results.append(
                    CheckResult(
                        name=name,
                        status=CheckStatus.SKIP,
                        message="Skipped (cluster unreachable)",
                    )
                )
            if render:
                self._render(results)
            return results

        # Phase 4: Cluster-dependent checks
        results.append(check_namespace(self._k8s, self._namespace))
        results.append(check_pod_health(self._k8s, self._namespace))
        results.append(check_pvc_status(self._k8s, self._namespace))
        results.append(check_rbac_permissions(self._k8s, self._namespace))
        results.append(check_helm_release(self._helm, self._release))
        results.append(check_version_compatibility(self._helm, self._k8s))
        results.append(check_storage_class(self._k8s))

        # Resource estimation (best-effort, uses enabled modules from config)
        results.append(check_resources(self._k8s, self._modules))

        # Service endpoint and health checks
        results.append(check_service_endpoints(self._k8s, self._namespace))
        results.append(check_health_endpoints(self._k8s, self._namespace))

        if render:
            self._render(results)
        return results

    def run_single(self, check_name: str) -> CheckResult | None:
        dispatch: dict[str, CheckResult] = {}

        # Always-available checks
        for r in get_all_binary_checks():
            dispatch[r.name] = r
        dispatch["helm-repo"] = check_helm_repo(self._helm)

        if check_name in dispatch:
            result = dispatch[check_name]
            self._render([result])
            return result

        # Cluster-dependent checks
        cluster_checks = {
            "cluster": lambda: check_cluster_connectivity(self._k8s),
            "namespace": lambda: check_namespace(self._k8s, self._namespace),
            "pods": lambda: check_pod_health(self._k8s, self._namespace),
            "pvcs": lambda: check_pvc_status(self._k8s, self._namespace),
            "rbac": lambda: check_rbac_permissions(self._k8s, self._namespace),
            "release": lambda: check_helm_release(self._helm, self._release),
            "compatibility": lambda: check_version_compatibility(self._helm, self._k8s),
            "storage-class": lambda: check_storage_class(self._k8s),
            "resources": lambda: check_resources(self._k8s, self._modules),
            "service-endpoints": lambda: check_service_endpoints(self._k8s, self._namespace),
            "health-endpoints": lambda: check_health_endpoints(self._k8s, self._namespace),
        }

        if check_name in cluster_checks:
            result = cluster_checks[check_name]()
            self._render([result])
            return result

        return None

    def _render(self, results: list[CheckResult]) -> None:
        columns = ["Status", "Check", "Message"]
        rows: list[list[str]] = []
        for r in results:
            rows.append([_STATUS_ICONS[r.status], r.name, r.message])

        self._console.table("ilum doctor", columns, rows)

        # Print suggestions for failures/warnings
        for r in results:
            if r.suggestion and r.status in (CheckStatus.FAIL, CheckStatus.WARN):
                self._console.info(f"  {r.name}: {r.suggestion}")

    def to_dict(self, results: list[CheckResult]) -> list[dict]:
        """Serialize check results for machine-readable output."""
        return [
            {
                "name": r.name,
                "status": r.status.value,
                "message": r.message,
                "suggestion": r.suggestion or None,
                "fixable": r.fixable,
            }
            for r in results
        ]

    def fix_simple_issues(self, results: list[CheckResult]) -> list[str]:
        """Attempt to fix fixable issues. Returns list of fix descriptions."""
        fixed = []
        for r in results:
            if not r.fixable or r.status not in (CheckStatus.FAIL, CheckStatus.WARN):
                continue
            if r.name == "namespace":
                try:
                    self._k8s.create_namespace(self._namespace)
                    fixed.append(f"Created namespace '{self._namespace}'")
                except Exception:
                    pass
            elif r.name == "helm-repo":
                try:
                    self._helm.repo_add("ilum", "https://charts.ilum.cloud")
                    fixed.append("Added ilum Helm repo")
                except Exception:
                    pass
        return fixed

    def has_failures(self) -> bool:
        """Run all checks and return True if any failed."""
        results = self.run_all()
        return any(r.status == CheckStatus.FAIL for r in results)
