"""Regime detection signal for hz.run().

Classifies market regime (calm vs volatile/trending) using rolling volatility,
momentum, or a composite of both. Works standalone or inside signal_combiner.

Usage:
    hz.run(
        pipeline=[
            hz.signal_combiner([
                hz.regime_signal("btc", method="composite", weight=0.3),
                hz.price_signal("btc", weight=0.7),
            ]),
            quoter,
        ],
        ...
    )
"""

from __future__ import annotations

from collections import deque

from horizon._horizon import estimate_volatility
from horizon.context import Context


def _vol_regime(prices: list[float], low: float, high: float) -> float:
    """Compute volatility regime score 0-1 from price history."""
    if len(prices) < 3:
        return 0.0
    vol = estimate_volatility(prices)
    if vol <= low:
        return 0.0
    if vol >= high:
        return 1.0
    return (vol - low) / (high - low)


def _trend_regime(
    prices: list[float], momentum_lookback: int, low: float, high: float
) -> float:
    """Compute trend regime score 0-1 from price momentum + volatility."""
    if len(prices) < 2:
        return 0.0
    # Momentum: absolute return over lookback window
    lb = min(momentum_lookback, len(prices) - 1)
    if lb <= 0:
        return 0.0
    ret = abs(prices[-1] - prices[-lb - 1]) / prices[-lb - 1] if prices[-lb - 1] > 0 else 0.0
    # Normalize: 5% move → ~1.0
    momentum_score = min(1.0, ret * 20.0)
    # Blend with volatility score
    vol_score = _vol_regime(prices, low, high)
    return min(1.0, (momentum_score + vol_score) / 2.0)


def regime_signal(
    feed_name: str,
    method: str = "composite",
    lookback: int = 50,
    vol_threshold_low: float = 0.005,
    vol_threshold_high: float = 0.05,
    momentum_lookback: int = 20,
    weight: float = 1.0,
) -> "Signal":
    """Create a regime detection signal for use in signal_combiner.

    Args:
        feed_name: Name of the feed to read prices from.
        method: "volatility", "trend", or "composite" (0.6*vol + 0.4*trend).
        lookback: Rolling window size for price history.
        vol_threshold_low: Volatility below this = calm (score 0).
        vol_threshold_high: Volatility above this = volatile (score 1).
        momentum_lookback: Lookback for trend momentum calculation.
        weight: Signal weight for combination.

    Returns:
        Signal object compatible with signal_combiner.
    """
    from horizon.signals import Signal

    price_histories: dict[str, deque[float]] = {}

    def _extract(ctx: Context) -> float:
        fd = ctx.feeds.get(feed_name)
        if fd is None:
            return 0.0
        price = fd.price if fd.price > 0 else (fd.bid + fd.ask) / 2.0
        if price <= 0:
            return 0.0

        market_id = (
            getattr(ctx.market, "id", "__default__") if ctx.market else "__default__"
        )
        if market_id not in price_histories:
            price_histories[market_id] = deque(maxlen=lookback)
        history = price_histories[market_id]
        history.append(price)

        prices = list(history)

        if method == "volatility":
            return _vol_regime(prices, vol_threshold_low, vol_threshold_high)
        elif method == "trend":
            return _trend_regime(
                prices, momentum_lookback, vol_threshold_low, vol_threshold_high
            )
        else:  # composite
            vol_score = _vol_regime(prices, vol_threshold_low, vol_threshold_high)
            trend_score = _trend_regime(
                prices, momentum_lookback, vol_threshold_low, vol_threshold_high
            )
            return min(1.0, 0.6 * vol_score + 0.4 * trend_score)

    _extract.__name__ = "regime_signal"
    return Signal(name=f"regime:{feed_name}", fn=_extract, weight=weight)
