from __future__ import annotations

import datetime
import hashlib
import json
from pathlib import Path
from typing import Any

import yaml

ROOT = Path(__file__).resolve().parents[1]
OUT_DIR = ROOT / "results" / "row25_stress"
REGISTRY = ROOT / "datasets" / "stress_registry.yaml"
LEGACY_REPORT = ROOT / "datasets" / "stress_report.csv"
CURVATON = ROOT / "datasets" / "curvaton_sourced_tensors.yaml"
DE_EFT = ROOT / "datasets" / "de_eft.yaml"
LEGACY_GATE = ROOT / "research" / "row25" / "legacy_gate_row25_stress_v22.py"

RESULT_PATHS = {
    "row19": ROOT / "results" / "row19_results.json",
    "row20": ROOT / "results" / "row20_cosmology" / "result.json",
    "jwst": ROOT / "results" / "row20_4_jwst" / "result.json",
    "row21": ROOT / "results" / "row21_axion" / "result.json",
    "row22": ROOT / "results" / "row22_nz" / "result.json",
    "row23": ROOT / "results" / "row23_ringdown" / "result.json",
    "answf": ROOT / "results" / "answf_eft" / "result.json",
}


def utc_now_z() -> str:
    return datetime.datetime.now(datetime.UTC).isoformat(timespec="seconds").replace("+00:00", "Z")


def sha256(path: Path) -> str | None:
    if not path.exists():
        return None
    h = hashlib.sha256()
    with path.open("rb") as f:
        for chunk in iter(lambda: f.read(1024 * 1024), b""):
            h.update(chunk)
    return h.hexdigest()


def load_json(path: Path) -> dict[str, Any] | None:
    if not path.exists():
        return None
    return json.loads(path.read_text(encoding="utf-8"))


def load_yaml(path: Path) -> dict[str, Any] | None:
    if not path.exists():
        return None
    return yaml.safe_load(path.read_text(encoding="utf-8"))


def result_pass(obj: dict[str, Any] | None) -> bool:
    return bool(obj and obj.get("verdict") == "PASS")


def resolve_ahg(results: dict[str, Any]) -> dict[str, Any]:
    row21 = results.get("row21")
    if not row21:
        return {"legacy_id": "AHG", "outcome": "PENDING", "reason": "Row 21 result missing."}
    summary = row21.get("summary", {})
    ratio = summary.get("max_pred_over_limit")
    passed = bool(result_pass(row21) and ratio is not None and float(ratio) <= 1.0)
    return {
        "legacy_id": "AHG",
        "active_scope": "Row 21 axion root-table exclusion stress",
        "outcome": "PASS" if passed else "FAIL",
        "metrics": {
            "prediction_band_ueV": [summary.get("m_min_ueV"), summary.get("m_max_ueV")],
            "root_table_coverage_ueV": [summary.get("comparison_m_min_ueV"), summary.get("comparison_m_max_ueV")],
            "max_pred_over_limit": ratio,
        },
        "boundary": "Current root table covers 10-60 ueV, not the full declared 6-60 ueV band.",
    }


def resolve_mhg(results: dict[str, Any]) -> dict[str, Any]:
    row23 = results.get("row23")
    if not row23:
        return {"legacy_id": "MHG", "outcome": "PENDING", "reason": "Row 23 result missing."}
    claim = row23.get("claim_boundary", {})
    canonical_zero = bool(
        claim.get("canonical_echo_channel_status") == "CLOSED_ZERO_AMPLITUDE"
        and row23.get("row19_canonical_gw_handoff", {}).get("R_NZH_amplitude") == 0.0
    )
    passed = bool(result_pass(row23) and canonical_zero)
    return {
        "legacy_id": "MHG",
        "active_scope": "Row 23 Kerr QNM frequency/damping stress",
        "outcome": "PASS" if passed else "FAIL",
        "metrics": {
            "n_events": row23.get("n_events"),
            "max_freq_frac_err": row23.get("max_freq_frac_err"),
            "max_tau_frac_err": row23.get("max_tau_frac_err"),
            "canonical_echo_amplitude_zero": canonical_zero,
        },
        "boundary": "The v19 registry called MHG a ringdown covariance test but the old resolver used the 80-point cosmology/GW vector. v23 resolves MHG through the actual Row 23 QNM benchmark plus the canonical zero-echo action handoff. A nonzero reflective extension is outside the canonical branch and is not an active kill-shot.",
    }


def resolve_ccg(results: dict[str, Any]) -> dict[str, Any]:
    row20 = results.get("row20")
    jwst = results.get("jwst")
    passed = result_pass(row20) and result_pass(jwst)
    summary = (row20 or {}).get("summary", {})
    return {
        "legacy_id": "CCG",
        "active_scope": "Row 20 compressed cosmology + separate JWST diagnostic-record integrity",
        "outcome": "PASS" if passed else "FAIL",
        "metrics": {
            "row20_chi2_per_dof": summary.get("chi2_per_dof"),
            "row20_threshold": summary.get("threshold_chi2_per_dof"),
            "jwst_diagnostic_record_status": (jwst or {}).get("status"),
        },
        "boundary": "The historical registry described a single joint CMB/BAO/SN/JWST fit. The active Row 20 does not include JWST; v23 keeps the JWST diagnostic as separate Row 20.4 and does not claim the historical joint test is implemented.",
    }


def resolve_cpg() -> dict[str, Any]:
    curv = load_yaml(CURVATON)
    de = load_yaml(DE_EFT)
    if curv is None or de is None:
        return {"legacy_id": "CPG", "outcome": "PENDING", "reason": "Curvaton or DE-EFT input missing."}

    r_src = float(curv["sourced_tensors"]["r_src_target"])
    r_hard_max = 0.03
    n_min = float(curv["sourced_tensors"]["TCC"]["N_min"])
    n_max = float(curv["sourced_tensors"]["TCC"]["N_max"])

    alphas = []
    for key in ("alphaB", "alphaK", "alphaM"):
        vals = [float(x["value"]) for x in de.get("horndeski", {}).get(key, [])]
        alphas.extend(vals)
    max_abs_alpha = max((abs(v) for v in alphas), default=0.0)
    max_jump = 0.0
    for key in ("alphaB", "alphaK", "alphaM"):
        vals = [float(x["value"]) for x in de.get("horndeski", {}).get(key, [])]
        for a, b in zip(vals, vals[1:]):
            max_jump = max(max_jump, abs(b - a))

    checks = {
        "sourced_tensor_target_below_hard_r_cap": r_src <= r_hard_max,
        "TCC_window_ordered": n_min <= n_max,
        "DE_EFT_alpha_values_finite": all(abs(v) < float("inf") for v in alphas),
        "DE_EFT_continuity_jump_below_0p05": max_jump <= 0.05,
    }
    return {
        "legacy_id": "CPG",
        "active_scope": "Direct deterministic checks from shipped curvaton_sourced_tensors.yaml and de_eft.yaml",
        "outcome": "PASS" if all(checks.values()) else "FAIL",
        "checks": checks,
        "metrics": {
            "r_src_target": r_src,
            "hard_r_cap": r_hard_max,
            "TCC_N_window": [n_min, n_max],
            "max_abs_shipped_alpha_function_value": max_abs_alpha,
            "max_adjacent_alpha_jump": max_jump,
        },
        "boundary": "This is a shipped-parameter consistency check, not a fit of curvaton/DE-EFT parameters to observational data.",
    }


def resolve_pmg(results: dict[str, Any]) -> dict[str, Any]:
    row19 = results.get("row19")
    answf = results.get("answf")
    passed = result_pass(row19) and result_pass(answf)
    return {
        "legacy_id": "PMG",
        "active_scope": "Live Row 19 FRG benchmark + live Row 0.2 ANSWF positivity",
        "outcome": "PASS" if passed else "FAIL",
        "metrics": {
            "row19_status": (row19 or {}).get("status"),
            "answf_status": (answf or {}).get("status"),
            "legacy_static_pmg_status_used": False,
        },
        "boundary": "The historical PMG listed legacy unification_tau inputs; v23 does not use the inactive conventional proton/unification branch as active PMG evidence.",
    }


def resolve_cig(results: dict[str, Any]) -> dict[str, Any]:
    row22 = results.get("row22")
    passed = result_pass(row22)
    return {
        "legacy_id": "CIG",
        "active_scope": "Live Row 22 NZ dual-slack + spectral-headroom stress",
        "outcome": "PASS" if passed else "FAIL",
        "metrics": {
            **((row22 or {}).get("metrics", {})),
            "legacy_static_cig_status_used_for_spectral_pass": False,
        },
        "boundary": "The old CIG static JSON also carried an unexplained ci_width=0.08. v23 does not claim that uncertainty-width statistic is independently recomputed because its estimator is not defined in the shipped executable path.",
    }


def gate_row25_stress() -> dict[str, Any]:
    loaded = {name: load_json(path) for name, path in RESULT_PATHS.items()}
    tests = [
        resolve_ahg(loaded),
        resolve_mhg(loaded),
        resolve_ccg(loaded),
        resolve_cpg(),
        resolve_pmg(loaded),
        resolve_cig(loaded),
    ]

    n_pass = sum(t.get("outcome") == "PASS" for t in tests)
    n_fail = sum(t.get("outcome") == "FAIL" for t in tests)
    n_pending = sum(t.get("outcome") == "PENDING" for t in tests)

    checks = {
        "historical_registry_preserved": REGISTRY.exists(),
        "historical_report_preserved": LEGACY_REPORT.exists(),
        "active_gate_does_not_mutate_historical_stress_report": True,
        "PMG_uses_live_Row19_and_ANSWF_not_static_status": tests[4].get("metrics", {}).get("legacy_static_pmg_status_used") is False,
        "CIG_uses_live_Row22_not_static_spectral_status": tests[5].get("metrics", {}).get("legacy_static_cig_status_used_for_spectral_pass") is False,
        "all_scoped_resolutions_pass": n_fail == 0 and n_pending == 0,
        "legacy_semantic_mismatches_are_explicit": True,
    }

    verdict = "PASS" if all(checks.values()) else ("FAIL" if n_fail else "PENDING_DATA")
    status = (
        "PASS_STRESS_REGISTRY_HARDENED__LEGACY_JOINT_AND_UNCERTAINTY_SUBGATES_OPEN"
        if verdict == "PASS"
        else "FAIL_OR_PENDING_STRESS_REGISTRY_HARDENING"
    )

    return {
        "row": 25,
        "gate": "break_utfanswf_stress_registry_hardened",
        "signature": "ROW25_V23_ACTIVE_GATE_AGGREGATOR_v1",
        "context": "LIVE",
        "tier": "Diagnostic",
        "blocking": False,
        "timestamp_utc": utc_now_z(),
        "verdict": verdict,
        "status": status,
        "n_tests": len(tests),
        "n_pass": n_pass,
        "n_pending": n_pending,
        "n_fail": n_fail,
        "critical_triggered": n_fail > 0,
        "checks": checks,
        "tests": tests,
        "historical_provenance": {
            "stress_registry": {"path": "datasets/stress_registry.yaml", "sha256": sha256(REGISTRY), "version": "v19"},
            "stress_report": {"path": "datasets/stress_report.csv", "sha256": sha256(LEGACY_REPORT)},
            "legacy_runner": {"path": "research/row25/legacy_gate_row25_stress_v22.py", "sha256": sha256(LEGACY_GATE)},
        },
        "open_boundaries": [
            "Historical CCG described a single joint CMB/BAO/SN/JWST fit; active v23 has Row 20 cosmology plus a separate JWST diagnostic record, not that joint fit.",
            "Historical CIG uncertainty-width statistic is not independently recomputed because the shipped bundle does not define its estimator.",
            "A nonzero reflective Row 23 extension remains optional/noncanonical; the canonical echo-amplitude channel is closed at zero and is not an open stress-test requirement.",
            "Row 21 direct exclusion table covers 10-60 ueV rather than the full declared 6-60 ueV axion band.",
        ],
        "notes": "Diagnostic PASS means the v19 stress registry has been reconciled to live v23 gates without using stale static pass flags or mutating the historical input report; explicitly missing historical subtests remain open.",
    }


def _write_resolved_csv(result: dict[str, Any]) -> None:
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    lines = ["legacy_test_id,outcome,active_scope,boundary"]
    import csv, io
    buf = io.StringIO()
    writer = csv.writer(buf)
    writer.writerow(["legacy_test_id", "outcome", "active_scope", "boundary"])
    for t in result.get("tests", []):
        writer.writerow([t.get("legacy_id", ""), t.get("outcome", ""), t.get("active_scope", ""), t.get("boundary", "")])
    (OUT_DIR / "resolved_v23.csv").write_text(buf.getvalue(), encoding="utf-8")


def main() -> None:
    OUT_DIR.mkdir(parents=True, exist_ok=True)
    result = gate_row25_stress()
    (OUT_DIR / "result.json").write_text(json.dumps(result, indent=2, sort_keys=True), encoding="utf-8")
    _write_resolved_csv(result)
    print(json.dumps({
        "row": 25,
        "status": result.get("status"),
        "verdict": result.get("verdict"),
        "pass": result.get("n_pass"),
        "pending": result.get("n_pending"),
        "fail": result.get("n_fail"),
    }, indent=2))


if __name__ == "__main__":
    main()
