#src/strategies/regime_filtered_trend.py
"""
Estrategia trend-following con filtro de régimen interno.
"""
from __future__ import annotations

import pandas as pd

from ..core.strategy import Strategy, Signal, calculate_sma, calculate_ema


class RegimeFilteredTrend(Strategy):
    """
    Trend following con cruce de medias y filtro de régimen simple.
    """

    strategy_id = "regime_filtered_trend"
    strategy_family = "regime_aware"
    display_name = "Regime Filtered Trend"
    description = "Cruce de medias con filtro de régimen basado en EMAs."

    def __init__(
        self,
        fast_period: int = 20,
        slow_period: int = 50,
        ma_type: str = "ema",
        long_regime_min_score: int = 2,
        short_regime_max_score: int = -2,
    ):
        params = {
            "fast_period": fast_period,
            "slow_period": slow_period,
            "ma_type": ma_type,
            "long_regime_min_score": long_regime_min_score,
            "short_regime_max_score": short_regime_max_score,
        }
        super().__init__(name="Regime Filtered Trend", params=params)

        self.fast_period = int(fast_period)
        self.slow_period = int(slow_period)
        self.ma_type = str(ma_type).lower()
        self.long_regime_min_score = int(long_regime_min_score)
        self.short_regime_max_score = int(short_regime_max_score)

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

    @classmethod
    def default_parameters(cls) -> dict:
        return {
            "fast_period": 20,
            "slow_period": 50,
            "ma_type": "ema",
            "long_regime_min_score": 2,
            "short_regime_max_score": -2,
        }

    @classmethod
    def strategy_metadata(cls) -> dict:
        return {
            "strategy_id": cls.strategy_id,
            "name": cls.display_name,
            "family": cls.strategy_family,
            "direction": "long_short",
            "description": cls.description,
        }

    @classmethod
    def strategy_definition(cls) -> dict:
        return {
            "meta": cls.strategy_metadata(),
            "defaults": cls.default_parameters(),
            "parameters_schema": cls.parameters_schema(),
        }

    @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",
            },
            "long_regime_min_score": {
                "type": "int",
                "min": -4,
                "max": 4,
                "default": 2,
            },
            "short_regime_max_score": {
                "type": "int",
                "min": -4,
                "max": 4,
                "default": -2,
            },
        }

    def init_indicators(self, data: pd.DataFrame) -> pd.DataFrame:
        close = data["close"]

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

        data["ema_20_regime"] = calculate_ema(close, 20)
        data["ema_50_regime"] = calculate_ema(close, 50)
        data["ema_100_regime"] = calculate_ema(close, 100)
        data["ema_200_regime"] = calculate_ema(close, 200)

        score = (
            (close > data["ema_20_regime"]).astype(int).replace({0: -1}) +
            (close > data["ema_50_regime"]).astype(int).replace({0: -1}) +
            (close > data["ema_100_regime"]).astype(int).replace({0: -1}) +
            (close > data["ema_200_regime"]).astype(int).replace({0: -1})
        )

        data["regime_score"] = score

        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
        data["ma_cross_change"] = data["ma_cross"].diff()

        return data

    def generate_signal(self, idx: int) -> Signal:
        if self.data is None:
            raise ValueError("Data no establecida")

        if idx < 1:
            return Signal.HOLD

        row = self.data.iloc[idx]

        needed = ["ma_fast", "ma_slow", "regime_score", "ma_cross_change"]
        if any(pd.isna(row[c]) for c in needed):
            return Signal.HOLD

        cross_change = row["ma_cross_change"]
        regime_score = int(row["regime_score"])

        if cross_change == 2 and regime_score >= self.long_regime_min_score and self.current_position <= 0:
            return Signal.BUY

        if cross_change == -2 and regime_score <= self.short_regime_max_score and self.current_position >= 0:
            return Signal.SELL

        if self.current_position > 0 and regime_score < self.long_regime_min_score:
            return Signal.SELL

        if self.current_position < 0 and regime_score > self.short_regime_max_score:
            return Signal.BUY

        return Signal.HOLD
