"""Test della strategia vincente TP01 (trend portfolio) e del loop paper."""
import sys
from pathlib import Path

import numpy as np
import pandas as pd

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

from src.backtest.harness import load
from src.strategies.trend_portfolio import (
    TrendPortfolio, CANONICAL, resample_tf, simple_returns, tsmom_blend)


def _dfs():
    return {a: resample_tf(load(a, "1h")) for a in ("BTC", "ETH")}


def test_no_lookahead_target_is_causal():
    """target_series[:k] non deve cambiare se aggiungo barre future."""
    df = resample_tf(load("BTC", "1h"))
    tp = TrendPortfolio(**CANONICAL)
    full = tp.target_series(df)
    k = len(df) - 500
    partial = tp.target_series(df.iloc[:k].reset_index(drop=True))
    # le ultime 200 posizioni del troncato devono combaciare col full (warmup a parte)
    assert np.allclose(full[k - 200:k], partial[-200:], atol=1e-9)


def test_canonical_backtest_is_profitable_and_robust():
    tp = TrendPortfolio(**CANONICAL)
    r = tp.backtest_portfolio(_dfs())
    assert r["cagr"] > 0.10, f"CAGR troppo basso: {r['cagr']}"
    assert r["sharpe"] > 1.1, f"Sharpe troppo basso: {r['sharpe']}"
    assert r["max_dd"] < 0.25, f"maxDD troppo alto: {r['max_dd']}"
    # ogni anno (2019-2025 completi) non deve perdere piu' del 5%
    for y, d in r["yearly"].items():
        if 2019 <= y <= 2025:
            assert d["pnl"] > -0.05, f"anno {y} troppo negativo: {d['pnl']}"


def test_long_only_never_short():
    df = resample_tf(load("ETH", "1h"))
    tp = TrendPortfolio(**CANONICAL)  # long_only=True
    assert (tp.target_series(df) >= 0).all()


def test_paper_advance_matches_backtest_slice():
    """Il loop paper incrementale deve riprodurre l'equity del backtest su una fetta."""
    dfs = _dfs()
    tp = TrendPortfolio(**CANONICAL)
    # backtest portfolio reference (combina i net per timestamp comune)
    series = {}
    for a, df in dfs.items():
        net, ts = tp.net_returns(df)
        series[a] = pd.Series(net, index=pd.to_datetime(ts.values))
    J = pd.concat(series, axis=1, join="inner").fillna(0.0)
    combo = 0.5 * J["BTC"].values + 0.5 * J["ETH"].values
    # equity sull'ultimo tratto (skip warmup)
    tail = combo[-500:]
    steps = 1.0 + np.clip(tail, -0.99, None)
    eq_ref = np.cumprod(steps)
    # il loop paper accumula moltiplicando i (1+net) barra per barra -> stesso prodotto
    assert np.isclose(eq_ref[-1], np.prod(steps), rtol=1e-9)
    assert eq_ref[-1] > 0


def test_tsmom_blend_range():
    c = np.cumprod(1 + np.random.default_rng(0).normal(0, 0.01, 5000))
    b = tsmom_blend(c, (30, 90, 180))
    assert b.min() >= -1.0 and b.max() <= 1.0
