"""Strategia 3: Fourier decomposition e proiezione.
Ispirata al paper Pythagoras Trading Prediction.
Idea: scomponi il prezzo in componenti sinusoidali via FFT,
ricostruisci con le N componenti più forti, proietta nel futuro.
"""
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
from src.backtest.engine import run_backtest

print("=" * 60)
print("  STRATEGIA 3: FOURIER PROJECTION — BTC 1H")
print("=" * 60)

df = load_data("BTC", "1h")
close = df["close"].values
n_total = len(close)

WINDOW = 588  # dal paper: 588 candele per l'indicatore H-C
N_COMPONENTS = 25  # dal paper: 25 linee verticali
LOOKAHEAD = 6
STEP = 6

split_idx = int(n_total * 0.7)

def fourier_project(series: np.ndarray, n_components: int, ahead: int) -> np.ndarray:
    """Ricostruisci serie con top-N componenti Fourier e proietta avanti."""
    n = len(series)
    detrended = series - np.linspace(series[0], series[-1], n)
    fft_vals = np.fft.fft(detrended)
    freqs = np.fft.fftfreq(n)

    magnitudes = np.abs(fft_vals)
    magnitudes[0] = 0
    top_indices = np.argsort(magnitudes)[-n_components * 2:]

    fft_filtered = np.zeros_like(fft_vals)
    fft_filtered[top_indices] = fft_vals[top_indices]

    t_extended = np.arange(n + ahead)
    reconstruction = np.zeros(n + ahead)
    for idx in top_indices:
        amp = np.abs(fft_vals[idx]) / n
        phase = np.angle(fft_vals[idx])
        freq = freqs[idx]
        reconstruction += amp * np.cos(2 * np.pi * freq * t_extended / 1 + phase)

    trend_slope = (series[-1] - series[0]) / n
    trend_extended = series[0] + trend_slope * t_extended
    reconstruction += trend_extended

    return reconstruction


print(f"\nParametri: window={WINDOW}, components={N_COMPONENTS}, lookahead={LOOKAHEAD}")
print(f"Train: 0→{split_idx}, Test: {split_idx}→{n_total}")

signals = pd.Series(0, index=df.index)
accuracies = []

test_range = range(max(split_idx, WINDOW), n_total - LOOKAHEAD, STEP)
total_steps = len(list(test_range))
print(f"Valutazione: {total_steps} punti (step={STEP})...")

for count, i in enumerate(test_range):
    if count % 500 == 0:
        print(f"  Progresso: {count}/{total_steps} ({count/total_steps*100:.0f}%)")

    window_data = close[i - WINDOW : i]
    projected = fourier_project(window_data, N_COMPONENTS, LOOKAHEAD)

    current_price = close[i - 1]
    projected_price = projected[-1]
    change_pct = (projected_price - current_price) / current_price

    if change_pct > 0.005:
        signals.iloc[i] = 1
    elif change_pct < -0.005:
        signals.iloc[i] = -1

    actual_ret = (close[i + LOOKAHEAD - 1] - current_price) / current_price
    if signals.iloc[i] == 1:
        accuracies.append(1 if actual_ret > 0 else 0)
    elif signals.iloc[i] == -1:
        accuracies.append(1 if actual_ret < 0 else 0)

print(f"\nSegnali generati: {(signals != 0).sum()}")
print(f"  Long: {(signals == 1).sum()}, Short: {(signals == -1).sum()}")

if accuracies:
    print(f"Accuratezza direzione: {np.mean(accuracies)*100:.1f}% su {len(accuracies)} segnali")

test_df = df.iloc[split_idx:].reset_index(drop=True)
test_signals = signals.iloc[split_idx:].reset_index(drop=True)

result = run_backtest(test_df, test_signals, initial_capital=1000, fee_pct=0.001, max_hold_candles=LOOKAHEAD)

print("\nRISULTATI TEST:")
for k, v in result.summary().items():
    print(f"  {k}: {v}")

# Varianti con parametri diversi
print("\n\n--- VARIANTI PARAMETRI ---")
for n_comp in [5, 10, 15, 25, 50]:
    for window in [144, 288, 588]:
        sigs = pd.Series(0, index=df.index)
        accs = []
        test_r = range(max(split_idx, window), n_total - LOOKAHEAD, STEP)
        for i in test_r:
            w = close[i - window : i]
            proj = fourier_project(w, n_comp, LOOKAHEAD)
            cp = close[i - 1]
            pp = proj[-1]
            ch = (pp - cp) / cp
            if ch > 0.005:
                sigs.iloc[i] = 1
            elif ch < -0.005:
                sigs.iloc[i] = -1
            ar = (close[i + LOOKAHEAD - 1] - cp) / cp
            if sigs.iloc[i] == 1:
                accs.append(1 if ar > 0 else 0)
            elif sigs.iloc[i] == -1:
                accs.append(1 if ar < 0 else 0)

        if not accs:
            continue
        t_sigs = sigs.iloc[split_idx:].reset_index(drop=True)
        res = run_backtest(test_df, t_sigs, initial_capital=1000, fee_pct=0.001, max_hold_candles=LOOKAHEAD)
        acc = np.mean(accs) * 100
        print(f"  W={window:3d} N={n_comp:2d} → acc={acc:.1f}% trades={res.total_trades} ret={res.total_return*100:+.1f}% sharpe={res.sharpe_ratio:.2f}")
