#src/strategies/donchian_breakout.py
"""
Estrategia de breakout de rango Donchian.
"""
from __future__ import annotations

import pandas as pd

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


class DonchianBreakout(Strategy):
    """
    Breakout de máximos/mínimos de N barras.

    Señales:
    - BUY: close rompe por encima del máximo Donchian previo
    - SELL: close rompe por debajo del mínimo Donchian previo
    """

    strategy_id = "donchian_breakout"
    strategy_family = "breakout"
    display_name = "Donchian Breakout"
    description = "Breakout de rango basado en máximos y mínimos rolling."

    def __init__(self, donchian_window: int = 20, exit_window: int = 10):
        params = {
            "donchian_window": donchian_window,
            "exit_window": exit_window,
        }
        super().__init__(name="Donchian Breakout", params=params)

        self.donchian_window = int(donchian_window)
        self.exit_window = int(exit_window)

    @classmethod
    def default_parameters(cls) -> dict:
        return {
            "donchian_window": 20,
            "exit_window": 10,
        }

    @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 {
            "donchian_window": {
                "type": "int",
                "min": 2,
                "max": 300,
                "default": 20,
            },
            "exit_window": {
                "type": "int",
                "min": 2,
                "max": 300,
                "default": 10,
            },
        }

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

        data["donchian_high"], data["donchian_low"] = calculate_donchian_channels(
            high,
            low,
            period=self.donchian_window,
            shift=1,
        )

        data["exit_high"], data["exit_low"] = calculate_donchian_channels(
            high,
            low,
            period=self.exit_window,
            shift=1,
        )

        data["breakout_up"] = cross_above(close, data["donchian_high"])
        data["breakout_down"] = cross_below(close, data["donchian_low"])

        data["lose_exit_low"] = cross_below(close, data["exit_low"])
        data["lose_exit_high"] = cross_above(close, data["exit_high"])

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

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

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

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

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

        return Signal.HOLD
