"""LOGGER FORWARD della vol term-structure Deribit (l'UNICA via legittima per avere il dato).

Lo scan (probe_vol_termstructure.py) ha stabilito: la storia per-scadenza NON e' pubblica su Deribit
(DVOL solo 30g; trade-history IV solo per strumenti vivi; il front rotola). Quindi un calendar-vol
NON e' backtestabile oggi. Questo logger COSTRUISCE il dataset in avanti: ogni run prende lo snapshot
ATM mark_iv per scadenza, lo interpola a TENOR FISSI (7/30/60/90/180g) e appende una riga per asset a
data/raw/vol_term_<asset>.parquet. Idempotente per giorno (riscrive la riga del giorno).

Da cron giornaliero (NON auto-cablato: e' ricerca forward, va aggiunto a mano se si vuole):
    uv run python scripts/research/log_vol_termstructure.py

Dopo ~6-12 mesi di accumulo -> certificare (cross-venue, monotonia, spike) e SOLO ALLORA testare un
calendar-vol (front vs back) su dati certificati. Nessun edge creduto prima.
"""
from __future__ import annotations
import sys
from pathlib import Path

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

import numpy as np
import pandas as pd
import requests

BASE = "https://www.deribit.com/api/v2/public/"
RAW = PROJECT_ROOT / "data" / "raw"
TENORS = [7, 30, 60, 90, 180]                 # giorni-a-scadenza fissi su cui interpolare l'ATM IV


def _get(method, **params):
    return requests.get(BASE + method, params=params, timeout=40).json().get("result", None)


def _atm_curve(cur, now_ms):
    """[(dte_giorni, atm_iv%)] ordinato per scadenza, dallo strike piu' vicino allo spot."""
    summ = _get("get_book_summary_by_currency", currency=cur, kind="option")
    idx = _get("get_index_price", index_name=f"{cur.lower()}_usd")
    if not summ or not idx:
        return None, None
    spot = float(idx["index_price"])
    best = {}                                  # exp_ms -> (dist_strike, iv)
    for o in summ:
        p = o["instrument_name"].split("-")
        if len(p) != 4 or o.get("mark_iv") is None:
            continue
        try:
            exp_ms = int(_dt.datetime.strptime(p[1], "%d%b%y").replace(
                tzinfo=_dt.timezone.utc).timestamp() * 1000)
        except ValueError:
            continue
        d = abs(float(p[2]) - spot)
        if exp_ms not in best or d < best[exp_ms][0]:
            best[exp_ms] = (d, float(o["mark_iv"]))
    curve = sorted(((e - now_ms) / 86400_000.0, iv) for e, (_, iv) in best.items() if e > now_ms)
    return spot, curve


def build_row(spot, curve, now_ms):
    """Pura: da (spot, curve=[(dte,iv)], now_ms) -> riga con ATM IV interpolata ai TENOR fissi."""
    if not curve or len(curve) < 2:
        return None
    dtes = np.array([c[0] for c in curve]); ivs = np.array([c[1] for c in curve])
    row = {"date": pd.Timestamp(now_ms, unit="ms", tz="UTC").normalize(), "spot": spot}
    for t in TENORS:
        row[f"iv_{t}d"] = float(np.interp(t, dtes, ivs))     # interp lineare sui DTE disponibili
    row["slope_7_180"] = row["iv_180d"] - row["iv_7d"]
    return row


def snapshot(cur, now_ms):
    spot, curve = _atm_curve(cur, now_ms)
    return build_row(spot, curve, now_ms) if curve else None


def append_row(cur, row):
    fp = RAW / f"vol_term_{cur.lower()}.parquet"
    df = pd.read_parquet(fp) if fp.exists() else pd.DataFrame()
    df = df[df["date"] != row["date"]] if len(df) else df    # idempotente per giorno
    df = pd.concat([df, pd.DataFrame([row])], ignore_index=True).sort_values("date")
    df.to_parquet(fp, index=False)
    return fp, len(df)


def main():
    now_ms = int(pd.Timestamp.now("UTC").timestamp() * 1000)
    print("=" * 78)
    print("  LOG vol term-structure (forward dataset builder)")
    print("=" * 78)
    for cur in ("BTC", "ETH"):
        row = snapshot(cur, now_ms)
        if row is None:
            print(f"  {cur}: snapshot fallito (chain vuota?)"); continue
        fp, n = append_row(cur, row)
        ivs = "  ".join(f"{t}g {row[f'iv_{t}d']:.1f}%" for t in TENORS)
        print(f"  {cur} {row['date'].date()}  spot ${row['spot']:,.0f}  | {ivs} | "
              f"slope7-180 {row['slope_7_180']:+.1f}pp  -> {n} righe in {fp.name}")
    print("\n  (forward-only: serve accumulo di mesi prima di poter certificare e testare un calendar-vol)")


if __name__ == "__main__":
    main()
