"""
verifier-panel — rho-aware critic-panel budgeting + the Phi-fit tail check.

Extends the shipped verifier-budget tool with the two operational rules produced
by the 2026-07-26 campaigns (epsilon-universality/ + correlated-critique/):

  1. PANEL: given a critic panel's measured per-critic error e and pairwise
     co-error P_both, estimate the SHARED error component s, the ceiling 1-s
     that no amount of same-substrate sampling crosses, the effective panel
     size n_eff = N/(1+(N-1)rho), and whether the next unit of budget should
     buy MORE SAMPLES or a DECORRELATED channel.
       s   = (P_both - e^2) / (1 + P_both - 2e)      [common-cause inversion]
       rho = (P_both - e^2) / (e(1-e))               [pairwise error corr]
     (Classical: Condorcet-with-correlation, Ladha 1992; Clemen-Winkler n_eff;
     Breiman 2001 strength-correlation bound. Nothing here is novel math; the
     tool makes it actionable, exactly as N*(eps) did for Thurstone/SDT.)

  2. CHECK-TAIL: before trusting ANY Phi-based extrapolation of eps to small
     values, test the Gaussian precondition. The Gaussian law is uniformly
     OPTIMISTIC under heavy tails — measured 11x at gap/sigma=4 and 723x at
     gap/sigma=6 (epsilon-universality/exp_w) — and one excess-kurtosis
     statistic separates the regimes (approx -0.07 Gaussian vs +26.8 t(3) at
     n=20k). Estimate eps directly; never extrapolate a Phi fit past the tail
     check.

Usage:
  verifier-panel panel --e 0.30 --pboth 0.15 [--n 5] [--target-acc 0.9]
  verifier-panel check-tail scores.txt [--json]
  verifier-panel check-tail --gap 1.0 --sigma 0.25   (pure-Phi warning mode)
"""

from __future__ import annotations

import argparse
import math
import sys


# --------------------------------------------------------------------- panel
def shared_component(e: float, p_both: float) -> tuple[float, float]:
    """(s, rho) from per-critic error e and pairwise co-error P_both."""
    if not (0 < e < 1):
        raise ValueError("e must be in (0,1)")
    lo, hi = e * e, e
    p_both = min(max(p_both, lo), hi)          # clamp to feasible region
    s = (p_both - e * e) / (1 + p_both - 2 * e)
    rho = (p_both - e * e) / (e * (1 - e))
    return max(0.0, s), max(0.0, min(1.0, rho))


def n_eff(N: int, rho: float) -> float:
    return N / (1 + (N - 1) * rho)


def majority_ceiling(s: float) -> float:
    """Accuracy ceiling for same-substrate majority as N -> infinity."""
    return 1.0 - s


def panel_report(e: float, p_both: float, N: int, target: float | None) -> None:
    s, rho = shared_component(e, p_both)
    ceil = majority_ceiling(s)
    print(f"critic panel  (per-critic error e={e:.3f}, pairwise co-error P_both={p_both:.3f})")
    print(f"  pairwise error correlation rho = {rho:.3f}")
    print(f"  shared error component     s   = {s:.3f}")
    print(f"  majority-vote CEILING      1-s = {ceil:.3f}   <- no same-substrate N crosses this")
    print(f"  effective panel size at N={N}:  n_eff = {n_eff(N, rho):.2f}  (of {N})")
    if target is not None:
        if target > ceil + 1e-9:
            print(f"  TARGET {target:.3f} is ABOVE the ceiling {ceil:.3f}.")
            print("  RECOMMENDATION: more same-substrate critics CANNOT reach it.")
            print("  Spend the next unit of budget on a DECORRELATED channel")
            print("  (different model/training distribution, execution, solver, source-check),")
            print("  which attacks s directly. Sampling only attacks the idiosyncratic part.")
        else:
            # how many critics to reach target given rho (normal approx on the vote)
            # majority correct when mean vote > 1/2; use n_eff in the CLT term
            from math import sqrt
            need = None
            for n in range(1, 2000):
                ne = n_eff(n, rho)
                # P(majority correct) approx on non-shared items, times (1-s) + shared-item floor
                z = (0.5 - e) * sqrt(ne) / sqrt(e * (1 - e))
                acc = (1 - s) * (0.5 * (1 + math.erf(z / math.sqrt(2)))) + 0.0
                # crude: on shared-failure items panel is wrong; on the rest CLT majority
                if acc >= target:
                    need = n
                    break
            if need:
                print(f"  TARGET {target:.3f} is reachable: ~{need} same-substrate critics suffice.")
            else:
                print(f"  TARGET {target:.3f} not reached by N<=2000 at this rho; decorrelate.")
    print("  NOTE (small panels): at small N a better-but-correlated critic can still beat")
    print("  worse-but-independent ones (crossover is N-dependent; correlated-critique/R5b).")


# ---------------------------------------------------------------- check-tail
def excess_kurtosis(xs: list[float]) -> float:
    n = len(xs)
    m = sum(xs) / n
    var = sum((x - m) ** 2 for x in xs) / n
    if var == 0:
        return 0.0
    m4 = sum((x - m) ** 4 for x in xs) / n
    return m4 / (var * var) - 3.0


def check_tail_scores(path: str) -> int:
    with open(path) as f:
        xs = [float(t) for t in f.read().split()]
    if len(xs) < 500:
        print(f"WARNING: only {len(xs)} scores; kurtosis is noisy below ~500 samples.")
    k = excess_kurtosis(xs)
    print(f"n={len(xs)}  excess kurtosis = {k:+.3f}")
    if k > 1.0:
        print("VERDICT: HEAVY-TAILED (kurt > 1). The Gaussian eps law is uniformly")
        print("  OPTIMISTIC here — measured up to 723x at gap/sigma=6. Do NOT")
        print("  extrapolate a Phi fit to small eps. Estimate eps directly by counting.")
        return 1
    if k > 0.3:
        print("VERDICT: BORDERLINE (0.3 < kurt <= 1). Treat Phi-based small-eps")
        print("  extrapolations as lower bounds only; prefer direct estimation.")
        return 0
    print("VERDICT: consistent with Gaussian (kurt <= 0.3). Phi-based eps(gap, sigma)")
    print("  = Phi(-gap/(sigma*sqrt(2))) is trustworthy in the sampled range —")
    print("  it is Thurstone (1927) Case V, exact for Gaussian scores.")
    return 0


def phi_warning(gap: float, sigma: float) -> None:
    z = gap / (sigma * math.sqrt(2))
    eps = 0.5 * (1 - math.erf(z / math.sqrt(2)))
    print(f"Phi-based eps(gap={gap}, sigma={sigma}) = {eps:.2e}")
    if eps < 1e-3:
        print("CAUTION: this is a small-eps extrapolation. Under Student-t(3) tails the")
        print("  true eps at this gap/sigma can exceed the Gaussian value by 10-700x.")
        print("  Run `verifier-panel check-tail <scores-file>` before trusting it.")


def cli(argv: list[str] | None = None) -> int:
    ap = argparse.ArgumentParser(
        prog="verifier-panel",
        description="rho-aware critic-panel budgeting and the Phi-fit tail check.")
    sub = ap.add_subparsers(dest="cmd", required=True)

    p1 = sub.add_parser("panel", help="ceiling + n_eff from measured (e, P_both)")
    p1.add_argument("--e", type=float, required=True, help="per-critic error rate")
    p1.add_argument("--pboth", type=float, required=True,
                    help="pairwise co-error rate (both critics wrong on same item)")
    p1.add_argument("--n", type=int, default=5, help="current panel size")
    p1.add_argument("--target-acc", type=float, default=None,
                    help="desired panel accuracy; tool says whether sampling can reach it")

    p2 = sub.add_parser("check-tail", help="test the Gaussian precondition of the eps law")
    p2.add_argument("scores", nargs="?", help="file of whitespace-separated scores")
    p2.add_argument("--gap", type=float, help="score gap (pure-Phi warning mode)")
    p2.add_argument("--sigma", type=float, help="score noise (pure-Phi warning mode)")

    args = ap.parse_args(argv)
    if args.cmd == "panel":
        panel_report(args.e, args.pboth, args.n, args.target_acc)
        return 0
    if args.scores:
        return check_tail_scores(args.scores)
    if args.gap is not None and args.sigma is not None:
        phi_warning(args.gap, args.sigma)
        return 0
    print("check-tail needs a scores file, or --gap and --sigma.", file=sys.stderr)
    return 2


if __name__ == "__main__":
    raise SystemExit(cli())
