"""Divergence detection strategy.

Detects price vs. indicator divergences (RSI/MACD) which are powerful
reversal signals with high success rates in ranging markets.
"""

from decimal import Decimal
from typing import Optional, Dict, Any, List
import pandas as pd
from src.strategies.base import BaseStrategy, StrategySignal, SignalType
from src.data.indicators import get_indicators
from src.core.logger import get_logger

logger = get_logger(__name__)


class DivergenceStrategy(BaseStrategy):
    """Divergence detection strategy.
    
    Detects bullish and bearish divergences between price and indicators
    (RSI or MACD) to identify potential reversals.
    
    - Bullish divergence: Price makes lower low, indicator makes higher low → BUY
    - Bearish divergence: Price makes higher high, indicator makes lower high → SELL
    """
    
    def __init__(self, name: str, parameters: Optional[Dict[str, Any]] = None, timeframes: Optional[list] = None):
        """Initialize divergence strategy.
        
        Parameters:
            indicator_type: Type of indicator - 'rsi' or 'macd' (default 'rsi')
            rsi_period: RSI period if using RSI (default 14)
            macd_fast: MACD fast period if using MACD (default 12)
            macd_slow: MACD slow period if using MACD (default 26)
            macd_signal: MACD signal period if using MACD (default 9)
            lookback: Lookback period for finding swings (default 20)
            min_swings: Minimum number of swings to detect divergence (default 2)
            min_confidence: Minimum confidence threshold for signal (default 0.5)
        """
        super().__init__(name, parameters, timeframes)
        
        self.indicator_type = self.parameters.get('indicator_type', 'rsi').lower()
        self.rsi_period = self.parameters.get('rsi_period', 14)
        self.macd_fast = self.parameters.get('macd_fast', 12)
        self.macd_slow = self.parameters.get('macd_slow', 26)
        self.macd_signal = self.parameters.get('macd_signal', 9)
        self.lookback = self.parameters.get('lookback', 20)
        self.min_swings = self.parameters.get('min_swings', 2)
        self.min_confidence = self.parameters.get('min_confidence', 0.5)
        
        self.indicators = get_indicators()
        self._price_history: List[float] = []
        self._last_divergence_type: Optional[str] = None
    
    def _calculate_indicator(self, prices: pd.Series) -> Optional[pd.Series]:
        """Calculate the selected indicator.
        
        Args:
            prices: Price series
            
        Returns:
            Indicator series or None
        """
        if self.indicator_type == 'rsi':
            if len(prices) < self.rsi_period + 1:
                return None
            return self.indicators.rsi(prices, self.rsi_period)
        
        elif self.indicator_type == 'macd':
            if len(prices) < self.macd_slow + self.macd_signal:
                return None
            macd_data = self.indicators.macd(prices, self.macd_fast, self.macd_slow, self.macd_signal)
            return macd_data['macd']
        
        return None
    
    async def on_tick(self, symbol: str, price: Decimal, timeframe: str, data: Dict[str, Any]) -> Optional[StrategySignal]:
        """Generate signal based on divergence detection."""
        # Add price to history
        self._price_history.append(float(price))
        
        # Determine minimum required data points
        if self.indicator_type == 'rsi':
            min_period = self.rsi_period + self.lookback * 3
        else:
            min_period = self.macd_slow + self.macd_signal + self.lookback * 3
        
        if len(self._price_history) < min_period:
            return None
        
        prices = pd.Series(self._price_history[-min_period:])
        
        # Calculate indicator
        indicator = self._calculate_indicator(prices)
        if indicator is None or len(indicator) < self.lookback * 2:
            return None
        
        # Align prices and indicator (indicator may be shorter due to calculation)
        if len(prices) != len(indicator):
            # Take the last len(indicator) prices to match indicator length
            prices = prices.iloc[-len(indicator):]
            indicator = indicator.reset_index(drop=True) if hasattr(indicator, 'reset_index') else indicator
        
        # Detect divergence
        divergence_result = self.indicators.detect_divergence(
            prices=prices,
            indicator=indicator,
            lookback=self.lookback,
            min_swings=self.min_swings
        )
        
        divergence_type = divergence_result.get('type')
        confidence = divergence_result.get('confidence', 0.0)
        
        # Check if we have a valid divergence with sufficient confidence
        if divergence_type is None or confidence < self.min_confidence:
            return None
        
        # Only generate signal if divergence type changed (avoid repeated signals)
        if divergence_type == self._last_divergence_type:
            return None
        
        self._last_divergence_type = divergence_type
        
        # Map divergence type to signal type
        if divergence_type == 'bullish':
            signal_type = SignalType.BUY
        elif divergence_type == 'bearish':
            signal_type = SignalType.SELL
        else:
            return None
        
        return StrategySignal(
            signal_type=signal_type,
            symbol=symbol,
            strength=confidence,
            price=price,
            metadata={
                'divergence_type': divergence_type,
                'confidence': confidence,
                'indicator_type': self.indicator_type,
                'lookback': self.lookback,
                'price_swing_high': divergence_result.get('price_swing_high'),
                'price_swing_low': divergence_result.get('price_swing_low'),
                'indicator_swing_high': divergence_result.get('indicator_swing_high'),
                'indicator_swing_low': divergence_result.get('indicator_swing_low'),
                'strategy': 'divergence'
            }
        )
    
    def on_signal(self, signal: StrategySignal) -> Optional[StrategySignal]:
        """Process signal."""
        return signal if self.should_execute(signal) else None

