"""
Optimisation des Stop-Loss et Take-Profit pour la stratégie INVERSE
Test systématique de différents niveaux de SL et ratios TP/SL
"""

import logging
import pandas as pd
import numpy as np
from apollo_client import ApolloDegenClient
from strategies.inverse_strategy import InverseStrategy, MarketRegimeDetector
from signals.short_term_signals import ShortTermSignalGenerator
import config

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Désactiver les logs verbeux des autres modules
logging.getLogger('InverseStrategy').setLevel(logging.WARNING)
logging.getLogger('ShortTermSignalGenerator').setLevel(logging.WARNING)
logging.getLogger('ApolloDegenClient').setLevel(logging.WARNING)

class StopLossOptimizer:
    """Optimise les niveaux de SL et ratios TP/SL"""

    def __init__(self, initial_capital: float = 1000):
        self.initial_capital = initial_capital
        self.inverse_strategy = InverseStrategy()

    def backtest_with_sl(self, df: pd.DataFrame, symbol: str, leverage: int,
                         sl_pct: float, tp_ratio: float):
        """
        Backtest avec un SL et ratio TP/SL spécifique

        Args:
            df: DataFrame des klines
            symbol: Symbole tradé
            leverage: Leverage utilisé
            sl_pct: Stop-loss en % (ex: 0.005 = 0.5%)
            tp_ratio: Ratio TP/SL (ex: 2.0 = TP à 2x le SL)
        """
        capital = self.initial_capital
        trades = []

        # Position sizing selon leverage
        if leverage >= 200:
            risk_pct = 0.02  # 2%
        elif leverage >= 150:
            risk_pct = 0.015  # 1.5%
        elif leverage >= 100:
            risk_pct = 0.012  # 1.2%
        else:
            risk_pct = 0.01  # 1%

        position_size_usd = self.initial_capital * risk_pct * leverage

        i = 50  # Warmup

        while i < len(df) - 100:
            window = df.iloc[:i+1].copy()

            # Générer signal inverse
            signal = self.inverse_strategy.generate_inverse_signal(window, symbol)

            if not signal:
                i += 1
                continue

            # Calculer SL et TP avec les paramètres testés
            entry_price = signal.entry_price

            if signal.side == 'LONG':
                sl = entry_price * (1 - sl_pct)
                tp = entry_price * (1 + sl_pct * tp_ratio)
            else:  # SHORT
                sl = entry_price * (1 + sl_pct)
                tp = entry_price * (1 - sl_pct * tp_ratio)

            quantity = position_size_usd / entry_price
            entry_time = df.iloc[i]['timestamp']

            # Chercher sortie
            for j in range(i + 1, min(i + 100, len(df))):
                current = df.iloc[j]
                high = current['high']
                low = current['low']

                exit_price = None
                exit_reason = None

                if signal.side == 'LONG':
                    if low <= sl:
                        exit_price = sl
                        exit_reason = 'SL'
                    elif high >= tp:
                        exit_price = tp
                        exit_reason = 'TP'
                else:  # SHORT
                    if high >= sl:
                        exit_price = sl
                        exit_reason = 'SL'
                    elif low <= tp:
                        exit_price = tp
                        exit_reason = 'TP'

                if exit_price:
                    # Calculer PnL
                    if signal.side == 'LONG':
                        pnl_pct = (exit_price - entry_price) / entry_price
                    else:
                        pnl_pct = (entry_price - exit_price) / entry_price

                    pnl_usd = position_size_usd * pnl_pct
                    pnl_usd -= position_size_usd * config.TRADING_FEE_TAKER * 2  # Entry + exit

                    capital += pnl_usd

                    trades.append({
                        'entry_time': entry_time,
                        'exit_time': current['timestamp'],
                        'side': signal.side,
                        'pnl_pct': pnl_pct * 100,
                        'pnl_usd': pnl_usd,
                        'exit_reason': exit_reason,
                        'sl_pct': sl_pct * 100,
                        'tp_ratio': tp_ratio
                    })

                    i = j + 1
                    break
            else:
                i += 1

        if not trades:
            return None

        winning = [t for t in trades if t['pnl_usd'] > 0]
        losing = [t for t in trades if t['pnl_usd'] <= 0]

        total_return = (capital - self.initial_capital) / self.initial_capital * 100
        win_rate = len(winning) / len(trades) if trades else 0

        # Calculer profit factor
        total_wins = sum(t['pnl_usd'] for t in winning) if winning else 0
        total_losses = abs(sum(t['pnl_usd'] for t in losing)) if losing else 1
        profit_factor = total_wins / total_losses if total_losses > 0 else 0

        return {
            'total_return': total_return,
            'win_rate': win_rate,
            'profit_factor': profit_factor,
            'trades': len(trades),
            'wins': len(winning),
            'losses': len(losing),
            'avg_win': np.mean([t['pnl_pct'] for t in winning]) if winning else 0,
            'avg_loss': np.mean([t['pnl_pct'] for t in losing]) if losing else 0,
            'final_capital': capital
        }


if __name__ == '__main__':
    logger.info("🎯 OPTIMISATION STOP-LOSS & TAKE-PROFIT\n")
    logger.info("="*80)

    # Récupérer données BTC 1m (meilleur timeframe trouvé)
    client = ApolloDegenClient()
    df = client.get_klines('BTCUSDT', '1m', limit=1500)

    logger.info(f"Données: {len(df)} chandeliers BTC 1m\n")

    # Leverages à tester (les meilleurs identifiés)
    leverages = [200]  # Focus sur le meilleur

    # Stop-loss à tester (en %) - Focus autour de 0.5%
    sl_levels = [0.003, 0.004, 0.005, 0.006, 0.007, 0.008]  # 0.3% à 0.8%

    # Ratios TP/SL à tester - Focus sur valeurs réalistes
    tp_ratios = [1.5, 2.0, 2.5, 3.0]

    results = []
    best_result = None
    best_config = None

    total_tests = len(leverages) * len(sl_levels) * len(tp_ratios)
    current_test = 0

    logger.info(f"Tests à effectuer: {total_tests}\n")

    for leverage in leverages:
        logger.info(f"\n{'='*80}")
        logger.info(f"📊 LEVERAGE {leverage}x")
        logger.info(f"{'='*80}\n")

        for sl_pct in sl_levels:
            logger.info(f"  SL: {sl_pct*100:.1f}%")

            for tp_ratio in tp_ratios:
                current_test += 1

                try:
                    optimizer = StopLossOptimizer(initial_capital=1000)
                    result = optimizer.backtest_with_sl(
                        df, 'BTCUSDT', leverage, sl_pct, tp_ratio
                    )

                    if result and result['total_return'] > 0:
                        logger.info(
                            f"    [{current_test}/{total_tests}] ✅ TP/SL={tp_ratio:.1f}:1 → "
                            f"{result['total_return']:+.2f}% | WR: {result['win_rate']*100:.0f}% | "
                            f"PF: {result['profit_factor']:.2f} | Trades: {result['trades']}"
                        )

                        result['leverage'] = leverage
                        result['sl_pct'] = sl_pct
                        result['tp_ratio'] = tp_ratio
                        results.append(result)

                        if best_result is None or result['total_return'] > best_result['total_return']:
                            best_result = result
                            best_config = {
                                'leverage': leverage,
                                'sl_pct': sl_pct,
                                'tp_ratio': tp_ratio
                            }
                    else:
                        logger.debug(
                            f"    [{current_test}/{total_tests}] ❌ TP/SL={tp_ratio:.1f}:1 → "
                            f"{result['total_return'] if result else 'N/A'}"
                        )

                except Exception as e:
                    logger.error(f"    [{current_test}/{total_tests}] ⚠️  Erreur: {e}")

    # Afficher résultats
    logger.info(f"\n\n{'='*80}")
    logger.info("📊 RÉSULTATS - Top 10 Configurations")
    logger.info(f"{'='*80}\n")

    if results:
        # Trier par return
        sorted_results = sorted(results, key=lambda x: x['total_return'], reverse=True)[:10]

        logger.info(f"{'Rank':<5} {'Lev':<6} {'SL%':<8} {'TP/SL':<8} {'Return%':<10} {'WR%':<8} {'PF':<8} {'Trades':<8}")
        logger.info("-" * 80)

        for i, r in enumerate(sorted_results, 1):
            logger.info(
                f"{i:<5} {r['leverage']:<6}x {r['sl_pct']*100:<8.2f} "
                f"{r['tp_ratio']:<8.1f} {r['total_return']:<10.2f} "
                f"{r['win_rate']*100:<8.0f} {r['profit_factor']:<8.2f} {r['trades']:<8}"
            )

    # Meilleure configuration
    if best_result:
        logger.info(f"\n\n{'='*80}")
        logger.info("🏆 MEILLEURE CONFIGURATION")
        logger.info(f"{'='*80}")
        logger.info(f"Leverage:      {best_config['leverage']}x")
        logger.info(f"Stop-Loss:     {best_config['sl_pct']*100:.2f}%")
        logger.info(f"TP/SL Ratio:   {best_config['tp_ratio']:.1f}:1")
        logger.info(f"Take-Profit:   {best_config['sl_pct']*100*best_config['tp_ratio']:.2f}%")
        logger.info(f"Return:        {best_result['total_return']:+.2f}%")
        logger.info(f"Win Rate:      {best_result['win_rate']*100:.1f}%")
        logger.info(f"Profit Factor: {best_result['profit_factor']:.2f}")
        logger.info(f"Total Trades:  {best_result['trades']}")
        logger.info(f"Wins/Losses:   {best_result['wins']}/{best_result['losses']}")
        logger.info(f"Avg Win:       {best_result['avg_win']:+.2f}%")
        logger.info(f"Avg Loss:      {best_result['avg_loss']:+.2f}%")
        logger.info(f"{'='*80}\n")

        # Suggestion de mise à jour config
        logger.info("💡 Pour appliquer cette config, modifier config.py:")
        logger.info(f"   STOP_LOSS_PCT[{best_config['leverage']}] = {best_config['sl_pct']}")
        logger.info(f"   TAKE_PROFIT_RATIO = {best_config['tp_ratio']}")

    else:
        logger.warning("Aucune configuration profitable trouvée")
