"""TP01 a BASSA FREQUENZA (>=12h) — ri-verifica dopo il bug look-ahead ffill-mixed-TF.

L'utente/agente ha trovato un look-ahead (ffill mixed-timeframe su barre open-labeled) che
gonfiava il 4h (~1.60 -> reale ~1.1) e ha concluso: NON scendere sotto le 12h (costi+overfit
dominano). Qui ricalcolo TP01 in modo PULITO per singolo TF (barre discrete, posizione shiftata
+1, NESSUN ffill/combine mixed-TF) su 4h/12h/1d, con un GUARD di causalita' esplicito sulla serie
resamplata (ricalcolo su prefisso). Fee 0.10% RT, hold-out 2025-26 bloccato.

    uv run python scripts/analysis/tp01_lowfreq.py
"""
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 numpy as np
import pandas as pd
from src.data.downloader import load_data
from src.strategies.trend_portfolio import TrendPortfolio, simple_returns, CANONICAL

HOLDOUT = pd.Timestamp("2025-01-01", tz="UTC")


def resample_tf(df_1h, rule):
    g = df_1h.copy()
    g.index = pd.to_datetime(g["timestamp"], unit="ms", utc=True)
    out = g.resample(rule, label="left", closed="left").agg(
        {"open": "first", "high": "max", "low": "min", "close": "last", "volume": "sum"}).dropna(subset=["open"])
    out["datetime"] = out.index
    return out.reset_index(drop=True)


def sleeve_net(df, tp):
    """Per-barra netto di uno sleeve: posizione decisa a close[i-1], tenuta in i (causale, no ffill)."""
    r = simple_returns(df["close"].values.astype(float))
    tgt = tp.target_series(df)
    held = np.zeros(len(tgt)); held[1:] = tgt[:-1]
    net = held * r - tp.fee_side * np.abs(np.diff(held, prepend=0.0)); net[0] = 0.0
    return np.clip(net, -0.99, None)


def causality_ok(df, tp, k=10):
    """Ricalcola target_series su prefissi e verifica che tgt[i] non cambi (no look-ahead)."""
    full = tp.target_series(df); n = len(df)
    rng = np.random.default_rng(0); bad = 0
    for i in rng.integers(int(n * 0.6), n - 1, size=k):
        p = tp.target_series(df.iloc[:i + 1].copy())
        if len(p) != i + 1 or not np.isclose(np.nan_to_num(p[i]), np.nan_to_num(full[i]), atol=1e-9):
            bad += 1
    return bad


def met(rr, idx):
    rr = rr[np.isfinite(rr)]
    if len(rr) < 2 or np.std(rr) == 0:
        return dict(sh=0, ret=0, dd=0, n=len(rr))
    bpy = 86400 * 365.25 / pd.Series(idx).diff().dt.total_seconds().median()
    eq = np.cumprod(1 + rr); pk = np.maximum.accumulate(eq)
    return dict(sh=float(np.mean(rr) / np.std(rr) * np.sqrt(bpy)), ret=float(eq[-1] - 1),
                dd=float(np.max((pk - eq) / pk)), n=len(rr))


def main():
    print("=" * 92)
    print("  TP01 RI-VERIFICA BASSA FREQUENZA — calcolo pulito per-TF (no ffill mixed-TF) | fee 0.10% RT")
    print("=" * 92)
    tp = TrendPortfolio(**CANONICAL)
    print(f"  {'TF':<5s}{'leak':>6s}{'FULL Sh':>9s}{'FULL ret':>10s}{'FULL DD':>9s}{'HOLD Sh':>9s}{'HOLD ret':>10s}{'HOLD DD':>9s}")
    for tf, rule in [("4h", "4h"), ("6h", "6h"), ("12h", "12h"), ("1d", "1D")]:
        series = {}; leak = 0
        for a in ("BTC", "ETH"):
            df = resample_tf(load_data(a, "1h"), rule)
            leak += causality_ok(df, tp)
            series[a] = pd.Series(sleeve_net(df, tp), index=pd.to_datetime(df["datetime"]))
        J = pd.concat(series, axis=1, join="inner").fillna(0.0)
        combo = 0.5 * J["BTC"].values + 0.5 * J["ETH"].values
        idx = J.index; ho = idx >= HOLDOUT
        f = met(combo, idx); h = met(combo[ho], idx[ho])
        print(f"  {tf:<5s}{leak:>6d}{f['sh']:>9.2f}{f['ret']*100:>+9.0f}%{f['dd']*100:>8.1f}%"
              f"{h['sh']:>9.2f}{h['ret']*100:>+9.1f}%{h['dd']*100:>8.1f}%")

    # buy&hold 50/50 a 1d come riferimento hold-out
    bh = {}
    for a in ("BTC", "ETH"):
        df = resample_tf(load_data(a, "1h"), "1D")
        bh[a] = pd.Series(simple_returns(df["close"].values.astype(float)), index=pd.to_datetime(df["datetime"]))
    Jb = pd.concat(bh, axis=1, join="inner").fillna(0.0)
    cb = 0.5 * Jb["BTC"].values + 0.5 * Jb["ETH"].values; ix = Jb.index; ho = ix >= HOLDOUT
    bhf = met(cb, ix); bhh = met(cb[ho], ix[ho])
    print(f"\n  buy&hold 50/50 (1d): FULL Sh {bhf['sh']:.2f} ret {bhf['ret']*100:+.0f}% DD {bhf['dd']*100:.0f}%"
          f" | HOLD-OUT Sh {bhh['sh']:.2f} ret {bhh['ret']*100:+.0f}% DD {bhh['dd']*100:.0f}%")
    print("\n  (leak=0 = nessun look-ahead nel calcolo per-TF. Confronta con la tesi: >=12h trustworthy.)")


if __name__ == "__main__":
    main()
