from __future__ import annotations

import json
import math
from pathlib import Path

import typer
from rich.console import Console

from invarlock.core.auto_tuning import get_tier_policies
from invarlock.reporting.report_builder import (
    PM_DRIFT_BAND_DEFAULT,
    make_report,
)

console = Console()


def explain_gates_command(
    report: str = typer.Option(..., "--report", help="Path to primary report.json"),
    baseline: str = typer.Option(
        ..., "--baseline", help="Path to baseline report.json"
    ),
) -> None:
    """Explain evaluation report gates for a report vs baseline.

    Loads the reports, builds an evaluation report, and prints gate thresholds,
    observed statistics, and pass/fail reasons in a compact, readable form.
    """
    report_path = Path(report)
    baseline_path = Path(baseline)
    if not report_path.exists() or not baseline_path.exists():
        console.print("[red]Missing --report or --baseline file[/red]")
        raise typer.Exit(1)

    try:
        report_data = json.loads(report_path.read_text())
        baseline_data = json.loads(baseline_path.read_text())
    except Exception as exc:  # noqa: BLE001
        console.print(f"[red]Failed to load inputs: {exc}[/red]")
        raise typer.Exit(1) from exc

    evaluation_report = make_report(report_data, baseline_data)
    validation = (
        evaluation_report.get("validation", {})
        if isinstance(evaluation_report.get("validation"), dict)
        else {}
    )
    auto = (
        evaluation_report.get("auto", {})
        if isinstance(evaluation_report.get("auto"), dict)
        else {}
    )
    tiny_relax = bool(auto.get("tiny_relax"))

    # Extract tier + metric policy (floors/hysteresis)
    tier = str(auto.get("tier", "balanced")).lower()
    effective_tier = "aggressive" if tiny_relax else tier
    tier_policies = get_tier_policies()
    tier_defaults = tier_policies.get(effective_tier, tier_policies.get("balanced", {}))
    resolved_policy = (
        evaluation_report.get("resolved_policy", {})
        if isinstance(evaluation_report.get("resolved_policy"), dict)
        else {}
    )
    metrics_policy: dict[str, object] = {}
    if not tiny_relax:
        metrics_policy = (
            resolved_policy.get("metrics", {})
            if isinstance(resolved_policy.get("metrics"), dict)
            else {}
        )
    if not metrics_policy:
        metrics_policy = (
            tier_defaults.get("metrics", {}) if isinstance(tier_defaults, dict) else {}
        )
        if not isinstance(metrics_policy, dict):
            metrics_policy = {}
    pm_policy = (
        metrics_policy.get("pm_ratio", {})
        if isinstance(metrics_policy.get("pm_ratio"), dict)
        else {}
    )
    hysteresis_ratio = float(pm_policy.get("hysteresis_ratio", 0.0))
    min_tokens = int(pm_policy.get("min_tokens", 0))
    try:
        limit_base = float(pm_policy.get("ratio_limit_base"))
    except Exception:
        limit_base = None
    if limit_base is None or not isinstance(limit_base, int | float):
        limit_base = None
    elif not float("-inf") < float(limit_base) < float("inf"):
        limit_base = None
    if limit_base is None:
        try:
            fallback = (
                tier_defaults.get("metrics", {})
                if isinstance(tier_defaults, dict)
                else {}
            )
            fallback_pm = (
                fallback.get("pm_ratio", {}) if isinstance(fallback, dict) else {}
            )
            limit_base = float(fallback_pm.get("ratio_limit_base"))
        except Exception:
            limit_base = None
    limit_with_hyst = (
        float(limit_base) + max(0.0, hysteresis_ratio)
        if isinstance(limit_base, int | float)
        else None
    )
    tokens_ok = True
    telem = (
        evaluation_report.get("telemetry", {})
        if isinstance(evaluation_report.get("telemetry"), dict)
        else {}
    )
    try:
        total_tokens = int(telem.get("preview_total_tokens", 0)) + int(
            telem.get("final_total_tokens", 0)
        )
        tokens_ok = (min_tokens == 0) or (total_tokens >= min_tokens) or tiny_relax
    except Exception:
        tokens_ok = True

    # Primary-metric ratio gate explanation (ppl-like kinds shown as ratios)
    ratio = None
    ratio_ci = None
    if isinstance(evaluation_report.get("primary_metric"), dict):
        pm = evaluation_report.get("primary_metric", {})
        ratio = pm.get("ratio_vs_baseline")
        ratio_ci = pm.get("display_ci")
    hysteresis_applied = bool(validation.get("hysteresis_applied"))
    status = "PASS" if bool(validation.get("primary_metric_acceptable")) else "FAIL"
    console.print("[bold]Gate: Primary Metric vs Baseline[/bold]")
    console.print(f"  status: {status}")
    if isinstance(ratio, int | float):
        if isinstance(ratio_ci, tuple | list) and len(ratio_ci) == 2:
            console.print(
                f"  observed: {ratio:.3f}x (CI {ratio_ci[0]:.3f}-{ratio_ci[1]:.3f})"
            )
        else:
            console.print(f"  observed: {ratio:.3f}x")
    if isinstance(limit_base, int | float):
        hyst_suffix = (
            f" (+hysteresis {hysteresis_ratio:.3f})" if hysteresis_ratio else ""
        )
        console.print(f"  threshold: ≤ {float(limit_base):.2f}x{hyst_suffix}")
    else:
        console.print("  threshold: unavailable")
    if tiny_relax:
        console.print(
            "  note: tiny relax enabled; aggressive-tier gates and token floors are informational"
        )
    token_state = "ok" if tokens_ok else "below floor"
    console.print(
        f"  tokens: {token_state} (token floors: min_tokens={min_tokens or 0}, total={int(telem.get('preview_total_tokens', 0)) + int(telem.get('final_total_tokens', 0)) if telem else 0})"
    )
    if hysteresis_applied:
        if isinstance(limit_with_hyst, int | float):
            console.print(
                f"  note: hysteresis applied → effective threshold = {float(limit_with_hyst):.3f}x"
            )

    # Tail gate explanation (warn/fail; based on per-window Δlog-loss vs baseline)
    pm_tail = (
        evaluation_report.get("primary_metric_tail", {})
        if isinstance(evaluation_report.get("primary_metric_tail"), dict)
        else {}
    )
    if pm_tail:
        mode = str(pm_tail.get("mode", "warn") or "warn").strip().lower()
        evaluated = bool(pm_tail.get("evaluated", False))
        passed = bool(pm_tail.get("passed", True))
        policy = (
            pm_tail.get("policy", {}) if isinstance(pm_tail.get("policy"), dict) else {}
        )
        stats = (
            pm_tail.get("stats", {}) if isinstance(pm_tail.get("stats"), dict) else {}
        )

        q = policy.get("quantile", 0.95)
        try:
            qf = float(q)
        except Exception:
            qf = 0.95
        qf = max(0.0, min(1.0, qf))
        q_key = f"q{int(round(100.0 * qf))}"
        q_name = f"P{int(round(100.0 * qf))}"
        q_val = stats.get(q_key)
        qmax = policy.get("quantile_max")
        eps = policy.get("epsilon", stats.get("epsilon"))
        mass = stats.get("tail_mass")
        mmax = policy.get("mass_max")

        if not evaluated:
            status_tail = "INFO"
        elif passed:
            status_tail = "PASS"
        elif mode == "fail":
            status_tail = "FAIL"
        else:
            status_tail = "WARN"

        console.print("\n[bold]Gate: Primary Metric Tail (ΔlogNLL)[/bold]")
        console.print(f"  mode: {mode}")
        console.print(f"  status: {status_tail}")
        if isinstance(q_val, int | float):
            console.print(f"  observed: {q_name}={float(q_val):.4f}")
        if isinstance(mass, int | float):
            console.print(f"  tail_mass: Pr[ΔlogNLL > ε]={float(mass):.4f}")
        thr_parts: list[str] = []
        if isinstance(qmax, int | float):
            thr_parts.append(f"{q_name}≤{float(qmax):.4f}")
        if isinstance(mmax, int | float):
            thr_parts.append(f"mass≤{float(mmax):.4f}")
        if isinstance(eps, int | float):
            thr_parts.append(f"ε={float(eps):.1e}")
        if thr_parts:
            console.print("  threshold: " + "; ".join(thr_parts))

    # Dataset split visibility from report provenance
    try:
        split = (report_data.get("provenance", {}) or {}).get("dataset_split")
        sf = (report_data.get("provenance", {}) or {}).get("split_fallback")
        if split:
            line = f"Dataset split: {split}"
            if sf:
                line += " (fallback)"
            # Click echo would be ideal, but we keep consistent console printing
            console.print(line)
    except Exception:
        pass

    # Drift gate explanation (ppl-like kinds only)
    drift = None
    drift_status = (
        "PASS" if bool(validation.get("preview_final_drift_acceptable")) else "FAIL"
    )
    pm = (
        evaluation_report.get("primary_metric", {})
        if isinstance(evaluation_report.get("primary_metric"), dict)
        else {}
    )
    kind = str(pm.get("kind", "") or "").lower()
    if kind.startswith("ppl"):
        preview = pm.get("preview")
        final = pm.get("final")
        if isinstance(preview, int | float) and isinstance(final, int | float):
            try:
                if float(preview) != 0.0:
                    drift = float(final) / float(preview)
            except Exception:
                drift = None

        console.print("\n[bold]Gate: Drift (final/preview)[/bold]")
        if isinstance(drift, int | float):
            console.print(f"  observed: {drift:.3f}")
        drift_band = (
            pm.get("drift_band") if isinstance(pm.get("drift_band"), dict) else {}
        )
        drift_min = drift_band.get("min")
        drift_max = drift_band.get("max")
        if not (
            isinstance(drift_min, int | float)
            and isinstance(drift_max, int | float)
            and math.isfinite(float(drift_min))
            and math.isfinite(float(drift_max))
            and float(drift_min) > 0.0
            and float(drift_min) < float(drift_max)
        ):
            drift_min, drift_max = PM_DRIFT_BAND_DEFAULT
        console.print(f"  threshold: {float(drift_min):.3f}-{float(drift_max):.3f}")
        console.print(f"  status: {drift_status}")

    # Guard Overhead explanation (if present)
    overhead = (
        evaluation_report.get("guard_overhead", {})
        if isinstance(evaluation_report.get("guard_overhead"), dict)
        else {}
    )
    if overhead:
        passed = bool(validation.get("guard_overhead_acceptable", True))
        threshold = overhead.get("threshold_percent")
        if not isinstance(threshold, int | float):
            threshold = float(overhead.get("overhead_threshold", 0.01)) * 100.0
        pct = overhead.get("overhead_percent")
        ratio = overhead.get("overhead_ratio")
        console.print("\n[bold]Gate: Guard Overhead[/bold]")
        if isinstance(pct, int | float):
            console.print(
                f"  observed: {pct:+.2f}%{f' ({ratio:.3f}x)' if isinstance(ratio, int | float) else ''}"
            )
        elif isinstance(ratio, int | float):
            console.print(f"  observed: {ratio:.3f}x")
        else:
            console.print("  observed: N/A")
        console.print(f"  threshold: ≤ +{float(threshold):.1f}%")
        console.print(f"  status: {'PASS' if passed else 'FAIL'}")
