"""ortho_score — judge a relative-value BOOK module for a LIVE ORTHOGONAL slot.

A module defines `book(btc, eth) -> (w_btc, w_eth)`. This runs the causality guard, the
standalone backtest, the MARGINAL-vs-TP01 score, and the executability check, then prints
one json line + a human verdict. earns_slot = ADDS + robust_oos + executable + causal.

  uv run python scripts/research/ortho/ortho_score.py --module <path.py>
  uv run python scripts/research/ortho/ortho_score.py --all      # score agents/*.py
"""
from __future__ import annotations

import argparse
import importlib.util
import json
import sys
from pathlib import Path

HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE))
import ortholib as ol  # noqa: E402

AGENTS = HERE / "agents"


def _book(path: Path):
    spec = importlib.util.spec_from_file_location(path.stem, path)
    mod = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(mod)
    return mod.book


def score(path: Path) -> dict:
    rec = {"name": path.stem}
    try:
        book = _book(path)
    except Exception as e:
        return {**rec, "error": f"import: {e}", "earns_slot": False}
    try:
        caus = ol.causality_ok(book)
        rec["causal"] = bool(caus.get("ok"))
        if not rec["causal"]:
            rec["causality"] = caus
            return {**rec, "earns_slot": False, "verdict": "LEAK"}
    except Exception as e:
        return {**rec, "error": f"causality: {e}", "earns_slot": False}
    try:
        r = ol.marginal(book)
        ev, m = r["book"], r["marginal"]
        rec.update(
            pnl=ev["pnl"], maxdd=ev["maxdd"], sharpe=ev["sharpe"],
            net_beta=ev["net_beta"], gross_lev=ev["gross_lev"], max_leg=ev["max_leg_frac"],
            turnover=ev["turnover_per_year"], executable=ev["executable"],
            corr_full=m.get("corr_full"), corr_hold=m.get("corr_hold"),
            uplift_hold=m.get("blends", {}).get("w25", {}).get("uplift_hold"),
            uplift_full=m.get("blends", {}).get("w25", {}).get("uplift_full"),
            alpha_ann=m.get("alpha_ann"), resid_sharpe=m.get("resid_sharpe_full"),
            robust_oos=m.get("robust_oos"), marginal_verdict=m.get("marginal_verdict"),
            clean_year_uplift=m.get("clean_year_uplift"),
            jackknife_min_uplift=m.get("jackknife_min_uplift"),
        )
        rec["earns_slot"] = bool(
            m.get("marginal_verdict") == "ADDS" and m.get("robust_oos") and ev["executable"])
    except Exception as e:
        import traceback
        return {**rec, "error": f"eval: {e}\n{traceback.format_exc()[-300:]}", "earns_slot": False}
    return rec


def _row(r: dict) -> str:
    if "error" in r:
        return f"  {r['name'][:30]:<30} ERROR {r['error'][:50]}"
    if r.get("verdict") == "LEAK":
        return f"  {r['name'][:30]:<30} LEAK (disqualified)"
    return (f"  {r['name'][:30]:<30} {str(r['marginal_verdict']):<9} "
            f"corr {str(r.get('corr_hold')):>6} up_h {str(r.get('uplift_hold')):>6} "
            f"rob {str(r.get('robust_oos')):>5} exec {str(r.get('executable')):>5} "
            f"| pnl {r['pnl']*100:>+5.0f}% dd {r['maxdd']*100:>3.0f}%  "
            f"{'<<< SLOT' if r.get('earns_slot') else ''}")


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--module"); ap.add_argument("--all", action="store_true")
    args = ap.parse_args()
    if args.all:
        rows = [score(p) for p in sorted(AGENTS.glob("agent_*.py"))]
        rows.sort(key=lambda r: (r.get("earns_slot", False),
                                 r.get("uplift_hold") or -9), reverse=True)
        print(f"\n  ORTHO-vs-TP01 leaderboard ({len(rows)} books)  judge = marginal uplift + robust_oos + executable")
        print(f"  {'name':<30} {'verdict':<9} {'corrH':>6} {'up_h':>6} {'rob':>5} {'exec':>5}  pnl/dd")
        print("  " + "-" * 92)
        for r in rows:
            print(_row(r))
        slots = [r for r in rows if r.get("earns_slot")]
        print(f"\n  EARNS SLOT: {[r['name'] for r in slots] or 'NONE'}")
        (HERE / "ortho_leaderboard.json").write_text(json.dumps(rows, indent=2, default=str))
    else:
        r = score(Path(args.module))
        print(json.dumps(r, indent=2, default=str))


if __name__ == "__main__":
    main()
