# src/strategies/moving_average.py
"""
Estrategia de cruce de medias móviles con filtro ADX opcional
"""
import pandas as pd
from ..core.strategy import Strategy, Signal, calculate_sma, calculate_ema


class MovingAverageCrossover(Strategy):
    """
    Estrategia de cruce de medias móviles

    Señales:@classmethod
    def default_parameters(cls) -> dict:
        return {
            "fast_period": 10,
            "slow_period": 30,
        }
    - BUY: Cruce alcista de medias + (ADX >= threshold si está activado)
    - SELL: Cruce bajista de medias
    - HOLD: En cualquier otro caso

    Parámetros:
        fast_period: Periodo MA rápida
        slow_period: Periodo MA lenta
        ma_type: 'sma' o 'ema'
        use_adx: Activar filtro ADX
        adx_threshold: Umbral mínimo de ADX

    Valores por defecto:
        20/50 EMA → clásico en crypto 1h - 4h
        EMA reacciona mejor que SMA
        Sin ADX todavía → primero evaluamos la señal “pura”
    """

    strategy_id = "moving_average"

    def __init__(
        self,
        fast_period: int = 20,
        slow_period: int = 50,
        ma_type: str = 'ema',
        use_adx: bool = False,
        adx_threshold: float = 20.0
    ):
        params = {
            'fast_period': fast_period,
            'slow_period': slow_period,
            'ma_type': ma_type,
            'use_adx': use_adx,
            'adx_threshold': adx_threshold
        }

        super().__init__(name="Moving Average Crossover", params=params)

        self.fast_period = fast_period
        self.slow_period = slow_period
        self.ma_type = ma_type.lower()
        self.use_adx = use_adx
        self.adx_threshold = adx_threshold

        if self.ma_type not in ['sma', 'ema']:
            raise ValueError("ma_type debe ser 'sma' o 'ema'")

    @classmethod
    def parameters_schema(cls) -> dict:
        return {
            "fast_period": {
                "type": "int",
                "min": 1,
                "max": 500,
                "default": 20,
            },
            "slow_period": {
                "type": "int",
                "min": 1,
                "max": 500,
                "default": 50,
            },
            "ma_type": {
                "type": "enum",
                "choices": ["sma", "ema"],
                "default": "ema",
            },
            "use_adx": {
                "type": "bool",
                "default": False,
            },
            "adx_threshold": {
                "type": "float",
                "min": 1,
                "max": 100,
                "default": 20.0,
            },
        }


    def init_indicators(self, data: pd.DataFrame) -> pd.DataFrame:
        """
        Calcula indicadores necesarios (medias móviles)
        """
        close_prices = data['close']

        if self.ma_type == 'sma':
            data['ma_fast'] = calculate_sma(close_prices, self.fast_period)
            data['ma_slow'] = calculate_sma(close_prices, self.slow_period)
        else:
            data['ma_fast'] = calculate_ema(close_prices, self.fast_period)
            data['ma_slow'] = calculate_ema(close_prices, self.slow_period)

        # Estado del cruce
        data['ma_cross'] = 0
        data.loc[data['ma_fast'] > data['ma_slow'], 'ma_cross'] = 1
        data.loc[data['ma_fast'] < data['ma_slow'], 'ma_cross'] = -1

        # Cambio de estado (cruce real)
        data['ma_cross_change'] = data['ma_cross'].diff()

        return data

    # ------------------------------------------------------------------

    def generate_signal(self, idx: int) -> Signal:
        """
        Genera señal de trading
        """
        if self.data is None:
            raise ValueError("Data no establecida")

        if idx < 1:
            return Signal.HOLD

        row = self.data.iloc[idx]

        # MAs válidas
        if pd.isna(row['ma_fast']) or pd.isna(row['ma_slow']):
            return Signal.HOLD

        # 🔵 FILTRO ADX (solo para entradas)
        if self.use_adx:
            if 'adx' not in self.data.columns or pd.isna(row['adx']):
                return Signal.HOLD

            if row['adx'] < self.adx_threshold:
                return Signal.HOLD

        cross_change = row['ma_cross_change']

        # Cruce alcista
        if cross_change == 2:
            return Signal.BUY

        # Cruce bajista (salida siempre permitida)
        elif cross_change == -2:
            return Signal.SELL

        return Signal.HOLD
