"""
Stratégies INVERSE Optimisées - 6 variantes à tester
"""

import pandas as pd
import numpy as np
from typing import Optional, Tuple
import logging

import sys
sys.path.append('..')
import config
from signals.short_term_signals import Signal, ShortTermSignalGenerator, TechnicalIndicators


class MarketRegimeDetectorAdvanced:
    """Détection RANGE avancée avec ATR, S/R, volume profile"""

    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)

    def detect_regime(self, df: pd.DataFrame, lookback: int = 100) -> str:
        if len(df) < lookback:
            return 'UNKNOWN'

        window = df.iloc[-lookback:]
        closes = window['close'].values

        # 1. Volatilité via ATR
        atr = TechnicalIndicators.atr(window['high'], window['low'], window['close'], period=14)
        avg_atr = atr.iloc[-20:].mean()
        price = closes[-1]
        atr_pct = (avg_atr / price) * 100

        # Range si ATR < 0.3%
        if atr_pct < 0.3:
            # 2. Support/Resistance touches
            period_high = closes.max()
            period_low = closes.min()
            range_size = period_high - period_low

            # Compter touches dans top/bottom 5%
            top_zone = period_high - range_size * 0.05
            bottom_zone = period_low + range_size * 0.05

            touches_top = sum(1 for c in closes[-50:] if c >= top_zone)
            touches_bottom = sum(1 for c in closes[-50:] if c <= bottom_zone)

            # Range confirmé si touches répétées
            if touches_top >= 2 and touches_bottom >= 2:
                return 'RANGE'

        # 3. Détection TREND (logique originale)
        q1 = closes[:lookback//4]
        q2 = closes[lookback//4:lookback//2]
        q3 = closes[lookback//2:3*lookback//4]
        q4 = closes[3*lookback//4:]

        avg_q1, avg_q2, avg_q3, avg_q4 = q1.mean(), q2.mean(), q3.mean(), q4.mean()

        if avg_q2 > avg_q1 and avg_q3 > avg_q2 and avg_q4 > avg_q3:
            return 'UPTREND'
        if avg_q2 < avg_q1 and avg_q3 < avg_q2 and avg_q4 < avg_q3:
            return 'DOWNTREND'

        return 'RANGE'


class InverseStrategyV1_EarlyExit:
    """V1: Exit anticipée sur changement de régime ou signal contraire"""

    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.signal_generator = ShortTermSignalGenerator()
        self.regime_detector = MarketRegimeDetectorAdvanced()
        self.version = "V1_EARLY_EXIT"

    def generate_inverse_signal(self, df: pd.DataFrame, symbol: str) -> Optional[Signal]:
        regime = self.regime_detector.detect_regime(df)
        signals = self.signal_generator.generate_signals(df, symbol)

        if not signals:
            return None

        best_signal = max(signals, key=lambda x: x.score)

        if regime == 'RANGE':
            inverse_side = 'SHORT' if best_signal.side == 'LONG' else 'LONG'
            new_sl = best_signal.take_profit if inverse_side == 'LONG' else best_signal.take_profit
            new_tp = best_signal.stop_loss if inverse_side == 'LONG' else best_signal.stop_loss

            return Signal(
                symbol=symbol,
                side=inverse_side,
                score=best_signal.score,
                strategy=f'INVERSE_V1_{best_signal.strategy}',
                entry_price=best_signal.entry_price,
                stop_loss=new_sl,
                take_profit=new_tp,
                confluences=best_signal.confluences + [f'INVERSE_{regime}'],
                timeframe=best_signal.timeframe,
                timestamp=best_signal.timestamp
            )
        elif regime in ['UPTREND', 'DOWNTREND']:
            return best_signal
        return None

    def should_exit_early(self, df: pd.DataFrame, symbol: str, entry_regime: str,
                          current_side: str) -> Tuple[bool, str]:
        """Vérifie si on doit sortir avant SL/TP"""
        # Régime a changé
        current_regime = self.regime_detector.detect_regime(df)
        if current_regime != entry_regime and current_regime != 'RANGE':
            return True, 'REGIME_CHANGE'

        # Signal contraire fort
        signals = self.signal_generator.generate_signals(df, symbol)
        if signals:
            best = max(signals, key=lambda x: x.score)
            if best.score >= 0.90 and best.side != current_side:
                return True, 'COUNTER_SIGNAL'

        return False, ''


class InverseStrategyV2_MultiTimeframe:
    """V2: Confirmation multi-timeframe (1m + 5m)"""

    def __init__(self, client):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.signal_generator = ShortTermSignalGenerator()
        self.regime_detector = MarketRegimeDetectorAdvanced()
        self.client = client
        self.version = "V2_MULTI_TF"

    def generate_inverse_signal(self, df: pd.DataFrame, symbol: str) -> Optional[Signal]:
        # Régime 1m
        regime_1m = self.regime_detector.detect_regime(df)

        # Régime 5m pour confirmation
        df_5m = self.client.get_klines(symbol, '5m', limit=300)
        regime_5m = self.regime_detector.detect_regime(df_5m)

        # Accepter seulement si cohérence
        if regime_1m == 'RANGE' and regime_5m in ['RANGE', 'UNKNOWN']:
            signals = self.signal_generator.generate_signals(df, symbol)
            if not signals:
                return None

            best_signal = max(signals, key=lambda x: x.score)
            inverse_side = 'SHORT' if best_signal.side == 'LONG' else 'LONG'

            return Signal(
                symbol=symbol,
                side=inverse_side,
                score=best_signal.score,
                strategy=f'INVERSE_V2_{best_signal.strategy}',
                entry_price=best_signal.entry_price,
                stop_loss=best_signal.take_profit,
                take_profit=best_signal.stop_loss,
                confluences=best_signal.confluences + [f'MTF_RANGE'],
                timeframe=best_signal.timeframe,
                timestamp=best_signal.timestamp
            )
        return None


class InverseStrategyV3_DynamicSizing:
    """V3: Position sizing dynamique selon score"""

    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.signal_generator = ShortTermSignalGenerator()
        self.regime_detector = MarketRegimeDetectorAdvanced()
        self.version = "V3_DYNAMIC_SIZE"

    def generate_inverse_signal(self, df: pd.DataFrame, symbol: str) -> Optional[Signal]:
        regime = self.regime_detector.detect_regime(df)
        signals = self.signal_generator.generate_signals(df, symbol)

        if not signals or regime != 'RANGE':
            return None

        best_signal = max(signals, key=lambda x: x.score)
        inverse_side = 'SHORT' if best_signal.side == 'LONG' else 'LONG'

        signal = Signal(
            symbol=symbol,
            side=inverse_side,
            score=best_signal.score,
            strategy=f'INVERSE_V3_{best_signal.strategy}',
            entry_price=best_signal.entry_price,
            stop_loss=best_signal.take_profit,
            take_profit=best_signal.stop_loss,
            confluences=best_signal.confluences + [f'DYN_SIZE'],
            timeframe=best_signal.timeframe,
            timestamp=best_signal.timestamp
        )

        return signal

    def get_position_size(self, leverage: int, score: float, initial_capital: float) -> float:
        """Position sizing basé sur score"""
        base_risk = 0.02 if leverage >= 200 else 0.015

        # Ajuster selon score
        if score >= 1.0:
            risk_multiplier = 1.5  # +50%
        elif score >= 0.95:
            risk_multiplier = 1.25
        elif score >= 0.90:
            risk_multiplier = 1.0
        else:
            risk_multiplier = 0.75  # -25%

        position_size = initial_capital * base_risk * risk_multiplier * leverage
        return position_size


class InverseStrategyV4_TrailingStop:
    """V4: Trailing stop agressif"""

    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.signal_generator = ShortTermSignalGenerator()
        self.regime_detector = MarketRegimeDetectorAdvanced()
        self.version = "V4_TRAILING"

    def generate_inverse_signal(self, df: pd.DataFrame, symbol: str) -> Optional[Signal]:
        regime = self.regime_detector.detect_regime(df)
        signals = self.signal_generator.generate_signals(df, symbol)

        if not signals or regime != 'RANGE':
            return None

        best_signal = max(signals, key=lambda x: x.score)
        inverse_side = 'SHORT' if best_signal.side == 'LONG' else 'LONG'

        return Signal(
            symbol=symbol,
            side=inverse_side,
            score=best_signal.score,
            strategy=f'INVERSE_V4_{best_signal.strategy}',
            entry_price=best_signal.entry_price,
            stop_loss=best_signal.take_profit,
            take_profit=best_signal.stop_loss,
            confluences=best_signal.confluences + ['TRAILING'],
            timeframe=best_signal.timeframe,
            timestamp=best_signal.timestamp
        )

    def update_trailing_stop(self, side: str, entry_price: float, current_price: float,
                            current_sl: float) -> float:
        """Met à jour le trailing stop"""
        if side == 'LONG':
            pnl_pct = (current_price - entry_price) / entry_price

            if pnl_pct >= 0.02:  # +2%
                new_sl = current_price * 0.9925  # Trail à 0.75%
            elif pnl_pct >= 0.01:  # +1%
                new_sl = current_price * 0.995  # Trail à 0.5%
            else:
                new_sl = current_sl

            return max(new_sl, current_sl)  # Ne jamais baisser le SL
        else:  # SHORT
            pnl_pct = (entry_price - current_price) / entry_price

            if pnl_pct >= 0.02:
                new_sl = current_price * 1.0075
            elif pnl_pct >= 0.01:
                new_sl = current_price * 1.005
            else:
                new_sl = current_sl

            return min(new_sl, current_sl)


class InverseStrategyV5_StrictFilter:
    """V5: Filtrage ultra strict (score ≥0.90, 3+ confluences)"""

    def __init__(self):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.signal_generator = ShortTermSignalGenerator()
        self.regime_detector = MarketRegimeDetectorAdvanced()
        self.version = "V5_STRICT"

    def generate_inverse_signal(self, df: pd.DataFrame, symbol: str) -> Optional[Signal]:
        regime = self.regime_detector.detect_regime(df)

        if regime != 'RANGE':
            return None

        signals = self.signal_generator.generate_signals(df, symbol)
        if not signals:
            return None

        # Filtrage strict
        filtered = [s for s in signals if s.score >= 0.90 and len(s.confluences) >= 3]
        if not filtered:
            return None

        best_signal = max(filtered, key=lambda x: x.score)
        inverse_side = 'SHORT' if best_signal.side == 'LONG' else 'LONG'

        return Signal(
            symbol=symbol,
            side=inverse_side,
            score=best_signal.score,
            strategy=f'INVERSE_V5_{best_signal.strategy}',
            entry_price=best_signal.entry_price,
            stop_loss=best_signal.take_profit,
            take_profit=best_signal.stop_loss,
            confluences=best_signal.confluences + ['STRICT_FILTER'],
            timeframe=best_signal.timeframe,
            timestamp=best_signal.timestamp
        )


class InverseStrategyV6_Combined:
    """V6: Combinaison des meilleures optimisations"""

    def __init__(self, client):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.signal_generator = ShortTermSignalGenerator()
        self.regime_detector = MarketRegimeDetectorAdvanced()
        self.client = client
        self.version = "V6_COMBINED"

    def generate_inverse_signal(self, df: pd.DataFrame, symbol: str) -> Optional[Signal]:
        # Multi-timeframe
        regime_1m = self.regime_detector.detect_regime(df)
        df_5m = self.client.get_klines(symbol, '5m', limit=300)
        regime_5m = self.regime_detector.detect_regime(df_5m)

        if regime_1m != 'RANGE' or regime_5m not in ['RANGE', 'UNKNOWN']:
            return None

        # Filtrage strict
        signals = self.signal_generator.generate_signals(df, symbol)
        filtered = [s for s in signals if s.score >= 0.90 and len(s.confluences) >= 3]

        if not filtered:
            return None

        best_signal = max(filtered, key=lambda x: x.score)
        inverse_side = 'SHORT' if best_signal.side == 'LONG' else 'LONG'

        return Signal(
            symbol=symbol,
            side=inverse_side,
            score=best_signal.score,
            strategy=f'INVERSE_V6_{best_signal.strategy}',
            entry_price=best_signal.entry_price,
            stop_loss=best_signal.take_profit,
            take_profit=best_signal.stop_loss,
            confluences=best_signal.confluences + ['COMBINED_OPT'],
            timeframe=best_signal.timeframe,
            timestamp=best_signal.timestamp
        )

    def get_position_size(self, leverage: int, score: float, initial_capital: float) -> float:
        """Position sizing dynamique"""
        base_risk = 0.02 if leverage >= 200 else 0.015
        risk_multiplier = 1.5 if score >= 1.0 else (1.25 if score >= 0.95 else 1.0)
        return initial_capital * base_risk * risk_multiplier * leverage

    def update_trailing_stop(self, side: str, entry_price: float, current_price: float,
                            current_sl: float) -> float:
        """Trailing stop"""
        if side == 'LONG':
            pnl_pct = (current_price - entry_price) / entry_price
            if pnl_pct >= 0.02:
                new_sl = current_price * 0.9925
            elif pnl_pct >= 0.01:
                new_sl = current_price * 0.995
            else:
                new_sl = current_sl
            return max(new_sl, current_sl)
        else:
            pnl_pct = (entry_price - current_price) / entry_price
            if pnl_pct >= 0.02:
                new_sl = current_price * 1.0075
            elif pnl_pct >= 0.01:
                new_sl = current_price * 1.005
            else:
                new_sl = current_sl
            return min(new_sl, current_sl)

    def should_exit_early(self, df: pd.DataFrame, symbol: str, entry_regime: str,
                          current_side: str) -> Tuple[bool, str]:
        """Exit anticipée"""
        current_regime = self.regime_detector.detect_regime(df)
        if current_regime != entry_regime and current_regime != 'RANGE':
            return True, 'REGIME_CHANGE'

        signals = self.signal_generator.generate_signals(df, symbol)
        if signals:
            best = max(signals, key=lambda x: x.score)
            if best.score >= 0.90 and best.side != current_side:
                return True, 'COUNTER_SIGNAL'

        return False, ''
