from __future__ import annotations

import json
import os
import sys
import math
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

from harness.gate_row20_cosmology import gate_cosmo
from harness.gate_row20_h0_resolution import gate_h0_resolution


def scan_h0_mechanisms(dataset_dir: str = "datasets") -> dict:
    row20 = gate_cosmo(dataset_dir)
    h0_gate = gate_h0_resolution(dataset_dir)

    summary = h0_gate["summary"]

    h0_early = float(summary["h0_early_anchor"])
    h0_local = float(summary["h0_local_target"])
    h0_bridge = float(summary["h0_bridge_prediction"])
    missing = float(summary["remaining_gap_to_local"])

    mechanisms = []

    def add_mechanism(name, available, delta_h0, status, notes):
        mechanisms.append({
            "name": name,
            "available": bool(available),
            "delta_h0_estimate": float(delta_h0),
            "absolute_fraction_of_missing_gap": (
                abs(float(delta_h0)) / abs(missing) if abs(missing) > 1.0e-12 else math.inf
            ),
            "status": status,
            "notes": notes,
        })

    parts = {p.get("tag"): p for p in row20.get("parts", [])}

    bao = parts.get("BAO_DM_H", {})
    bao_bridge = bao.get("bao_dm_h_bridge", {})
    bao_delta = float(summary.get("delta_h0_from_bao_bridge", 0.0))

    add_mechanism(
        "BAO_DM_H covariance bridge",
        bao_bridge.get("score") == bao_bridge.get("max_score"),
        bao_delta,
        "ACTIVE_BUT_INSUFFICIENT",
        "Promoted v22 bridge improves BAO covariance artifact but only shifts H0 by about +0.53.",
    )

    sn = parts.get("SN", {})
    sn_diag = sn.get("sn_diagnostic", {})
    sn_m_fit = sn_diag.get("M_fit")

    add_mechanism(
        "SN nuisance calibration layer",
        sn_m_fit is not None,
        0.0,
        "CALIBRATION_ONLY_NOT_H0_MECHANISM",
        "SN M_fit is a nuisance offset. Treating it as H0 movement would be invalid without a calibrated ladder model.",
    )

    cmb_cal = row20.get("summary", {}).get("cosmo_meta", {}).get("cmb_calibration")
    cmb_delta_proxy = 0.0
    if cmb_cal is not None:
        cmb_delta_proxy = h0_early * (float(cmb_cal) - 1.0)

    add_mechanism(
        "CMB calibration term",
        cmb_cal is not None,
        cmb_delta_proxy,
        "PRESENT_BUT_NOT_VALID_H0_RELEASE",
        "CMB calibration exists, but using it as a free H0 release would be a new interpretation unless independently derived.",
    )

    gw = parts.get("GW", {})
    gw_resid = gw.get("residual_preview", [])
    gw_delta_proxy = float(gw_resid[0]) if gw_resid else 0.0

    add_mechanism(
        "GW H0 proxy layer",
        gw.get("status") == "RUN",
        gw_delta_proxy,
        "PROXY_PRESENT_BUT_NOT_LOCAL_LADDER_SOLUTION",
        "GW residuals sit above 67.4 but are proxy diagnostics, not a calibrated SH0ES-style local ladder solution.",
    )

    add_mechanism(
        "NZ/CFGL projection operator",
        bool(bao_bridge),
        0.0,
        "USED_BY_BAO_BRIDGE_ONLY",
        "NZ/CFGL appears through the BAO wall bridge, but no separate +5 km/s/Mpc H0 transfer is currently demonstrated.",
    )

    add_mechanism(
        "Compressed/full cosmology mapping",
        row20.get("verdict") == "PASS",
        0.0,
        "CONSISTENT_BUT_NO_EXTRA_H0_SHIFT",
        "Mapping is stable after v21/v22, but no additional H0 bridge movement is exposed by current outputs.",
    )

    usable = [
        m for m in mechanisms
        if m["status"] in {"ACTIVE_BUT_INSUFFICIENT", "FOUND_VALID_SHIFT"}
    ]

    total_existing_shift = sum(m["delta_h0_estimate"] for m in usable)
    remaining_after_existing = h0_local - (h0_early + total_existing_shift)

    if abs(remaining_after_existing) <= 1.5:
        scan_status = "FOUND_EXISTING_H0_MECHANISM"
        verdict = "PASS"
    elif total_existing_shift > 0.0:
        scan_status = "NO_EXISTING_H0_MECHANISM_FOUND"
        verdict = "CANNOT_SOLVE"
    else:
        scan_status = "REQUIRES_NEW_PHYSICS_OR_NEW_PARAMETER"
        verdict = "FAIL"

    return {
        "scan": "UTFANSWF_v22_H0_MECHANISM_SCAN",
        "source_gate": h0_gate.get("signature"),
        "row20_signature": row20.get("signature"),
        "summary": {
            "h0_early_anchor": h0_early,
            "h0_local_target": h0_local,
            "h0_bridge_prediction_from_formal_gate": h0_bridge,
            "missing_gap_before_scan": missing,
            "total_existing_valid_shift_found": total_existing_shift,
            "remaining_gap_after_existing_mechanisms": remaining_after_existing,
            "status": scan_status,
            "rules": {
                "physics_changed": False,
                "thresholds_changed": False,
                "datasets_changed": False,
                "new_free_parameters": False,
            },
        },
        "mechanisms": mechanisms,
        "verdict": verdict,
    }


if __name__ == "__main__":
    result = scan_h0_mechanisms("datasets")

    print("\n================================")
    print(f"H0 MECHANISM SCAN — {result['verdict']}")
    print(f"status: {result['summary']['status']}")
    print(f"valid existing shift: {result['summary']['total_existing_valid_shift_found']:.6f}")
    print(f"remaining gap: {result['summary']['remaining_gap_after_existing_mechanisms']:.6f}")
    print("================================\n")

    print(json.dumps(result, indent=2))