import json
import math
import os
from pathlib import Path

ROOT = Path(".")

INPUT_FIRST = ROOT / "results" / "row20_h0_attribution" / "row20_NZ_CFGL_bridge_operator.json"

OUTPUT_JSON = ROOT / "results" / "row20_h0_attribution" / "row20_BAO_common_split_operator.json"
OUTPUT_REPORT = ROOT / "results" / "row20_h0_attribution" / "row20_BAO_common_split_operator.md"


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


def rss(vals):
    return math.sqrt(sum(v * v for v in vals))


def chi2(vals):
    return sum(v * v for v in vals)


def main():
    data = load(INPUT_FIRST)

    r = data["transformed_residual_vector"]

    rH = r["r_H_BAO"]
    rDM = r["r_D_M_BAO"]

    # Common/split decomposition
    r_common = 0.5 * (rH + rDM)
    r_split = 0.5 * (rH - rDM)

    # v22 diagnostic operator:
    # Preserve common mode, damp split mode.
    # lambda_split is not free here; test full split neutralization first.
    lambda_split = 0.0

    r_split_new = lambda_split * r_split

    rH_new = r_common + r_split_new
    rDM_new = r_common - r_split_new

    transformed = {
        "r_ell_A": r["r_ell_A"],
        "r_H_BAO": rH_new,
        "r_D_M_BAO": rDM_new,
        "r_SN": r["r_SN"],
        "r_SH0ES": r["r_SH0ES"],
    }

    original_values = list(r.values())
    transformed_values = list(transformed.values())

    original_rss = rss(original_values)
    transformed_rss = rss(transformed_values)

    original_chi2 = chi2(original_values)
    transformed_chi2 = chi2(transformed_values)

    rss_improvement = original_rss - transformed_rss
    chi2_improvement = original_chi2 - transformed_chi2

    tests = {
        "common_mode_preserved": abs(0.5 * (transformed["r_H_BAO"] + transformed["r_D_M_BAO"]) - r_common) < 1e-9,
        "split_mode_neutralized": abs(0.5 * (transformed["r_H_BAO"] - transformed["r_D_M_BAO"])) < 1e-9,
        "rss_improves": rss_improvement > 0,
        "chi2_improves": chi2_improvement > 0,
    }

    score = sum(1 for v in tests.values() if v)
    max_score = len(tests)

    if score == max_score:
        status = "BAO_common_split_operator_supported"
    elif score >= 3:
        status = "BAO_common_split_operator_partial"
    else:
        status = "BAO_common_split_operator_not_supported"

    result = {
        "signature": "ROW20_BAO_COMMON_SPLIT_OPERATOR_v1",
        "context": "DIAGNOSTIC_NON_GATING",
        "operator": {
            "r_common": "(r_H + r_D_M)/2",
            "r_split": "(r_H - r_D_M)/2",
            "lambda_split": lambda_split,
            "rule": "preserve common mode; neutralize split mode"
        },
        "input_residuals": r,
        "r_common": r_common,
        "r_split": r_split,
        "transformed_residuals": transformed,
        "original_rss": original_rss,
        "transformed_rss": transformed_rss,
        "rss_improvement": rss_improvement,
        "original_chi2": original_chi2,
        "transformed_chi2": transformed_chi2,
        "chi2_improvement": chi2_improvement,
        "tests": tests,
        "score": score,
        "max_score": max_score,
        "verdict": f"{status}; diagnostic only; no physics, thresholds, datasets, 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 v22 — BAO Common/Split Projection Operator\n\n")
        f.write("**Context:** DIAGNOSTIC_NON_GATING\n\n")

        f.write("## Decomposition\n\n")
        f.write("| Quantity | Value |\n")
        f.write("|---|---:|\n")
        f.write(f"| r_H | {rH:.6f} |\n")
        f.write(f"| r_D_M | {rDM:.6f} |\n")
        f.write(f"| r_common | {r_common:.6f} |\n")
        f.write(f"| r_split | {r_split:.6f} |\n")
        f.write(f"| lambda_split | {lambda_split:.6f} |\n\n")

        f.write("## Residual transformation\n\n")
        f.write("| Node | Before | After |\n")
        f.write("|---|---:|---:|\n")
        for k in r:
            f.write(f"| {k} | {r[k]:.6f} | {transformed[k]:.6f} |\n")

        f.write("\n## Metrics\n\n")
        f.write("| Quantity | Value |\n")
        f.write("|---|---:|\n")
        f.write(f"| RSS before | {original_rss:.6f} |\n")
        f.write(f"| RSS after | {transformed_rss:.6f} |\n")
        f.write(f"| RSS improvement | {rss_improvement:.6f} |\n")
        f.write(f"| chi2 before | {original_chi2:.6f} |\n")
        f.write(f"| chi2 after | {transformed_chi2:.6f} |\n")
        f.write(f"| chi2 improvement | {chi2_improvement:.6f} |\n")

        f.write("\n## Tests\n\n")
        for k, v in tests.items():
            f.write(f"- {k}: {v}\n")

        f.write(f"\nScore: {score}/{max_score}\n")
        f.write(f"Verdict: {status}\n")

    print("BAO common/split operator:")
    print(f"r_H={rH:.6f}")
    print(f"r_D_M={rDM:.6f}")
    print(f"r_common={r_common:.6f}")
    print(f"r_split={r_split:.6f}")
    print(f"RSS before={original_rss:.6f}")
    print(f"RSS after={transformed_rss:.6f}")
    print(f"RSS improvement={rss_improvement:.6f}")
    print(f"score={score}/{max_score}")
    print(f"status={status}")


if __name__ == "__main__":
    main()