#!/usr/bin/env python3
import csv
import json
from fractions import Fraction
from pathlib import Path

import matplotlib

matplotlib.use("Agg")
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon, Rectangle


ROOT = Path(__file__).resolve().parents[1]
DATA = ROOT / "data" / "F2_decision_identifiability_data.csv"
OUT_PNG = ROOT / "figures" / "F2_decision_identifiability.png"
OUT_SVG = ROOT / "figures" / "F2_decision_identifiability.svg"
OUT_VALIDATION = ROOT / "validation" / "F2_decision_identifiability_validation.json"


def q(value):
    return value if isinstance(value, Fraction) else Fraction(str(value))


def classify_point(debt, adjustment, baseline=10, limit=5):
    denominator = q(baseline) + q(adjustment)
    if denominator <= 0:
        raise ValueError("denominator must be positive")
    return "PASS" if q(debt) / denominator <= q(limit) else "FAIL"


def classify_interval(debt, lower, upper, baseline=10, limit=5):
    lower_q, upper_q = q(lower), q(upper)
    if lower_q > upper_q:
        raise ValueError("lower bound exceeds upper bound")
    if q(baseline) + lower_q <= 0:
        raise ValueError("denominator must remain positive")
    worst = q(debt) / (q(baseline) + lower_q)
    best = q(debt) / (q(baseline) + upper_q)
    if worst <= q(limit):
        return "SUPPORTED_PASS"
    if best > q(limit):
        return "SUPPORTED_FAIL"
    return "UNRESOLVED"


def read_rows(path=DATA):
    with path.open(newline="", encoding="utf-8") as handle:
        rows = list(csv.DictReader(handle))
    for row in rows:
        status = classify_interval(
            row["debt_eur_m"], row["adjustment_lower_eur_m"], row["adjustment_upper_eur_m"],
            row["baseline_ebitda_eur_m"], row["ratio_limit"],
        )
        if status != row["expected_status"]:
            raise ValueError(f"status mismatch for {row['case_id']}: {status}")
        if row["evidence_class"] != "synthetic_deterministic":
            raise ValueError("unexpected evidence class")
    return rows


def make_figure(rows):
    plt.rcParams.update({
        "font.family": "DejaVu Sans",
        "font.size": 9.5,
        "axes.titlesize": 12,
        "axes.labelsize": 10,
        "svg.hashsalt": "eoji-vr005-f2",
    })
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12.8, 6.5))

    # Panel A: exact states. There is no unresolved point state.
    x = [0, 3]
    boundary = [50, 65]
    ax1.add_patch(Polygon([(0, 40), (3, 40), (3, 65), (0, 50)], closed=True,
                          facecolor="#dce8e2", edgecolor="none", hatch=".."))
    ax1.add_patch(Polygon([(0, 50), (3, 65), (3, 75), (0, 75)], closed=True,
                          facecolor="#eadfdf", edgecolor="none", hatch="//"))
    ax1.plot(x, boundary, color="#1b2a41", linewidth=2.2)
    ax1.text(0.15, 43.0, "PASS\nD ≤ 5(10+A)", fontweight="bold")
    ax1.text(1.9, 71.0, "FAIL\nD > 5(10+A)", fontweight="bold")
    ax1.set_xlim(0, 3)
    ax1.set_ylim(40, 75)
    ax1.set_xlabel("Exact adjustment A (EURm)")
    ax1.set_ylabel("Debt D (EURm)")
    ax1.set_title("A. Exact states have a determinate answer", loc="left", fontweight="bold")
    ax1.grid(color="#ffffff", linewidth=0.9)

    # Panel B: interval evidence for D=60 in lower/upper-bound space.
    # Valid triangle: 0 ≤ L ≤ U ≤ 3. Threshold A*=2.
    fail_poly = Polygon([(0, 0), (0, 2), (2, 2)], closed=True,
                        facecolor="#eadfdf", edgecolor="#6a7078", hatch="//")
    unresolved_poly = Polygon([(0, 2), (0, 3), (2, 3), (2, 2)], closed=True,
                              facecolor="#eee9d8", edgecolor="#6a7078", hatch="xx")
    pass_poly = Polygon([(2, 2), (2, 3), (3, 3)], closed=True,
                        facecolor="#dce8e2", edgecolor="#6a7078", hatch="..")
    for poly in (fail_poly, unresolved_poly, pass_poly):
        ax2.add_patch(poly)
    ax2.plot([0, 3], [0, 3], color="#1b2a41", linewidth=1.2)
    ax2.axhline(2, color="#1b2a41", linestyle="--", linewidth=1.0)
    ax2.axvline(2, color="#1b2a41", linestyle="--", linewidth=1.0)
    ax2.scatter([0], [3], marker="o", s=100, color="#1b2a41", zorder=4)
    ax2.text(0.12, 2.92, "●  Example interval [0,3]", ha="left", va="top", fontsize=8.7)
    ax2.text(0.45, 1.0, "SUPPORTED FAIL\nU < 2", ha="center", va="center", fontweight="bold")
    ax2.text(0.85, 2.28, "UNRESOLVED\nL < 2 ≤ U", ha="center", va="center", fontweight="bold")
    ax2.text(2.48, 2.72, "SUPPORTED PASS\nL ≥ 2", ha="center", va="center", fontweight="bold")
    ax2.set_xlim(0, 3)
    ax2.set_ylim(0, 3)
    ax2.set_aspect("equal", adjustable="box")
    ax2.set_xlabel("Lower admissible bound L for A (EURm)")
    ax2.set_ylabel("Upper admissible bound U for A (EURm)")
    ax2.set_title("B. For D=60, evidence is sufficient only if\nits interval stays on one side", loc="left", fontweight="bold")

    fig.suptitle("A missing value is not always a missing decision", x=0.04, y=0.98, ha="left", fontsize=15, fontweight="bold")
    fig.subplots_adjust(left=0.07, right=0.98, top=0.82, bottom=0.18, wspace=0.24)
    fig.text(0.04, 0.035, "Synthetic exact calculation: D/(10+A) ≤ 5, 0 ≤ A ≤ 3. No probability, borrower observation or legal conclusion.", fontsize=9, color="#3f4854")
    metadata = {"Creator": "EOJI visual research 005", "Date": None}
    fig.savefig(OUT_PNG, dpi=220, metadata=metadata)
    fig.savefig(OUT_SVG, metadata=metadata)
    plt.close(fig)


def main():
    rows = read_rows()
    make_figure(rows)
    boundary_checks = {
        "D45_threshold_A": -1.0,
        "D60_threshold_A": 2.0,
        "D70_threshold_A": 4.0,
        "D60_at_A2": classify_point(60, 2),
        "D60_below_A2": classify_point(60, 1.999),
        "D60_above_A2": classify_point(60, 2.001),
    }
    validation = {
        "status": "PASS",
        "evidence_class": "synthetic_deterministic",
        "formula": "D/(10+A) ≤ 5",
        "interval_results": {r["case_id"]: r["expected_status"] for r in rows},
        "boundary_checks": boundary_checks,
        "monotonicity": "ratio decreases as A increases for positive denominator; boundary debt increases 5 EURm per 1 EURm A",
        "outputs": [str(OUT_PNG.relative_to(ROOT)), str(OUT_SVG.relative_to(ROOT))],
        "exclusions": ["no empirical frequency", "no probability", "no legal compliance", "no EOJI performance"],
    }
    OUT_VALIDATION.write_text(json.dumps(validation, indent=2) + "\n", encoding="utf-8")


if __name__ == "__main__":
    main()
