# src/strategies/bollinger_reversion.py
"""
Estrategia de reversión a la media con Bollinger Bands.
"""
from __future__ import annotations

import pandas as pd

from ..core.strategy import Strategy, Signal, calculate_bollinger_bands, cross_above, cross_below

class BollingerReversion(Strategy):
    """
    Mean reversion basada en Bollinger Bands.

    Señales:
    - BUY: close cruza por debajo de la banda inferior
    - SELL: close cruza por encima de la banda superior
    - HOLD: resto de casos
    """

    strategy_id = "bollinger_reversion"
    strategy_family = "mean_reversion"
    display_name = "Bollinger Reversion"
    description = "Reversión a la media usando Bollinger Bands."

    def __init__(
        self,
        bb_period: int = 20,
        bb_std: float = 2.0,
        exit_on_mid: bool = True,
    ):
        params = {
            "bb_period": bb_period,
            "bb_std": bb_std,
            "exit_on_mid": exit_on_mid,
        }
        super().__init__(name="Bollinger Reversion", params=params)

        self.bb_period = int(bb_period)
        self.bb_std = float(bb_std)
        self.exit_on_mid = bool(exit_on_mid)

    @classmethod
    def default_parameters(cls) -> dict:
        return {
            "bb_period": 20,
            "bb_std": 2.0,
            "exit_on_mid": True,
        }

    @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 {
            "bb_period": {
                "type": "int",
                "min": 2,
                "max": 500,
                "default": 20,
            },
            "bb_std": {
                "type": "float",
                "min": 0.1,
                "max": 5.0,
                "default": 2.0,
            },
            "exit_on_mid": {
                "type": "bool",
                "default": True,
            },
        }

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

        bb_mid, bb_upper, bb_lower = calculate_bollinger_bands(
            close,
            period=self.bb_period,
            std_dev=self.bb_std,
        )

        data["bb_mid"] = bb_mid
        data["bb_upper"] = bb_upper
        data["bb_lower"] = bb_lower

        data["cross_below_lower"] = cross_below(close, data["bb_lower"])
        data["cross_above_upper"] = cross_above(close, data["bb_upper"])
        data["cross_above_mid"] = cross_above(close, data["bb_mid"])
        data["cross_below_mid"] = cross_below(close, data["bb_mid"])

        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 = ["bb_mid", "bb_upper", "bb_lower"]
        if any(pd.isna(row[c]) for c in needed):
            return Signal.HOLD

        if bool(row["cross_below_lower"]) and self.current_position <= 0:
            return Signal.BUY

        if bool(row["cross_above_upper"]) and self.current_position >= 0:
            return Signal.SELL

        if self.exit_on_mid:
            if self.current_position > 0 and bool(row["cross_above_mid"]):
                return Signal.SELL
            if self.current_position < 0 and bool(row["cross_below_mid"]):
                return Signal.BUY

        return Signal.HOLD
