"""Signal generation: crossover, spread-widen, latency-warning."""

from __future__ import annotations

from dataclasses import dataclass
from typing import List, Optional

try:
    import pandas as pd
    _HAS_PANDAS = True
except ImportError:
    _HAS_PANDAS = False

from sd_api.models.quote import Quote
from sd_api.models.signal import TradeSignal


@dataclass
class SignalEvent:
    """A discrete trading signal event.

    Attributes
    ----------
    ts_ms : int
        Timestamp in milliseconds.
    side : str
        "BUY" or "SELL".
    reason : str
        Human-readable description.
    strength : float
        Signal strength in [-1.0, 1.0].
    price : float | None
        Reference price.
    """
    ts_ms: int
    side: str
    reason: str
    strength: float
    price: Optional[float] = None


def crossover_signal(
    close_series: "pd.Series",
    fast: int = 10,
    slow: int = 20,
) -> List[SignalEvent]:
    """Detect SMA/EMA crossover signals.

    Parameters
    ----------
    close_series : pd.Series
        Chronological close price series.
    fast : int
        Fast MA window (default: 10).
    slow : int
        Slow MA window (default: 20).

    Returns
    -------
    List[SignalEvent]
        BUY when fast crosses above slow; SELL when fast crosses below slow.
    """
    if not _HAS_PANDAS:
        raise ImportError("pandas is required for crossover_signal")
    ma_fast = close_series.rolling(window=fast, min_periods=fast).mean()
    ma_slow = close_series.rolling(window=slow, min_periods=slow).mean()

    diff = ma_fast - ma_slow
    prev_diff = diff.shift(1)

    signals: List[SignalEvent] = []
    timestamps = close_series.index
    prices = close_series.values
    ts_values = close_series.index  # use index as timestamp proxy

    for i in range(1, len(diff)):
        if pd.isna(diff[i]) or pd.isna(prev_diff[i]):
            continue
        if prev_diff[i] <= 0 < diff[i]:
            signals.append(SignalEvent(
                ts_ms=int(ts_values[i].timestamp() * 1000) if hasattr(ts_values[i], "timestamp") else 0,
                side="BUY",
                reason=f"Fast MA({fast}) crossed above Slow MA({slow})",
                strength=min(1.0, abs(diff[i]) / abs(ma_slow[i]) if ma_slow[i] != 0 else 0),
                price=prices[i] if i < len(prices) else None,
            ))
        elif prev_diff[i] >= 0 > diff[i]:
            signals.append(SignalEvent(
                ts_ms=int(ts_values[i].timestamp() * 1000) if hasattr(ts_values[i], "timestamp") else 0,
                side="SELL",
                reason=f"Fast MA({fast}) crossed below Slow MA({slow})",
                strength=min(1.0, abs(diff[i]) / abs(ma_slow[i]) if ma_slow[i] != 0 else 0),
                price=prices[i] if i < len(prices) else None,
            ))
    return signals


def spread_widen_signal(
    quotes: List[Quote],
    threshold_multiplier: float = 2.0,
    window: int = 20,
) -> List[SignalEvent]:
    """Detect when spread significantly widens vs recent average.

    Parameters
    ----------
    quotes : List[Quote]
        Chronological quotes.
    threshold_multiplier : float
        Trigger when current spread > avg * this multiplier.
    window : int
        Lookback for computing the rolling average.

    Returns
    -------
    List[SignalEvent]
        SELL signals when spread widens (high volatility).
    """
    spreads = [q.spread if q.spread is not None else 0.0 for q in quotes]
    if len(spreads) < window:
        return []

    signals: List[SignalEvent] = []
    for i in range(window, len(spreads)):
        avg = sum(spreads[i - window:i]) / window
        cur = spreads[i]
        if avg > 0 and cur > avg * threshold_multiplier:
            signals.append(SignalEvent(
                ts_ms=quotes[i].quote_ts_ms or 0,
                side="SELL",
                reason=f"Spread widened: {cur:.4f} > {threshold_multiplier}x avg ({avg:.4f})",
                strength=min(1.0, cur / (avg * threshold_multiplier * 2)) if avg > 0 else 0.0,
                price=quotes[i].mid,
            ))
    return signals


def latency_warning_signal(
    quotes: List[Quote],
    threshold_ms: int = 500,
) -> List[SignalEvent]:
    """Flag quotes with excessive latency.

    Parameters
    ----------
    quotes : List[Quote]
    threshold_ms : int
        Latency threshold in milliseconds (default: 500).

    Returns
    -------
    List[SignalEvent]
        WARNING events (neutral side) for high-latency ticks.
    """
    signals: List[SignalEvent] = []
    for q in quotes:
        if q.latency_ms is not None and q.latency_ms > threshold_ms:
            signals.append(SignalEvent(
                ts_ms=q.quote_ts_ms or 0,
                side="HOLD",
                reason=f"High latency: {q.latency_ms}ms > {threshold_ms}ms threshold",
                strength=0.0,
                price=q.mid,
            ))
    return signals
