"""Calibra una superficie premi REALE dalla catena cerbero-bite -> data/games/opt_calib_*.json.

Per ETH e BTC, dalla chain reale (OptionChain): premio mediano (ask, %spot), spread
bid/ask mediano, e IV mediana per (moneyness OTM x tenor). Piu' DVOL medio della finestra
(per scalare i premi sulla storia). + gate liquidita': max OTM con bid>0 frequente.
Cosi' il motore del gioco prezza con NUMERI REALI invece del Black-Scholes sintetico.

    uv run python -m scripts.games.opt_calibrate
"""
from __future__ import annotations

import json
import sys
from pathlib import Path

import numpy as np
import pandas as pd

PROJECT_ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(PROJECT_ROOT))

from scripts.analysis.options_chain import OptionChain

OUT = PROJECT_ROOT / "data" / "games"
# griglie: OTM firmato (put<0, call>0) e tenor in giorni
OTM_GRID = [-0.25, -0.20, -0.15, -0.10, -0.07, -0.05, -0.03, 0.0,
            0.03, 0.05, 0.07, 0.10, 0.15, 0.20, 0.25]
TEN_GRID = [7, 14, 21, 30, 45]


def calibrate(asset: str):
    oc = OptionChain(asset)
    d = oc.df.copy()
    spot = oc._spot_proxy()
    d["spot"] = d["timestamp"].map(spot)
    d = d.dropna(subset=["spot", "ask", "bid", "iv"])
    d = d[d["ask"] > 0]
    d["otm"] = d["strike"] / d["spot"] - 1.0          # firmato: <0 put OTM, >0 call OTM
    d["prem_pct"] = d["ask"] * 100.0                   # ask in coin -> %notional
    d["spread"] = (d["ask"] - d["bid"]) / ((d["ask"] + d["bid"]) / 2)
    d["sellable"] = (d["bid"] > 0).astype(float)
    # superficie: per ciascun (tipo, otm_bin, tenor_bin) -> mediane
    surf = {"P": {}, "C": {}}
    for typ in ("P", "C"):
        dt = d[d["option_type"] == typ]
        for ten in TEN_GRID:
            tlo, thi = ten * 0.6, ten * 1.6
            dtt = dt[(dt["tenor_d"] >= tlo) & (dt["tenor_d"] <= thi)]
            for otm in OTM_GRID:
                # banda moneyness +-1.5% attorno al target
                band = dtt[(dtt["otm"] >= otm - 0.02) & (dtt["otm"] <= otm + 0.02)]
                if len(band) < 5:
                    continue
                surf[typ][f"{otm:+.2f}|{ten}"] = dict(
                    prem=round(float(band["prem_pct"].median()), 4),
                    spread=round(float(band["spread"].median()), 4),
                    iv=round(float(band["iv"].median()), 4),
                    sellable=round(float(band["sellable"].mean()), 3),
                    n=int(len(band)))
    dvol_avg = float(np.nanmedian(d["iv"][d["otm"].abs() < 0.03]))  # ~ATM IV medio
    # gate liquidita': OTM piu' profondo (put) con bid>0 nel >=50% dei casi
    puts = d[d["option_type"] == "P"]
    deep = puts[puts["otm"] <= -0.10]
    out = {"asset": asset, "dvol_chain": round(dvol_avg, 4),
           "surface": surf, "otm_grid": OTM_GRID, "ten_grid": TEN_GRID,
           "window": [str(oc.df["ts"].min())[:10], str(oc.df["ts"].max())[:10]]}
    (OUT / f"opt_calib_{asset.lower()}.json").write_text(json.dumps(out))
    npts = len(surf["P"]) + len(surf["C"])
    print(f"{asset}: {npts} punti superficie | ATM IV ~{dvol_avg:.2f} | finestra {out['window']}")
    # stampa qualche premio reale put per sanity
    for key in ["-0.05|14", "-0.10|14", "-0.15|30", "-0.20|45"]:
        v = surf["P"].get(key)
        if v:
            print(f"   put {key:>9}: prem {v['prem']:.2f}% spread {v['spread']*100:.0f}% "
                  f"iv {v['iv']:.0f}% sellable {v['sellable']*100:.0f}% (n={v['n']})")


if __name__ == "__main__":
    for a in ("BTC", "ETH"):
        calibrate(a)
