"""Basic factors for OpenClaw Trading.

This module provides free basic trading factors that are available
to all agents without purchase. These include common technical
indicators like moving average crossovers, RSI, MACD, and Bollinger Bands.
"""

from typing import Any, Optional

import pandas as pd

from openclaw.factor.base import BuyFactor, SellFactor
from openclaw.factor.types import FactorCategory, FactorContext, FactorResult, FactorSignal
from openclaw.indicators.technical import bollinger_bands, ema, macd, rsi, sma


class MovingAverageCrossoverFactor(BuyFactor):
    """Buy factor based on moving average crossover.

    Generates a buy signal when a fast moving average crosses above
    a slow moving average. This is a classic trend-following signal.

    Parameters:
        fast_period: Period for fast moving average (default: 10)
        slow_period: Period for slow moving average (default: 30)
        use_ema: Use exponential moving average instead of simple (default: True)
    """

    def __init__(
        self,
        parameters: Optional[dict[str, Any]] = None,
    ):
        super().__init__(
            name="MA Crossover",
            description="Buy when fast MA crosses above slow MA",
            category=FactorCategory.BASIC,
            price=0.0,
            parameters=parameters,
        )
        self.fast_period = self.parameters.get("fast_period", 10)
        self.slow_period = self.parameters.get("slow_period", 30)
        self.use_ema = self.parameters.get("use_ema", True)

        # Track previous state for crossover detection
        self._prev_fast: Optional[float] = None
        self._prev_slow: Optional[float] = None

    def on_init(self) -> None:
        """Initialize factor with parameter validation."""
        if self.fast_period >= self.slow_period:
            raise ValueError("fast_period must be less than slow_period")

    def on_evaluate(self, context: FactorContext) -> Optional[FactorResult]:
        """Evaluate moving average crossover.

        Args:
            context: Factor context with market data

        Returns:
            FactorResult if crossover detected, None otherwise
        """
        if len(context.data) < self.slow_period + 1:
            return None

        close = context.data["close"]

        # Calculate moving averages
        if self.use_ema:
            fast_ma = ema(close, self.fast_period)
            slow_ma = ema(close, self.slow_period)
        else:
            fast_ma = sma(close, self.fast_period)
            slow_ma = sma(close, self.slow_period)

        # Get current and previous values
        curr_fast = fast_ma.iloc[-1]
        curr_slow = slow_ma.iloc[-1]
        prev_fast = fast_ma.iloc[-2]
        prev_slow = slow_ma.iloc[-2]

        # Check for crossover (fast was below, now above)
        if prev_fast <= prev_slow and curr_fast > curr_slow:
            # Calculate confidence based on the strength of the crossover
            crossover_pct = (curr_fast - curr_slow) / curr_slow
            confidence = min(0.5 + crossover_pct * 10, 0.9)

            return FactorResult(
                signal=FactorSignal.BUY,
                confidence=confidence,
                symbol=context.symbol,
                metadata={
                    "fast_ma": curr_fast,
                    "slow_ma": curr_slow,
                    "crossover_pct": crossover_pct,
                    "factor_name": self.metadata.name,
                },
            )

        return None


class RSIOversoldFactor(BuyFactor):
    """Buy factor based on RSI oversold conditions.

    Generates a buy signal when RSI falls below an oversold threshold
    and then crosses back above it. This is a mean-reversion signal.

    Parameters:
        period: RSI calculation period (default: 14)
        oversold_threshold: RSI level considered oversold (default: 30)
    """

    def __init__(
        self,
        parameters: Optional[dict[str, Any]] = None,
    ):
        super().__init__(
            name="RSI Oversold",
            description="Buy when RSI indicates oversold conditions",
            category=FactorCategory.BASIC,
            price=0.0,
            parameters=parameters,
        )
        self.period = self.parameters.get("period", 14)
        self.oversold_threshold = self.parameters.get("oversold_threshold", 30)

        self._was_oversold = False

    def on_evaluate(self, context: FactorContext) -> Optional[FactorResult]:
        """Evaluate RSI oversold condition.

        Args:
            context: Factor context with market data

        Returns:
            FactorResult if oversold condition met, None otherwise
        """
        if len(context.data) < self.period + 1:
            return None

        close = context.data["close"]
        rsi_values = rsi(close, self.period)

        current_rsi = rsi_values.iloc[-1]

        # Check if currently oversold
        if current_rsi < self.oversold_threshold:
            self._was_oversold = True
            return None

        # Check for exit from oversold zone
        if self._was_oversold and current_rsi >= self.oversold_threshold:
            self._was_oversold = False

            # Calculate confidence based on how deeply oversold it was
            confidence = min(0.5 + (self.oversold_threshold - current_rsi) / 100, 0.85)

            return FactorResult(
                signal=FactorSignal.BUY,
                confidence=confidence,
                symbol=context.symbol,
                metadata={
                    "rsi": current_rsi,
                    "threshold": self.oversold_threshold,
                    "factor_name": self.metadata.name,
                },
            )

        return None


class MACDCrossoverFactor(BuyFactor):
    """Buy factor based on MACD crossover.

    Generates a buy signal when MACD line crosses above the signal line.
    MACD is a trend-following momentum indicator.

    Parameters:
        fast_period: Fast EMA period (default: 12)
        slow_period: Slow EMA period (default: 26)
        signal_period: Signal line EMA period (default: 9)
    """

    def __init__(
        self,
        parameters: Optional[dict[str, Any]] = None,
    ):
        super().__init__(
            name="MACD Crossover",
            description="Buy when MACD crosses above signal line",
            category=FactorCategory.BASIC,
            price=0.0,
            parameters=parameters,
        )
        self.fast_period = self.parameters.get("fast_period", 12)
        self.slow_period = self.parameters.get("slow_period", 26)
        self.signal_period = self.parameters.get("signal_period", 9)

    def on_evaluate(self, context: FactorContext) -> Optional[FactorResult]:
        """Evaluate MACD crossover.

        Args:
            context: Factor context with market data

        Returns:
            FactorResult if MACD crossover detected, None otherwise
        """
        min_periods = self.slow_period + self.signal_period
        if len(context.data) < min_periods + 1:
            return None

        close = context.data["close"]
        macd_data = macd(
            close,
            fast_period=self.fast_period,
            slow_period=self.slow_period,
            signal_period=self.signal_period,
        )

        macd_line = macd_data["macd"]
        signal_line = macd_data["signal"]
        histogram = macd_data["histogram"]

        # Check for crossover (MACD was below signal, now above)
        if (
            len(macd_line) >= 2
            and macd_line.iloc[-2] <= signal_line.iloc[-2]
            and macd_line.iloc[-1] > signal_line.iloc[-1]
        ):
            # Calculate confidence based on histogram strength
            hist_strength = abs(histogram.iloc[-1]) / abs(macd_line.iloc[-1])
            confidence = min(0.55 + hist_strength * 0.3, 0.9)

            return FactorResult(
                signal=FactorSignal.BUY,
                confidence=confidence,
                symbol=context.symbol,
                metadata={
                    "macd": macd_line.iloc[-1],
                    "signal": signal_line.iloc[-1],
                    "histogram": histogram.iloc[-1],
                    "factor_name": self.metadata.name,
                },
            )

        return None


class BollingerBandBreakoutFactor(BuyFactor):
    """Buy factor based on Bollinger Bands breakout.

    Generates a buy signal when price breaks above the upper Bollinger Band
    or bounces off the lower band. This captures volatility expansion signals.

    Parameters:
        period: Period for moving average (default: 20)
        std_dev: Standard deviation multiplier (default: 2.0)
        breakout_type: Type of breakout - 'upper' or 'lower_bounce' (default: 'lower_bounce')
    """

    def __init__(
        self,
        parameters: Optional[dict[str, Any]] = None,
    ):
        super().__init__(
            name="Bollinger Band Breakout",
            description="Buy on Bollinger Band breakout or bounce",
            category=FactorCategory.BASIC,
            price=0.0,
            parameters=parameters,
        )
        self.period = self.parameters.get("period", 20)
        self.std_dev = self.parameters.get("std_dev", 2.0)
        self.breakout_type = self.parameters.get("breakout_type", "lower_bounce")

        self._was_below_lower = False

    def on_evaluate(self, context: FactorContext) -> Optional[FactorResult]:
        """Evaluate Bollinger Band breakout.

        Args:
            context: Factor context with market data

        Returns:
            FactorResult if breakout detected, None otherwise
        """
        if len(context.data) < self.period + 1:
            return None

        close = context.data["close"]
        bb_data = bollinger_bands(close, period=self.period, std_dev=self.std_dev)

        upper = bb_data["upper"]
        middle = bb_data["middle"]
        lower = bb_data["lower"]

        current_price = close.iloc[-1]
        prev_price = close.iloc[-2]

        if self.breakout_type == "upper":
            # Breakout above upper band
            if prev_price <= upper.iloc[-2] and current_price > upper.iloc[-1]:
                confidence = min(0.6 + (current_price - upper.iloc[-1]) / upper.iloc[-1] * 10, 0.85)

                return FactorResult(
                    signal=FactorSignal.BUY,
                    confidence=confidence,
                    symbol=context.symbol,
                    metadata={
                        "price": current_price,
                        "upper_band": upper.iloc[-1],
                        "breakout_type": "upper",
                        "factor_name": self.metadata.name,
                    },
                )

        elif self.breakout_type == "lower_bounce":
            # Bounce off lower band
            if current_price < lower.iloc[-1]:
                self._was_below_lower = True
                return None

            if self._was_below_lower and current_price >= lower.iloc[-1]:
                self._was_below_lower = False

                # Calculate how close to the middle band we are
                band_range = upper.iloc[-1] - lower.iloc[-1]
                position_in_band = (current_price - lower.iloc[-1]) / band_range if band_range > 0 else 0
                confidence = min(0.5 + position_in_band * 0.4, 0.8)

                return FactorResult(
                    signal=FactorSignal.BUY,
                    confidence=confidence,
                    symbol=context.symbol,
                    metadata={
                        "price": current_price,
                        "lower_band": lower.iloc[-1],
                        "middle_band": middle.iloc[-1],
                        "breakout_type": "lower_bounce",
                        "factor_name": self.metadata.name,
                    },
                )

        return None


class RSIOverboughtFactor(SellFactor):
    """Sell factor based on RSI overbought conditions.

    Generates a sell signal when RSI rises above an overbought threshold.
    This is a mean-reversion sell signal.

    Parameters:
        period: RSI calculation period (default: 14)
        overbought_threshold: RSI level considered overbought (default: 70)
    """

    def __init__(
        self,
        parameters: Optional[dict[str, Any]] = None,
    ):
        super().__init__(
            name="RSI Overbought",
            description="Sell when RSI indicates overbought conditions",
            category=FactorCategory.BASIC,
            price=0.0,
            parameters=parameters,
        )
        self.period = self.parameters.get("period", 14)
        self.overbought_threshold = self.parameters.get("overbought_threshold", 70)

    def on_evaluate(self, context: FactorContext) -> Optional[FactorResult]:
        """Evaluate RSI overbought condition.

        Args:
            context: Factor context with market data

        Returns:
            FactorResult if overbought condition met, None otherwise
        """
        if len(context.data) < self.period:
            return None

        close = context.data["close"]
        rsi_values = rsi(close, self.period)

        current_rsi = rsi_values.iloc[-1]

        # Check for overbought condition
        if current_rsi > self.overbought_threshold:
            # Calculate confidence based on how overbought
            confidence = min(0.5 + (current_rsi - self.overbought_threshold) / 60, 0.85)

            return FactorResult(
                signal=FactorSignal.SELL,
                confidence=confidence,
                symbol=context.symbol,
                metadata={
                    "rsi": current_rsi,
                    "threshold": self.overbought_threshold,
                    "factor_name": self.metadata.name,
                },
            )

        return None
