"""``ilum rollback`` command — explicit rollback to a previous revision."""

from __future__ import annotations

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.core.safety import compute_diff, make_snapshot, save_snapshot
from ilum.errors import HelmError, IlumError
from ilum.wizard.deps import ensure_tools


def rollback(
    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."
    ),
    revision: int = typer.Option(0, "--revision", help="Target revision (0 = previous)."),
    dry_run: bool = typer.Option(
        False, "--dry-run", help="Show what would happen without executing."
    ),
    yes: bool = typer.Option(False, "--yes", "-y", help="Skip confirmation prompt."),
    timeout: str = typer.Option("10m", "--timeout", help="Helm timeout for rollback operation."),
) -> None:
    """Roll back an Ilum release to a previous revision."""
    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()
        helm = HelmClient(kubecontext=context, namespace=namespace, timeout=timeout)
        mgr = ReleaseManager(
            helm=helm,
            k8s=KubeClient(kubecontext=context),
            resolver=ModuleResolver(),
            config_mgr=ConfigManager(paths),
            paths=paths,
        )

        # Fetch history
        history_result = helm.history(release)
        history = history_result.json_data or []

        if len(history) < 2:
            console.error("Only one revision exists. Nothing to roll back to.")
            raise typer.Exit(code=1)

        current = history[-1]
        current_rev = int(current.get("revision", 0))

        # Determine target revision
        target_rev = current_rev - 1 if revision == 0 else revision

        # Validate target exists
        target = None
        for entry in history:
            if int(entry.get("revision", 0)) == target_rev:
                target = entry
                break
        if target is None:
            console.error(f"Revision {target_rev} not found in release history.")
            raise typer.Exit(code=1)

        # Show comparison
        summary_rows = [
            ["Current revision", str(current_rev)],
            ["Current status", current.get("status", "unknown")],
            ["Current chart", current.get("chart", "unknown")],
            ["Current deployed", current.get("updated", "unknown")],
            ["", ""],
            ["Target revision", str(target_rev)],
            ["Target status", target.get("status", "unknown")],
            ["Target chart", target.get("chart", "unknown")],
            ["Target deployed", target.get("updated", "unknown")],
        ]

        # Fetch values at both revisions for diff
        try:
            current_vals = helm.get_values_at_revision(release, current_rev)
            target_vals = helm.get_values_at_revision(release, target_rev)
            diff = compute_diff(
                dict(target_vals.json_data or {}),
                dict(current_vals.json_data or {}),
            )
            has_diff = not diff.is_empty
        except HelmError:
            diff = None
            has_diff = False

        if fmt != OutputFormat.TABLE:
            data = {
                "current_revision": current_rev,
                "target_revision": target_rev,
                "current_status": current.get("status", "unknown"),
                "target_status": target.get("status", "unknown"),
                "dry_run": dry_run,
            }
            if diff:
                data["diff"] = {
                    "added": diff.added,
                    "changed": {k: {"old": v[0], "new": v[1]} for k, v in diff.changed.items()},
                    "removed": diff.removed,
                }
            cmd_result = CommandResult(
                data=data,
                summary=f"Rollback {release} from r{current_rev} to r{target_rev}",
            )
            ResultFormatter().format(
                cmd_result,
                fmt,
                console,
                no_headers=console.no_headers,
                field_selector=console.field_selector,
            )
            if dry_run:
                return
        else:
            console.operation_summary(summary_rows, title="Rollback Plan")
            if has_diff and diff:
                console.diff_table(diff, title="Values Changes (current vs target)")

        if dry_run:
            console.info("Dry run — no changes applied.")
            return

        # Confirm
        if not yes and not console.confirm(
            f"Roll back '{release}' from revision {current_rev} to {target_rev}?"
        ):
            console.info("Rollback cancelled.")
            raise typer.Exit(code=0)

        # Execute rollback
        with console.status_spinner(f"Rolling back to revision {target_rev}..."):
            helm.rollback(release, target_rev)

        # Post-rollback: save snapshot
        try:
            new_values = mgr.fetch_live_values(release)
            info = mgr.get_release_info(release)
            snapshot = make_snapshot(
                release=release,
                namespace=namespace,
                chart_version=info.chart_version,
                values=new_values,
                operation="rollback",
            )
            save_snapshot(snapshot, paths.state_dir)
        except Exception:
            pass  # Best-effort

        # Post-rollback: update module config
        try:
            computed = mgr.fetch_computed_values(release)
            live_modules = mgr.resolver.detect_enabled_modules(computed)
            mgr.save_enabled_modules(sorted(live_modules), release=release)
        except Exception:
            pass  # Best-effort

        console.success(f"Rolled back '{release}' to revision {target_rev}.")

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