import json
import os
from pathlib import Path

ROOT = Path(".")
INPUT_JSON = ROOT / "results" / "row20_h0_attribution" / "row20_thin_slice_attribution.json"
OUTPUT_JSON = ROOT / "results" / "row20_h0_attribution" / "row20_BAO_bridge_probe.json"
OUTPUT_REPORT = ROOT / "results" / "row20_h0_attribution" / "row20_BAO_bridge_probe_REPORT.md"


def load_json(path):
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)


def find_block(data, tag):
    for block in data.get("blocks", []):
        if block.get("tag") == tag:
            return block
    raise ValueError(f"Block not found: {tag}")


def parse_node(node):
    label = node["slice"]
    z = float(label.split("z=")[1])
    observable = "H" if label.startswith("H") else "D_M"
    return {
        "slice": label,
        "observable": observable,
        "z": z,
        "data": node["data"],
        "theory": node["theory"],
        "residual": node["residual_after_gate_handling"],
        "sigma": node["sigma_diag"],
        "pull": node["diag_pull_sigma"],
        "chi2": node["white_chi2_contribution"],
    }


def main():
    data = load_json(INPUT_JSON)
    bao = find_block(data, "BAO_DM_H")

    nodes = sorted([parse_node(n) for n in bao["nodes"]], key=lambda x: (x["z"], x["observable"]))

    z0510 = [n for n in nodes if abs(n["z"] - 0.510) < 1e-6]
    z0706 = [n for n in nodes if abs(n["z"] - 0.706) < 1e-6]

    h_nodes = [n for n in nodes if n["observable"] == "H"]
    dm_nodes = [n for n in nodes if n["observable"] == "D_M"]

    # Bridge drift from z=0.510 to z=0.706
    def drift(pair):
        low, high = sorted(pair, key=lambda x: x["z"])
        return {
            "observable": low["observable"],
            "low_z": low["z"],
            "high_z": high["z"],
            "low_pull": low["pull"],
            "high_pull": high["pull"],
            "pull_drift": high["pull"] - low["pull"],
            "low_residual": low["residual"],
            "high_residual": high["residual"],
            "residual_drift": high["residual"] - low["residual"],
        }

    h_drift = drift(h_nodes)
    dm_drift = drift(dm_nodes)

    # At z=0.706, check consistency between transverse and radial BAO pulls
    dm0706 = next(n for n in z0706 if n["observable"] == "D_M")
    h0706 = next(n for n in z0706 if n["observable"] == "H")

    z0706_pull_gap = h0706["pull"] - dm0706["pull"]
    z0706_chi2_total = h0706["chi2"] + dm0706["chi2"]

    interpretation = []

    interpretation.append(
        "BAO bridge probe tests whether the z=0.706 bridge slice follows CMB, bends toward SN/SH0ES, or carries its own DM/H projection tension."
    )

    if h0706["pull"] > 0 and dm0706["pull"] < 0:
        interpretation.append(
            "At z=0.706, H(z) and D_M pull in opposite signs, indicating radial/transverse BAO projection tension."
        )

    if abs(z0706_pull_gap) > 2:
        interpretation.append(
            "The z=0.706 radial/transverse pull gap is large enough to mark the bridge as internally stressed."
        )
    else:
        interpretation.append(
            "The z=0.706 radial/transverse pull gap is moderate; bridge stress exists but is not dominant over CMB ell_A or SN midpoint."
        )

    if z0706_chi2_total < data["totals"]["SH0ES_overlay"]["chi2"]:
        interpretation.append(
            "BAO z=0.706 stress is much smaller than SH0ES overlay stress, so BAO does not carry the solution to H0≈73."
        )

    result = {
        "signature": "ROW20_BAO_BRIDGE_PROBE_v1_NATIVE_DIAGNOSTIC",
        "context": "DIAGNOSTIC_NON_GATING",
        "rules": data.get("rules", {}),
        "BAO_DM_H_block": {
            "chi2": bao["chi2"],
            "dof": bao["dof"],
            "chi2_per_dof": bao["chi2_per_dof"],
            "nodes": nodes,
        },
        "bridge_drift": {
            "H": h_drift,
            "D_M": dm_drift,
        },
        "z_0_706_projection": {
            "D_M": dm0706,
            "H": h0706,
            "pull_gap_H_minus_DM": z0706_pull_gap,
            "combined_chi2": z0706_chi2_total,
        },
        "interpretation": interpretation,
        "verdict": (
            "BAO_bridge_geometry_probe_complete; diagnostic only; "
            "no physics, thresholds, datasets, parameters, or gate verdicts changed."
        ),
    }

    OUTPUT_JSON.parent.mkdir(parents=True, exist_ok=True)

    with open(OUTPUT_JSON, "w", encoding="utf-8") as f:
        json.dump(result, f, indent=2)

    with open(OUTPUT_REPORT, "w", encoding="utf-8") as f:
        f.write("# UTFANSWF v21 — Row 20 BAO Bridge Probe\n\n")
        f.write("**Context:** DIAGNOSTIC_NON_GATING. No physics, thresholds, datasets, parameters, or gate verdicts changed.\n\n")

        f.write("## BAO DM/H nodes\n\n")
        f.write("| Slice | z | observable | data | theory | residual | pull sigma | chi2 |\n")
        f.write("|---|---:|---|---:|---:|---:|---:|---:|\n")
        for n in nodes:
            f.write(
                f"| {n['slice']} | {n['z']:.3f} | {n['observable']} | "
                f"{n['data']:.6f} | {n['theory']:.6f} | "
                f"{n['residual']:.6f} | {n['pull']:.6f} | {n['chi2']:.6f} |\n"
            )

        f.write("\n## Bridge drift z=0.510 → z=0.706\n\n")
        f.write("| Observable | low pull | high pull | pull drift | low residual | high residual | residual drift |\n")
        f.write("|---|---:|---:|---:|---:|---:|---:|\n")
        for row in [h_drift, dm_drift]:
            f.write(
                f"| {row['observable']} | {row['low_pull']:.6f} | {row['high_pull']:.6f} | "
                f"{row['pull_drift']:.6f} | {row['low_residual']:.6f} | "
                f"{row['high_residual']:.6f} | {row['residual_drift']:.6f} |\n"
            )

        f.write("\n## z=0.706 projection check\n\n")
        f.write("| Quantity | Value |\n")
        f.write("|---|---:|\n")
        f.write(f"| D_M pull | {dm0706['pull']:.6f} |\n")
        f.write(f"| H pull | {h0706['pull']:.6f} |\n")
        f.write(f"| H minus D_M pull gap | {z0706_pull_gap:.6f} |\n")
        f.write(f"| combined z=0.706 chi2 | {z0706_chi2_total:.6f} |\n")

        f.write("\n## Interpretation\n\n")
        for item in interpretation:
            f.write(f"- {item}\n")

        f.write("\n## Verdict\n\n")
        f.write(result["verdict"] + "\n")

    print("Wrote:")
    print(os.path.abspath(OUTPUT_JSON))
    print(os.path.abspath(OUTPUT_REPORT))
    print()
    print("BAO bridge probe:")
    print(f"BAO_DM_H chi2/dof={bao['chi2_per_dof']:.6f}")
    print(f"H z=0.706 pull={h0706['pull']:.6f} sigma")
    print(f"D_M z=0.706 pull={dm0706['pull']:.6f} sigma")
    print(f"z=0.706 H-D_M pull gap={z0706_pull_gap:.6f}")
    print(f"z=0.706 combined chi2={z0706_chi2_total:.6f}")


if __name__ == "__main__":
    main()