#!/usr/bin/env python3
"""
Build ``vrp_fund_pitch_multi_sleeve_data.js`` from multi-sleeve merge outputs so ``vrp_fund_pitch.html``
can show real CAGR / Sharpe / max DD, equity curve points, and per-sleeve trade rows (entry/exit,
raw vs scaled PnL, risk model, optional full ``src_json`` payload).

Inputs (defaults): ``RenTech/data/logs/multi_sleeve_default_equity_daily.csv`` and
``RenTech/data/logs/multi_sleeve_default_ALL_SLEEVES_trade_log.csv``.

Re-run after: ``run_multi_sleeve_portfolio.py``.
"""
from __future__ import annotations

import argparse
import csv
import json
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))

import pandas as pd

from RenTech.strategy_stack.portfolio_vrp_plus_vxx import _metrics_block


def _str_or(row: dict[str, str], key: str, default: str = "") -> str:
    return (row.get(key) or "").strip() or default


def _float_or_none(row: dict[str, str], key: str) -> float | None:
    v = row.get(key, "")
    if not v:
        return None
    try:
        f = float(v)
        return f if math.isfinite(f) else None
    except (TypeError, ValueError):
        return None


def _decimal_year(ts: pd.Timestamp) -> float:
    y = int(ts.year)
    start = pd.Timestamp(year=y, month=1, day=1)
    next_y = pd.Timestamp(year=y + 1, month=1, day=1)
    return y + float((ts - start) / (next_y - start))


def main() -> None:
    ap = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
    ap.add_argument(
        "--equity-csv",
        type=Path,
        default=_REPO / "RenTech/data/logs/multi_sleeve_default_equity_daily.csv",
    )
    ap.add_argument(
        "--trades-csv",
        type=Path,
        default=_REPO / "RenTech/data/logs/multi_sleeve_default_ALL_SLEEVES_trade_log.csv",
    )
    ap.add_argument(
        "--out-js",
        type=Path,
        default=_REPO / "vrp_fund_pitch_multi_sleeve_data.js",
    )
    ap.add_argument(
        "--max-src-json-chars",
        type=int,
        default=4000,
        help="Truncate src_json in embedded trade rows (0 = no truncate)",
    )
    args = ap.parse_args()

    eq_path = args.equity_csv.expanduser().resolve()
    tr_path = args.trades_csv.expanduser().resolve()
    out_js = args.out_js.expanduser().resolve()

    if not eq_path.is_file():
        raise SystemExit(f"Missing equity CSV: {eq_path}")
    if not tr_path.is_file():
        raise SystemExit(f"Missing trades CSV: {tr_path}")

    df = pd.read_csv(eq_path, index_col=0, parse_dates=True)
    if "eq_full" not in df.columns:
        raise SystemExit("equity CSV missing eq_full column")
    eq = df["eq_full"].astype(float)
    m = _metrics_block(eq, "multi_sleeve")

    chart = [
        {"x": round(_decimal_year(pd.Timestamp(t)), 5), "y": float(eq.loc[t])}
        for t in eq.index
        if pd.notna(t)
    ]

    trades: list[dict] = []
    max_src = int(args.max_src_json_chars)
    with tr_path.open(encoding="utf-8") as f:
        rdr = csv.DictReader(f)
        for row in rdr:
            sj = row.get("src_json") or ""
            if max_src > 0 and len(sj) > max_src:
                sj = sj[: max_src - 3] + "..."
            trades.append(
                {
                    "sleeve_id": _str_or(row, "sleeve_id"),
                    "entry_date": _str_or(row, "entry_date"),
                    "exit_date": _str_or(row, "exit_date"),
                    # brokerage-statement columns
                    "legs_display": _str_or(row, "legs_display"),
                    "broker_risk_usd_logged": _float_or_none(row, "broker_risk_usd_logged"),
                    "contracts_qty_logged": _str_or(row, "contracts_qty_logged"),
                    # PnL
                    "pnl_usd_raw_in_log": float(row.get("pnl_usd_raw_in_log") or 0),
                    # merge-scaling fields (kept in CSV for reproducibility; not shown in main table)
                    "pnl_usd_scaled": float(row.get("pnl_usd_scaled") or 0),
                    "sleeve_scale_factor": float(row.get("sleeve_scale_factor") or 0),
                    "sleeve_risk_ref_usd": float(row.get("sleeve_risk_ref_usd") or 0),
                    "sleeve_capital_usd": float(row.get("sleeve_capital_usd") or 0),
                    "risk_model": _str_or(row, "risk_model"),
                    "source_jsonl": _str_or(row, "source_jsonl"),
                    "src_json": sj,
                }
            )

    sleeve_ids = sorted({t["sleeve_id"] for t in trades if t.get("sleeve_id")})

    meta = {
        "equity_csv": str(eq_path.relative_to(_REPO)) if str(eq_path).startswith(str(_REPO)) else str(eq_path),
        "trades_csv": str(tr_path.relative_to(_REPO)) if str(tr_path).startswith(str(_REPO)) else str(tr_path),
        "calendar_start": str(eq.index.min().date()) if len(eq) else "",
        "calendar_end": str(eq.index.max().date()) if len(eq) else "",
        "n_equity_days": int(len(eq)),
        "n_trades": len(trades),
        "sleeve_ids": sleeve_ids,
        "return_pct": float(m["return_pct"]) if math.isfinite(float(m["return_pct"])) else None,
        "cagr_pct": float(m["cagr_pct"]) if math.isfinite(float(m["cagr_pct"])) else None,
        "sharpe": float(m["sharpe"]) if math.isfinite(float(m["sharpe"])) else None,
        "max_dd_pct": float(m["max_dd_pct"]) if math.isfinite(float(m["max_dd_pct"])) else None,
        "end_equity": float(m["end_equity"]) if math.isfinite(float(m["end_equity"])) else None,
        "start_capital": float(m["start_capital"]),
        "years": float(m["years"]) if math.isfinite(float(m["years"])) else None,
    }

    payload = {
        "MULTI_SLEEVE_META": meta,
        "MULTI_SLEEVE_EQUITY_CHART": chart,
        "MULTI_SLEEVE_TRADES": trades,
    }

    out_js.parent.mkdir(parents=True, exist_ok=True)
    # Single assignment so ``file://`` pages can load without CORS fetch.
    body = (
        "// AUTO-GENERATED by RenTech/strategy_stack/generate_vrp_fund_pitch_multi_sleeve_assets.py\n"
        "// Do not edit by hand; re-run the generator after refreshing merge CSVs.\n"
        "window.MULTI_SLEEVE_BUNDLE = "
        + json.dumps(payload, separators=(",", ":"))
        + ";\n"
        "window.MULTI_SLEEVE_META = window.MULTI_SLEEVE_BUNDLE.MULTI_SLEEVE_META;\n"
        "window.MULTI_SLEEVE_EQUITY_CHART = window.MULTI_SLEEVE_BUNDLE.MULTI_SLEEVE_EQUITY_CHART;\n"
        "window.MULTI_SLEEVE_TRADES = window.MULTI_SLEEVE_BUNDLE.MULTI_SLEEVE_TRADES;\n"
    )
    out_js.write_text(body, encoding="utf-8")
    print(f"Wrote {out_js}  ({len(body) // 1024} KB)")


if __name__ == "__main__":
    main()
