#!/usr/bin/env python3
"""
Rank all ``build_catalog_100()`` specs on one date window: sequential
:func:`run_signal_backtest` + realized-equity Sharpe and total return vs ``--capital``.

Writes CSV for sorting in Excel / follow-on, and prints top rows to stdout.

Example::

    cd /Users/robzingale/trading_bot && PYTHONUNBUFFERED=1 .venv/bin/python \\
      RenTech/strategy_stack/rank_lit_catalog_window.py \\
      --start 2016-01-04 --end 2026-04-02 --capital 100000 \\
      --out-csv RenTech/data/logs/lit_catalog_rank_2016_2026_apr02.csv
"""
from __future__ import annotations

import argparse
import csv
import math
import sys
from pathlib import Path

_REPO = Path(__file__).resolve().parents[2]
if str(_REPO) not in sys.path:
    sys.path.insert(0, str(_REPO))

from RenTech.core.theta_chunks_loader import theta_chunks_date_bounds
from RenTech.strategy_stack import research_literature_theta_strategies as L
from RenTech.strategy_stack.literature_search_agent import _compile_signal, _compile_trade
from RenTech.strategy_stack.literature_strategy_catalog import build_catalog_100


def main() -> None:
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--theta-dir", type=Path, default=_REPO / "RenTech/data/theta_chunks")
    ap.add_argument("--start", type=str, default="2016-01-04")
    ap.add_argument("--end", type=str, default="", help="Inclusive; empty = Theta upper bound")
    ap.add_argument("--capital", type=float, default=100_000.0)
    ap.add_argument(
        "--out-csv",
        type=Path,
        default=_REPO / "RenTech/data/logs/lit_catalog_rank_default.csv",
    )
    ap.add_argument("--top", type=int, default=25, help="How many rows to print per sort key")
    args = ap.parse_args()

    end = str(args.end).strip()
    if not end:
        _, d1 = theta_chunks_date_bounds(args.theta_dir.expanduser().resolve())
        end = d1.strftime("%Y-%m-%d")
        print(f"--end omitted → {end}", flush=True)

    cap = float(args.capital)
    print(f"Preparing Theta {args.start}..{end} …", flush=True)
    days, panel, get_chain, iv_atm, skew_put_minus_call_iv, n_contracts, spy_wide = (
        L.prepare_theta_research_context(
            theta_dir=args.theta_dir,
            capital=cap,
            start=str(args.start),
            end=end,
            max_days=0,
        )
    )
    print(f"  sessions={len(days)}  capital=${cap:,.0f}", flush=True)

    rows: list[dict[str, object]] = []
    for spec in build_catalog_100():
        sig = _compile_signal(spec, panel, iv_atm, skew_put_minus_call_iv, n_contracts, spy_wide)
        tfn = _compile_trade(spec)
        ex, pnls, ntr = L.run_signal_backtest(
            days, get_chain, panel, sig, int(spec.hold), tfn, spec.trade_params
        )
        eq, sh = L.equity_curve_from_realized(ex, pnls, days, cap)
        end_eq = float(eq.iloc[-1])
        pnl = end_eq - cap
        ret_pct = (end_eq / cap - 1.0) * 100.0 if cap > 0 else float("nan")
        rows.append(
            {
                "sid": spec.sid,
                "family": spec.family,
                "description": spec.description,
                "hold": int(spec.hold),
                "trades": int(ntr),
                "sharpe": float(sh) if math.isfinite(sh) else float("nan"),
                "total_pnl_usd": round(pnl, 2),
                "total_return_pct": round(ret_pct, 4),
                "end_equity_usd": round(end_eq, 2),
            }
        )

    out = args.out_csv.expanduser().resolve()
    out.parent.mkdir(parents=True, exist_ok=True)
    keys = list(rows[0].keys()) if rows else []
    with out.open("w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=keys)
        w.writeheader()
        w.writerows(rows)
    print(f"Wrote {out}", flush=True)

    top_n = int(args.top)
    finite = [r for r in rows if math.isfinite(float(r["sharpe"]))]
    by_sh = sorted(finite, key=lambda r: float(r["sharpe"]), reverse=True)[:top_n]
    by_ret = sorted(rows, key=lambda r: float(r["total_return_pct"]), reverse=True)[:top_n]

    print(f"\nTop {top_n} by Sharpe (finite only, n={len(finite)}):", flush=True)
    for r in by_sh:
        print(
            f"  {r['sharpe']:7.3f}  {r['sid']:8s}  {r['trades']:4d}  {str(r['family']):18s}  "
            f"{float(r['total_return_pct']):8.2f}%  {r['description'][:64]}",
            flush=True,
        )

    print(f"\nTop {top_n} by total return %:", flush=True)
    for r in by_ret:
        sh = r["sharpe"]
        shs = f"{float(sh):.3f}" if isinstance(sh, (int, float)) and math.isfinite(float(sh)) else "nan"
        print(
            f"  {float(r['total_return_pct']):8.2f}%  Sharpe={shs:>7s}  {r['sid']:8s}  {int(r['trades']):4d}  "
            f"{str(r['family']):18s}  {r['description'][:52]}",
            flush=True,
        )


if __name__ == "__main__":
    main()
