"""S3-01: Squeeze Migliorato — test per-anno, dati reali.
Miglioramenti rispetto al squeeze base:
1. Cross-asset: squeeze su BTC + ETH contemporaneo = segnale più forte
2. Timing orario: accuracy per fascia oraria
3. Squeeze duration weighted: squeeze lunghi → breakout più forti
4. Dual-timeframe: squeeze su 1h confermato da 15m
5. Anti-fakeout: skip se candela post-breakout ritraccia >50%
6. Dynamic exit: trailing stop basato su ATR
"""
from __future__ import annotations
import sys
sys.path.insert(0, ".")

import numpy as np
import pandas as pd
from src.data.downloader import load_data

FEE_RT = 0.002
INITIAL = 1000
LEVERAGE = 3


def keltner_ratio(close, high, low, window=14):
    n = len(close)
    r = np.full(n, np.nan)
    for i in range(window, n):
        wc, wh, wl = close[i-window:i], high[i-window:i], low[i-window:i]
        ma = np.mean(wc)
        bb_std = np.std(wc)
        tr = np.maximum(wh-wl, np.maximum(np.abs(wh-np.roll(wc,1)), np.abs(wl-np.roll(wc,1))))
        atr = np.mean(tr[1:])
        kc = (ma+1.5*atr)-(ma-1.5*atr)
        bb = (ma+2*bb_std)-(ma-2*bb_std)
        if kc > 0:
            r[i] = bb/kc
    return r


def atr_calc(high, low, close, period=14):
    tr = np.maximum(high-low, np.maximum(np.abs(high-np.roll(close,1)), np.abs(low-np.roll(close,1))))
    tr[0] = high[0]-low[0]
    r = np.full(len(close), np.nan)
    r[period-1] = np.mean(tr[:period])
    k = 2/(period+1)
    for i in range(period, len(close)):
        r[i] = tr[i]*k + r[i-1]*(1-k)
    return r


def detect_squeezes(close, high, low, volume, kcr, sq_thr=0.8, min_dur=5):
    """Ritorna lista di squeeze events con metadata."""
    events = []
    in_sq = False
    sq_start = 0
    n = len(close)

    for i in range(1, n):
        if np.isnan(kcr[i]):
            continue
        is_sq = kcr[i] < sq_thr
        if is_sq and not in_sq:
            in_sq = True
            sq_start = i
        elif not is_sq and in_sq:
            in_sq = False
            dur = i - sq_start
            if dur < min_dur:
                continue
            avg_vol = np.mean(volume[sq_start:i])
            # Range durante squeeze
            sq_range = (np.max(high[sq_start:i]) - np.min(low[sq_start:i])) / close[sq_start] if close[sq_start] > 0 else 0
            events.append({
                "release_idx": i,
                "duration": dur,
                "avg_vol": avg_vol,
                "squeeze_range": sq_range,
                "kcr_at_release": kcr[i],
            })
    return events


def run_improved_squeeze(primary_asset, tf="1h"):
    # Carica asset primario
    df = load_data(primary_asset, tf)
    c, h, l, v = df["close"].values, df["high"].values, df["low"].values, df["volume"].values
    n = len(df)
    ts = pd.to_datetime(df["timestamp"], unit="ms", utc=True)
    ts_ms = df["timestamp"].values

    kcr = keltner_ratio(c, h, l, 14)
    atr_14 = atr_calc(h, l, c, 14)
    events = detect_squeezes(c, h, l, v, kcr)

    # Carica asset secondario per cross-check
    secondary = "BTC" if primary_asset == "ETH" else "ETH"
    df2 = load_data(secondary, tf)
    c2, h2, l2 = df2["close"].values, df2["high"].values, df2["low"].values
    ts2_ms = df2["timestamp"].values
    kcr2 = keltner_ratio(c2, h2, l2, 14)

    # Mappa ts2 → indici per allineare
    def find_idx2(ts_val):
        idx = np.searchsorted(ts2_ms, ts_val)
        return min(idx, len(c2)-1)

    # Carica 15m per dual-TF
    if tf == "1h":
        df_15m = load_data(primary_asset, "15m")
        c15 = df_15m["close"].values
        h15 = df_15m["high"].values
        l15 = df_15m["low"].values
        ts15 = df_15m["timestamp"].values
        kcr_15m = keltner_ratio(c15, h15, l15, 14)
    else:
        kcr_15m = None
        ts15 = None

    # ================================================================
    #  CONFIGURAZIONI
    # ================================================================
    configs = [
        # (name, use_cross, use_timing, use_duration, use_dual_tf, use_antifake, use_trailing, hold, stop_atr)
        ("BASE",                False, False, False, False, False, False, 3, 0),
        ("cross_asset",         True,  False, False, False, False, False, 3, 0),
        ("timing_filter",       False, True,  False, False, False, False, 3, 0),
        ("long_squeeze",        False, False, True,  False, False, False, 3, 0),
        ("dual_tf",             False, False, False, True,  False, False, 3, 0),
        ("anti_fakeout",        False, False, False, False, True,  False, 3, 0),
        ("trailing_stop",       False, False, False, False, False, True,  6, 1.5),
        ("cross+timing",        True,  True,  False, False, False, False, 3, 0),
        ("cross+long+timing",   True,  True,  True,  False, False, False, 3, 0),
        ("cross+dual_tf",       True,  False, False, True,  False, False, 3, 0),
        ("ALL_FILTERS",         True,  True,  True,  True,  True,  False, 3, 0),
        ("ALL+trailing",        True,  True,  True,  True,  True,  True,  6, 1.5),
        ("cross+antifake",      True,  False, False, False, True,  False, 3, 0),
        ("timing+antifake",     False, True,  False, False, True,  False, 3, 0),
        ("cross+timing+antifk", True,  True,  False, False, True,  False, 3, 0),
        ("cross+timing+trail",  True,  True,  False, False, False, True,  6, 1.5),
    ]

    print(f"\n{'#'*75}")
    print(f"  {primary_asset} {tf} — SQUEEZE MIGLIORATO")
    print(f"{'#'*75}")

    results = []

    for name, f_cross, f_timing, f_dur, f_dual, f_antifake, f_trail, hold, stop_atr_m in configs:
        yearly = {}
        capital = float(INITIAL)
        peak = capital
        max_dd = 0

        for ev in events:
            i = ev["release_idx"]
            if i + hold + 2 >= n:
                continue

            # --- FILTRI ---
            skip = False

            # Cross-asset: secondary deve anche essere in squeeze recente o breakout
            if f_cross:
                i2 = find_idx2(ts_ms[i])
                if i2 >= 5:
                    sec_in_squeeze = any(not np.isnan(kcr2[j]) and kcr2[j] < 0.85 for j in range(max(0,i2-10), i2+1))
                    if not sec_in_squeeze:
                        skip = True

            # Timing: solo certe ore (testato: 6-14 UTC migliori)
            if f_timing:
                hour = ts.iloc[i].hour
                if hour < 4 or hour > 16:
                    skip = True

            # Duration: solo squeeze > 10 barre
            if f_dur:
                if ev["duration"] < 10:
                    skip = True

            # Dual-TF: squeeze anche su 15m
            if f_dual and kcr_15m is not None and ts15 is not None:
                i15 = np.searchsorted(ts15, ts_ms[i])
                if i15 >= 5:
                    sq_15m = any(not np.isnan(kcr_15m[j]) and kcr_15m[j] < 0.85 for j in range(max(0,i15-20), i15+1))
                    if not sq_15m:
                        skip = True

            # Anti-fakeout: prima candela post-breakout non deve ritracciare >50%
            if f_antifake and i + 1 < n:
                breakout_bar_range = h[i] - l[i]
                if breakout_bar_range > 0:
                    if c[i] > c[i-1]:  # breakout up
                        retrace = (h[i] - c[i]) / breakout_bar_range
                    else:  # breakout down
                        retrace = (c[i] - l[i]) / breakout_bar_range
                    if retrace > 0.6:
                        skip = True

            if skip:
                continue

            # --- DIREZIONE ---
            first_ret = (c[i] - c[i-1]) / c[i-1]
            if abs(first_ret) < 0.001:
                continue
            direction = 1 if first_ret > 0 else -1

            # --- EXIT ---
            entry = c[i-1]
            if f_trail and not np.isnan(atr_14[i]):
                # Trailing stop
                trail_dist = atr_14[i] * stop_atr_m
                best_price = entry
                exit_price = c[min(i+hold, n-1)]
                for j in range(i, min(i+hold+1, n)):
                    if direction == 1:
                        best_price = max(best_price, h[j])
                        if l[j] <= best_price - trail_dist:
                            exit_price = best_price - trail_dist
                            break
                    else:
                        best_price = min(best_price, l[j])
                        if h[j] >= best_price + trail_dist:
                            exit_price = best_price + trail_dist
                            break
                    exit_price = c[j]
            else:
                exit_price = c[min(i+hold-1, n-1)]

            actual = (exit_price - entry) / entry * direction
            net = actual * LEVERAGE - FEE_RT * LEVERAGE

            capital += capital * 0.15 * net
            capital = max(capital, 10)
            if capital > peak: peak = capital
            dd = (peak - capital) / peak
            max_dd = max(max_dd, dd)

            year = ts.iloc[i].year
            if year not in yearly:
                yearly[year] = {"wins": 0, "total": 0, "pnls": []}
            yearly[year]["total"] += 1
            if actual > 0:
                yearly[year]["wins"] += 1
            yearly[year]["pnls"].append(net * INITIAL)

        all_t = sum(d["total"] for d in yearly.values())
        all_w = sum(d["wins"] for d in yearly.values())
        if all_t < 30:
            continue

        acc = all_w / all_t * 100
        all_pnls = [p for d in yearly.values() for p in d["pnls"]]
        tot_pnl = sum(all_pnls)

        # Worst year
        worst_y_acc = 100
        worst_y = ""
        for y, d in yearly.items():
            ya = d["wins"]/d["total"]*100 if d["total"] > 0 else 0
            if ya < worst_y_acc:
                worst_y_acc = ya
                worst_y = str(y)

        results.append({
            "name": name, "trades": all_t, "acc": acc, "pnl": tot_pnl,
            "max_dd": max_dd*100, "capital": capital,
            "worst": f"{worst_y}({worst_y_acc:.0f}%)",
            "yearly": yearly,
        })

    # Sort by accuracy
    results.sort(key=lambda x: x["acc"], reverse=True)

    print(f"\n  {'Name':.<26s} {'Trades':>7s} {'Acc':>6s} {'PnL€':>9s} {'DD%':>6s} {'Capital':>10s} {'Worst':>12s}")
    print(f"  {'-'*80}")
    for r in results:
        tag = "✅✅" if r["acc"] >= 80 else "✅" if r["acc"] >= 76 else ""
        print(f"  {r['name']:.<26s} {r['trades']:>7d} {r['acc']:>5.1f}% €{r['pnl']:>+8.0f} {r['max_dd']:>5.1f}% €{r['capital']:>9,.0f} {r['worst']:>12s} {tag}")

    # Dettaglio per anno del migliore
    if results:
        best = results[0]
        print(f"\n  MIGLIORE: {best['name']} → {best['acc']:.1f}% acc")
        print(f"  {'Anno':>6s} {'Trades':>7s} {'Acc':>6s} {'PnL€':>9s}")
        for y in sorted(best["yearly"]):
            d = best["yearly"][y]
            ya = d["wins"]/d["total"]*100 if d["total"] > 0 else 0
            yp = sum(d["pnls"])
            tag = " ← CRASH" if y in [2020,2021,2022] else ""
            print(f"  {y:>6d} {d['total']:>7d} {ya:>5.1f}% €{yp:>+8.0f}{tag}")

    return results


# Run su entrambi gli asset e timeframe
all_results = {}
for asset in ["ETH", "BTC"]:
    for tf in ["1h", "15m"]:
        key = f"{asset}_{tf}"
        all_results[key] = run_improved_squeeze(asset, tf)

# Classifica globale
print(f"\n\n{'='*75}")
print(f"  CLASSIFICA GLOBALE — TOP 15")
print(f"{'='*75}")

global_list = []
for key, results in all_results.items():
    for r in results:
        global_list.append({**r, "asset_tf": key})

global_list.sort(key=lambda x: x["acc"], reverse=True)
print(f"\n  {'Asset_TF':.<12s} {'Name':.<26s} {'Trades':>6s} {'Acc':>6s} {'PnL€':>9s} {'DD%':>5s} {'Worst':>12s}")
for r in global_list[:15]:
    tag = "✅✅" if r["acc"] >= 80 else "✅" if r["acc"] >= 76 else ""
    print(f"  {r['asset_tf']:.<12s} {r['name']:.<26s} {r['trades']:>6d} {r['acc']:>5.1f}% €{r['pnl']:>+8.0f} {r['max_dd']:>4.1f}% {r['worst']:>12s} {tag}")
