import pandas as pd

from signalab.data.market_data import MarketData
from signalab.indicator._utils import _ema, _ma, _mid_price
from signalab.indicator.base import Indicator
from signalab.indicator.mamethod import MAMethod


class MovingAverageCross(Indicator):
    """Fast/slow moving-average crossover indicator.

    Notes
    -----
    Signals are encoded as ``1`` for buy, ``-1`` for sell, and ``0`` for hold.
    In ``crossover_only`` mode, a signal is emitted only at crossing points.
    """

    def __init__(
        self,
        fast_period: int = 8,
        slow_period: int = 16,
        method: MAMethod = MAMethod.EMA,
        candles_type: str = "5m",
        crossover_only: bool = True,
        label: str = "ma",
    ):
        """Initialize moving-average crossover configuration.

        Parameters
        ----------
        fast_period : int, default=8
            Fast moving-average period.
        slow_period : int, default=16
            Slow moving-average period.
        method : MAMethod, default=MAMethod.EMA
            Moving-average method used for both lines.
        candles_type : str, default="5m"
            Candle timeframe queried from :class:`MarketData`.
        crossover_only : bool, default=True
            If ``True``, signals are emitted only at crossing points.
        label : str, default="ma"
            Output label namespace.
        """
        super().__init__(name="Moving Average Cross", label=label)
        self.fast_period = fast_period
        self.slow_period = slow_period
        self.method = method
        self.crossover_only = crossover_only
        self.candles_type = candles_type

    def compute(self, data: MarketData) -> pd.DataFrame:
        """Compute moving-average crossover values and signals.

        Parameters
        ----------
        data : MarketData
            Source market data.

        Returns
        -------
        pd.DataFrame
            Dataframe with ``fast``, ``slow``, and ``signal`` columns.
        """
        candles = data.query(self.candles_type)
        price = _mid_price(candles)

        fast = _ma(price, self.fast_period, self.method)
        slow = _ma(price, self.slow_period, self.method)

        df = pd.DataFrame(index=candles.index)
        df["fast"] = fast
        df["slow"] = slow

        df["signal"] = 0

        if self.crossover_only:
            prev_fast = fast.shift(1)
            prev_slow = slow.shift(1)
            df.loc[(fast > slow) & (prev_fast <= prev_slow), "signal"] = 1  # crossed up
            df.loc[
                (fast < slow) & (prev_fast >= prev_slow), "signal"
            ] = -1  # crossed down
        else:
            df.loc[fast > slow, "signal"] = 1
            df.loc[fast < slow, "signal"] = -1
        df.dropna(inplace=True)
        return df

    def __str__(self) -> str:
        """Return a compact string representation.

        Returns
        -------
        str
            Indicator summary.
        """
        return (
            f"MA(fast={self.fast_period}, slow={self.slow_period}, "
            f"method={self.method.value}, candles={self.candles_type})"
        )


class MACD(Indicator):
    """Moving Average Convergence Divergence indicator.

    Notes
    -----
    Signals are encoded as ``1`` for buy, ``-1`` for sell, and ``0`` for hold.
    In ``crossover_only`` mode, signals are emitted only when the histogram
    changes sign.
    """

    def __init__(
        self,
        fast_period: int = 5,
        slow_period: int = 13,
        signal_period: int = 3,
        crossover_only: bool = True,
        candles_type: str = "5m",
        label: str = "macd",
    ):
        """Initialize MACD configuration.

        Parameters
        ----------
        fast_period : int, default=5
            Fast EMA period.
        slow_period : int, default=13
            Slow EMA period.
        signal_period : int, default=3
            Signal-line EMA period.
        crossover_only : bool, default=True
            If ``True``, signals are emitted only at histogram sign changes.
        candles_type : str, default="5m"
            Candle timeframe queried from :class:`MarketData`.
        label : str, default="macd"
            Output label namespace.
        """
        super().__init__(name="MACD", label=label)
        self.fast_period = fast_period
        self.slow_period = slow_period
        self.signal_period = signal_period
        self.crossover_only = crossover_only
        self.candles_type = candles_type

    def compute(self, data: MarketData) -> pd.DataFrame:
        """Compute MACD line, signal line, histogram, and trading signal.

        Parameters
        ----------
        data : MarketData
            Source market data.

        Returns
        -------
        pd.DataFrame
            Dataframe with ``macd``, ``signal_line``, ``histogram``, and
            ``signal`` columns.
        """
        candles = data.query(self.candles_type)
        price = _mid_price(candles)

        fast_ema = _ema(price, self.fast_period)
        slow_ema = _ema(price, self.slow_period)
        macd_line = fast_ema - slow_ema
        signal_line = _ema(macd_line, self.signal_period)
        histogram = macd_line - signal_line

        df = pd.DataFrame(index=candles.index)
        df["macd"] = macd_line
        df["signal_line"] = signal_line
        df["histogram"] = histogram

        if self.crossover_only:
            # Signal only on the candle where crossover happens
            prev_hist = histogram.shift(1)
            df["signal"] = 0
            df.loc[(histogram > 0) & (prev_hist <= 0), "signal"] = 1  # crossed up
            df.loc[(histogram < 0) & (prev_hist >= 0), "signal"] = -1  # crossed down
        else:
            # Signal as long as macd is above/below signal line
            df["signal"] = 0
            df.loc[histogram > 0, "signal"] = 1
            df.loc[histogram < 0, "signal"] = -1

        df.dropna(inplace=True)
        return df

    def __str__(self) -> str:
        """Return a compact string representation.

        Returns
        -------
        str
            Indicator summary.
        """
        return (
            f"MACD(fast={self.fast_period}, slow={self.slow_period}, "
            f"signal={self.signal_period}, candles={self.candles_type})"
        )
