"""``ilum diff`` command — compare values across sources."""

from __future__ import annotations

from pathlib import Path
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.core.safety import compute_diff, load_snapshot
from ilum.errors import IlumError
from ilum.wizard.deps import ensure_tools

_VALID_SOURCES = ("defaults", "file", "revision", "snapshot")


def _walk_dot_path(data: dict[str, Any], path: str) -> Any:
    """Walk a dot-separated path into a nested dict."""
    parts = path.split(".")
    current: Any = data
    for part in parts:
        if not isinstance(current, dict) or part not in current:
            raise typer.BadParameter(f"Key path '{path}' not found in values.")
        current = current[part]
    return current


def diff(
    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."
    ),
    source: str = typer.Option(
        "defaults",
        "--source",
        "-s",
        help="Comparison source: defaults, file, revision, snapshot.",
    ),
    values_file: Path | None = typer.Option(  # noqa: B008
        None, "--values-file", "-f", help="Local YAML file to compare (with --source file)."
    ),
    revision: int | None = typer.Option(
        None, "--revision", help="Revision number to compare (with --source revision)."
    ),
    path: str | None = typer.Option(None, "--path", help="Dot-notation path to filter diff."),
) -> None:
    """Compare values across different sources."""
    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)

    if source not in _VALID_SOURCES:
        console.error(f"Invalid source: '{source}'. Must be one of: {', '.join(_VALID_SOURCES)}")
        raise typer.Exit(code=1)

    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,
        )

        mgr.get_release_info(release)

        if source == "defaults":
            old = mgr.fetch_live_values(release)
            new = mgr.fetch_computed_values(release)
            title = "User-Supplied vs Computed Values"
        elif source == "file":
            if not values_file:
                console.error("--values-file is required with --source file")
                raise typer.Exit(code=1)
            from ilum.core.values import load_values

            old = mgr.fetch_live_values(release)
            new = load_values(values_file)
            title = f"Live Values vs {values_file.name}"
        elif source == "revision":
            if revision is None:
                console.error("--revision is required with --source revision")
                raise typer.Exit(code=1)
            result = mgr.helm.get_values_at_revision(release, revision)
            old = dict(result.json_data or {})
            new = mgr.fetch_live_values(release)
            title = f"Revision {revision} vs Current"
        else:  # snapshot
            snapshot = load_snapshot(release, namespace, paths.state_dir, context)
            if snapshot is None:
                console.warning("No CLI snapshot found. Run an operation first to create one.")
                raise typer.Exit(code=0)
            old = snapshot.values
            new = mgr.fetch_live_values(release)
            title = "Last CLI Snapshot vs Live Values"

        # Apply path filter before diff
        if path:
            old = _walk_dot_path(old, path) if isinstance(old, dict) else old
            new = _walk_dot_path(new, path) if isinstance(new, dict) else new
            if not isinstance(old, dict) or not isinstance(new, dict):
                console.error(f"Path '{path}' does not resolve to a dict on both sides.")
                raise typer.Exit(code=1)

        d = compute_diff(old, new)

        if fmt != OutputFormat.TABLE:
            diff_data: dict[str, Any] = {
                "added": d.added,
                "changed": {k: {"old": v[0], "new": v[1]} for k, v in d.changed.items()},
                "removed": d.removed,
            }
            cmd_result = CommandResult(data=diff_data, summary=title)
            ResultFormatter().format(
                cmd_result,
                fmt,
                console,
                no_headers=console.no_headers,
                field_selector=console.field_selector,
            )
        else:
            if d.is_empty:
                console.info("No differences found.")
            else:
                console.diff_table(d, title=title)

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