"""Strategia #3 candidata: time-series momentum / trend (TSMOM).
Posizione continua decisa a close[i] dai dati passati; fee SOLO sui cambi di
posizione (poche operazioni su TF alto = fee non letali). Niente look-ahead:
il rendimento del bar i->i+1 usa la direzione decisa a close[i].
"""
from __future__ import annotations

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.honest_lab import ema, get_df, available_assets, FEE_RT  # noqa: E402

LEV = 3.0
POS = 0.15


def simulate_position(sig: np.ndarray, df: pd.DataFrame, fee_rt: float = FEE_RT,
                      lev: float = LEV, pos: float = POS) -> dict:
    """sig[i] in {-1,0,1} = direzione tenuta nel bar i->i+1, decisa a close[i].
    Fee one-way = fee_rt/2 su ogni unita' di variazione posizione."""
    c = df["close"].values
    n = len(c)
    ts = pd.to_datetime(df["timestamp"], unit="ms", utc=True)
    cap = peak = 1000.0
    max_dd = 0.0
    cur = 0.0
    flips = 0
    bars_in = 0
    yearly: dict[int, float] = {}
    for i in range(n - 1):
        s = sig[i]
        if not np.isfinite(s):
            s = 0.0
        if s != cur:
            cap -= cap * pos * (fee_rt / 2) * lev * abs(s - cur)
            flips += abs(s - cur) > 0
            cur = s
        pr = (c[i + 1] - c[i]) / c[i]
        bar_ret = pos * lev * pr * cur
        cap = max(cap * (1 + bar_ret), 10.0)
        peak = max(peak, cap); max_dd = max(max_dd, (peak - cap) / peak)
        if cur != 0:
            bars_in += 1
        yr = ts.iloc[i].year
        yearly[yr] = yearly.get(yr, 0.0) + bar_ret * 100
    return {
        "ret": (cap / 1000 - 1) * 100,
        "dd": max_dd * 100,
        "flips": flips,
        "exposure": bars_in / n * 100,
        "yearly": yearly,
        "pos_years": sum(1 for v in yearly.values() if v > 0),
        "n_years": len(yearly),
    }


def tsmom_signal(df, lookback=30, long_only=False):
    """+1 se close>close[-lookback], -1 (o 0 se long_only) altrimenti."""
    c = df["close"].values
    sig = np.zeros(len(c))
    for i in range(lookback, len(c)):
        up = c[i] > c[i - lookback]
        sig[i] = 1.0 if up else (0.0 if long_only else -1.0)
    return sig


def ema_dual_signal(df, fast=20, slow=100, long_only=False):
    """+1 se EMA_fast>EMA_slow."""
    c = df["close"].values
    ef, es = ema(c, fast), ema(c, slow)
    sig = np.where(ef > es, 1.0, 0.0 if long_only else -1.0)
    sig[:slow] = 0.0
    return sig


def oos(sig, df, frac=0.30):
    split = int(len(df) * (1 - frac))
    s2 = sig.copy(); s2[:split] = 0.0
    return simulate_position(s2, df)


def show(label, df, sig):
    full = simulate_position(sig, df)
    o = oos(sig, df)
    anni = f"{full['pos_years']}/{full['n_years']}"
    print(f"  {label:<26s}{full['flips']:>6d}{full['ret']:>+9.0f}{o['ret']:>+9.0f}"
          f"{full['dd']:>6.0f}{full['exposure']:>6.0f}{anni:>8s}")
    return full, o


if __name__ == "__main__":
    assets = available_assets()
    print(f"TSMOM / trend — fee {FEE_RT*100:.2f}% RT, leva3x pos15% | OOS30%")
    for tf in ["1d", "4h"]:
        print(f"\n  ###### TF {tf} ######")
        for a in assets:
            df = get_df(a, tf)
            print(f"\n  === {a} {tf} ===  {'Flip':>5s}{'FULL%':>8s}{'OOS%':>8s}{'DD%':>6s}{'Exp%':>6s}{'AnniP':>8s}")
            show("TSMOM lb30 long/short", df, tsmom_signal(df, 30))
            show("TSMOM lb30 long-only", df, tsmom_signal(df, 30, long_only=True))
            show("TSMOM lb90 long/short", df, tsmom_signal(df, 90))
            show("EMA 20/100 long/short", df, ema_dual_signal(df, 20, 100))
            show("EMA 20/100 long-only", df, ema_dual_signal(df, 20, 100, long_only=True))
