"""Tests for regime detection signal."""

import os
import pytest

os.environ.setdefault("HORIZON_API_KEY", "test-key-123")

from horizon._horizon import Market
from horizon.context import Context, FeedData, InventorySnapshot
from horizon.regime import regime_signal, _vol_regime, _trend_regime
from horizon.signals import Signal, signal_combiner


def _make_ctx(feed_name="btc", price=0.0, bid=0.0, ask=0.0, market_id=None):
    feeds = {feed_name: FeedData(price=price, bid=bid, ask=ask, timestamp=1.0)}
    market = Market(id=market_id, name=market_id, slug=market_id) if market_id else None
    return Context(feeds=feeds, inventory=InventorySnapshot(), market=market)


class TestVolRegime:
    def test_low_vol_returns_zero(self):
        # Stable prices → low vol → 0
        prices = [0.50] * 20
        assert _vol_regime(prices, 0.005, 0.05) == 0.0

    def test_high_vol_returns_one(self):
        # Wild swings → high vol → 1
        prices = [0.3, 0.7, 0.3, 0.7, 0.3, 0.7, 0.3, 0.7]
        score = _vol_regime(prices, 0.005, 0.05)
        assert score == 1.0

    def test_insufficient_data(self):
        assert _vol_regime([0.5, 0.6], 0.005, 0.05) == 0.0

    def test_intermediate_vol(self):
        # Moderate moves
        prices = [0.50, 0.51, 0.50, 0.51, 0.50, 0.51]
        score = _vol_regime(prices, 0.001, 0.1)
        assert 0.0 <= score <= 1.0


class TestTrendRegime:
    def test_no_trend_returns_low(self):
        prices = [0.50] * 20
        score = _trend_regime(prices, 10, 0.005, 0.05)
        assert score == 0.0

    def test_strong_trend(self):
        # 10% move up
        prices = [0.40 + i * 0.01 for i in range(20)]
        score = _trend_regime(prices, 10, 0.001, 0.1)
        assert score > 0.0

    def test_insufficient_data(self):
        assert _trend_regime([0.5], 10, 0.005, 0.05) == 0.0


class TestRegimeSignal:
    def test_returns_signal_object(self):
        sig = regime_signal("btc")
        assert isinstance(sig, Signal)
        assert sig.name == "regime:btc"

    def test_volatility_method(self):
        sig = regime_signal("btc", method="volatility", lookback=10)
        # Calm market
        for p in [0.50] * 10:
            ctx = _make_ctx(price=p)
            val = sig.fn(ctx)
        assert val == 0.0

    def test_trend_method(self):
        sig = regime_signal("btc", method="trend", lookback=20, momentum_lookback=5)
        for p in [0.50] * 10:
            ctx = _make_ctx(price=p)
            val = sig.fn(ctx)
        assert val == 0.0

    def test_composite_method(self):
        sig = regime_signal("btc", method="composite", lookback=20)
        for p in [0.50] * 15:
            ctx = _make_ctx(price=p)
            val = sig.fn(ctx)
        assert 0.0 <= val <= 1.0

    def test_volatile_market_scores_high(self):
        sig = regime_signal(
            "btc", method="volatility", lookback=20,
            vol_threshold_low=0.001, vol_threshold_high=0.05,
        )
        prices = [0.3, 0.7, 0.3, 0.7, 0.3, 0.7, 0.3, 0.7, 0.3, 0.7]
        for p in prices:
            ctx = _make_ctx(price=p)
            val = sig.fn(ctx)
        assert val > 0.5

    def test_missing_feed_returns_zero(self):
        sig = regime_signal("missing_feed")
        ctx = _make_ctx(feed_name="btc", price=0.5)
        assert sig.fn(ctx) == 0.0

    def test_zero_price_returns_zero(self):
        sig = regime_signal("btc")
        ctx = _make_ctx(price=0.0)
        assert sig.fn(ctx) == 0.0

    def test_uses_bid_ask_if_no_price(self):
        sig = regime_signal("btc", lookback=10)
        for _ in range(5):
            ctx = _make_ctx(price=0.0, bid=0.45, ask=0.55)
            val = sig.fn(ctx)
        # Should have used (bid+ask)/2 = 0.5
        assert val >= 0.0

    def test_per_market_isolation(self):
        sig = regime_signal("btc", method="volatility", lookback=10,
                           vol_threshold_low=0.001, vol_threshold_high=0.05)
        # Market A: calm
        for p in [0.50] * 10:
            ctx = _make_ctx(price=p, market_id="mkt_a")
            val_a = sig.fn(ctx)

        # Market B: volatile
        for p in [0.3, 0.7, 0.3, 0.7, 0.3, 0.7, 0.3, 0.7]:
            ctx = _make_ctx(price=p, market_id="mkt_b")
            val_b = sig.fn(ctx)

        assert val_a < val_b

    def test_works_with_signal_combiner(self):
        sig = regime_signal("btc", weight=0.5)
        combiner = signal_combiner([sig])
        for _ in range(5):
            ctx = _make_ctx(price=0.5)
            result = combiner(ctx)
        assert 0.0 <= result <= 1.0

    def test_custom_weight(self):
        sig = regime_signal("btc", weight=2.0)
        assert sig.weight == 2.0
