"""``ilum status`` command."""

from __future__ import annotations

import re
import time
from datetime import UTC, datetime, timedelta
from typing import Any

import typer

from ilum.cli.defaults import resolve_profile_defaults
from ilum.config.manager import ConfigManager
from ilum.config.paths import IlumPaths
from ilum.core.helm import HelmClient
from ilum.core.kubernetes import KubeClient
from ilum.core.modules import ModuleResolver
from ilum.core.release import ReleaseManager
from ilum.errors import IlumError
from ilum.wizard.deps import ensure_tools


def _parse_duration(value: str) -> timedelta:
    """Parse a human-friendly duration like ``1h``, ``30m``, ``2h30m`` into a timedelta."""
    pattern = re.compile(r"(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$")
    m = pattern.match(value.strip())
    if not m or not any(m.groups()):
        raise typer.BadParameter(f"Invalid duration: '{value}'. Use format like 1h, 30m, 2h30m.")
    hours = int(m.group(1) or 0)
    minutes = int(m.group(2) or 0)
    seconds = int(m.group(3) or 0)
    return timedelta(hours=hours, minutes=minutes, seconds=seconds)


def _format_age(iso_timestamp: str) -> str:
    """Convert an ISO timestamp to a human-readable age like ``2m ago``."""
    if not iso_timestamp:
        return ""
    try:
        ts = datetime.fromisoformat(iso_timestamp)
        if ts.tzinfo is None:
            ts = ts.replace(tzinfo=UTC)
        delta = datetime.now(UTC) - ts
        total = int(delta.total_seconds())
        if total < 60:
            return f"{total}s ago"
        if total < 3600:
            return f"{total // 60}m ago"
        if total < 86400:
            return f"{total // 3600}h ago"
        return f"{total // 86400}d ago"
    except (ValueError, TypeError):
        return iso_timestamp


def status(
    release: str | None = typer.Option(
        None, "--release", "-r", show_default="from profile", help="Helm release name."
    ),
    namespace: str | None = typer.Option(
        None, "--namespace", "-n", show_default="from profile", help="Kubernetes namespace."
    ),
    context: str | None = typer.Option(
        None, "--context", show_default="from profile", help="Kubernetes context."
    ),
    pods: bool = typer.Option(True, "--pods/--no-pods", help="Show pod readiness."),
    show_modules: bool = typer.Option(
        True,
        "--modules/--no-modules",
        help="Show enabled modules.",
    ),
    wait: bool = typer.Option(False, "--wait", help="Wait for all pods to be ready."),
    wait_timeout: int = typer.Option(
        300, "--wait-timeout", help="Seconds to wait for pods (with --wait)."
    ),
    events: bool = typer.Option(False, "--events", help="Show recent Kubernetes events."),
    events_type: str | None = typer.Option(
        None, "--events-type", help="Filter events by type (e.g. Warning)."
    ),
    events_since: str | None = typer.Option(
        None, "--events-since", help="Filter events by age (e.g. 1h, 30m)."
    ),
) -> None:
    """Show the status of an Ilum installation."""
    release, namespace, context = resolve_profile_defaults(release, namespace, context)
    import ilum.cli.output as output_mod

    console = output_mod.console
    from ilum.cli.formatters import CommandResult, OutputFormat, ResultFormatter

    fmt = OutputFormat(console.output_format)

    try:
        ensure_tools(["helm"], console)
        paths = IlumPaths.default()
        paths.ensure_dirs()
        mgr = ReleaseManager(
            helm=HelmClient(kubecontext=context, namespace=namespace),
            k8s=KubeClient(kubecontext=context),
            resolver=ModuleResolver(),
            config_mgr=ConfigManager(paths),
            paths=paths,
        )

        # Release info
        info = mgr.get_release_info(release)

        # Collect module data
        live_modules: list[str] = []
        if show_modules:
            try:
                live_values = mgr.fetch_computed_values(release)
                live_modules = mgr.resolver.detect_enabled_modules(live_values)
            except IlumError:
                pass

        # Collect pod data
        pod_data: list[dict[str, Any]] = []
        if pods or wait:
            try:
                all_pods = mgr.k8s.list_pods(namespace)
                pod_data = [
                    {
                        "name": p.name,
                        "phase": p.phase,
                        "ready": p.ready,
                        "restarts": p.restart_count,
                    }
                    for p in all_pods
                ]
            except IlumError:
                pass

        # --wait: poll until all pods ready or timeout
        if wait:
            deadline = time.monotonic() + wait_timeout
            all_ready = False

            with console.progress_spinner("Waiting for pods to be ready...") as handle:
                while time.monotonic() < deadline:
                    try:
                        all_pods = mgr.k8s.list_pods(namespace)
                        active = [p for p in all_pods if p.phase not in ("Succeeded", "Failed")]
                        ready_count = sum(1 for p in active if p.ready and p.phase == "Running")
                        total = len(active)
                        handle.update(f"Pods: {ready_count}/{total} ready")

                        if active and all(p.ready and p.phase == "Running" for p in active):
                            all_ready = True
                            break
                    except IlumError:
                        pass
                    time.sleep(5)

            if all_ready:
                console.success("All pods are ready.")
            else:
                console.error(f"Timed out after {wait_timeout}s waiting for pods.")
                raise typer.Exit(code=1)

            # Refresh pod data after wait
            try:
                all_pods = mgr.k8s.list_pods(namespace)
                pod_data = [
                    {
                        "name": p.name,
                        "phase": p.phase,
                        "ready": p.ready,
                        "restarts": p.restart_count,
                    }
                    for p in all_pods
                ]
            except IlumError:
                pass

        # Collect events data
        events_data: list[dict[str, Any]] = []
        if events:
            try:
                all_events = mgr.k8s.list_events(namespace)

                # Filter by type
                if events_type:
                    all_events = [e for e in all_events if e.event_type == events_type]

                # Filter by age
                if events_since:
                    duration = _parse_duration(events_since)
                    cutoff = datetime.now(UTC) - duration
                    filtered = []
                    for e in all_events:
                        if e.last_seen:
                            try:
                                ts = datetime.fromisoformat(e.last_seen)
                                if ts.tzinfo is None:
                                    ts = ts.replace(tzinfo=UTC)
                                if ts >= cutoff:
                                    filtered.append(e)
                            except (ValueError, TypeError):
                                filtered.append(e)
                        else:
                            filtered.append(e)
                    all_events = filtered

                events_data = [
                    {
                        "type": e.event_type,
                        "reason": e.reason,
                        "object": e.involved_object,
                        "message": e.message,
                        "count": e.count,
                        "last_seen": e.last_seen,
                        "age": _format_age(e.last_seen),
                    }
                    for e in all_events
                ]
            except IlumError:
                pass

        # Machine-readable output
        if fmt != OutputFormat.TABLE:
            data: dict[str, Any] = {
                "release": {
                    "name": info.name,
                    "namespace": info.namespace,
                    "status": info.status,
                    "chart_version": info.chart_version,
                    "revision": info.revision,
                },
                "enabled_modules": sorted(live_modules),
                "pods": pod_data,
            }
            if events:
                data["events"] = events_data
            result = CommandResult(
                data=data,
                summary=f"Release {info.name}: {info.status}",
            )
            ResultFormatter().format(
                result,
                fmt,
                console,
                no_headers=console.no_headers,
                field_selector=console.field_selector,
            )
            return

        # --- Rich table output (TABLE format) ---
        status_color = "green" if info.status == "deployed" else "red"

        info_lines = [
            f"  Release:   {info.name}",
            f"  Namespace: {info.namespace}",
            f"  Status:    [{status_color}]{info.status}[/{status_color}]",
            f"  Chart:     {info.chart}-{info.chart_version}",
            f"  Revision:  {info.revision}",
            f"  Deployed:  {info.last_deployed}",
        ]
        console.panel("\n".join(info_lines), title="Release Info")

        # Enabled modules (live detection)
        if show_modules:
            if live_modules:
                rows = [[m] for m in sorted(live_modules)]
                console.table("Enabled Modules (Live)", ["Module"], rows)
            else:
                console.info("No modules detected on the live release.")

            # Config drift detection
            config_modules = set(mgr.get_enabled_modules(release=release))
            live_set = set(live_modules)

            if not config_modules and live_set:
                # First run on an existing cluster — auto-sync config from live state
                mgr.save_enabled_modules(sorted(live_set), release=release)
                console.info(f"Synced {len(live_set)} live modules to CLI config.")
            else:
                for mod_name in sorted(config_modules - live_set):
                    console.warning(
                        f"Config drift: '{mod_name}' tracked in config but disabled on cluster"
                    )
                for mod_name in sorted(live_set - config_modules):
                    console.warning(
                        f"Config drift: '{mod_name}' enabled on cluster but not tracked in config"
                    )

        # Pod readiness
        if pods:
            if pod_data:
                rows = []
                for pd_item in pod_data:
                    ready_str = "\u2713" if pd_item["ready"] else "\u2717"
                    phase_color = (
                        "green" if pd_item["phase"] == "Running" and pd_item["ready"] else "yellow"
                    )
                    rows.append(
                        [
                            pd_item["name"],
                            f"[{phase_color}]{pd_item['phase']}[/{phase_color}]",
                            ready_str,
                            str(pd_item["restarts"]),
                        ]
                    )
                console.table(
                    "Pod Status",
                    ["Name", "Phase", "Ready", "Restarts"],
                    rows,
                )
            else:
                console.info("No pods found in namespace.")

        # Events
        if events and events_data:
            rows = []
            for ev in events_data:
                type_color = "yellow" if ev["type"] == "Warning" else "green"
                rows.append(
                    [
                        f"[{type_color}]{ev['type']}[/{type_color}]",
                        ev["reason"],
                        ev["object"],
                        ev["message"][:80],
                        str(ev["count"]),
                        ev["age"],
                    ]
                )
            console.table(
                "Events",
                ["Type", "Reason", "Object", "Message", "Count", "Age"],
                rows,
            )
        elif events:
            console.info("No events found.")

    except IlumError as exc:
        console.handle_error(exc)
        raise typer.Exit(code=1) from exc
