"""
Test feature computations — deterministic scenarios.
"""

import pytest

from senpai.md_consumer.features import (
    _percentile,
    _realized_vol,
    _vwap,
    check_signal,
    compute_features,
)
from senpai.md_consumer.models import QuoteEvent, TradeEvent
from senpai.md_consumer.state import LatestState, TradeRecord


# ── VWAP ───────────────────────────────────────────────────────────────


def test_vwap_basic():
    trades = [
        TradeRecord(price=100.0, size=10.0, ts=0),
        TradeRecord(price=200.0, size=10.0, ts=0),
    ]
    # VWAP = (100*10 + 200*10) / (10+10) = 150
    assert _vwap(trades) == 150.0


def test_vwap_weighted():
    trades = [
        TradeRecord(price=100.0, size=90.0, ts=0),
        TradeRecord(price=200.0, size=10.0, ts=0),
    ]
    # VWAP = (100*90 + 200*10) / 100 = 110
    assert _vwap(trades) == 110.0


def test_vwap_empty():
    assert _vwap([]) is None


def test_vwap_zero_volume():
    trades = [TradeRecord(price=100.0, size=0.0, ts=0)]
    assert _vwap(trades) is None


# ── Realized volatility ───────────────────────────────────────────────


def test_realized_vol_constant_price():
    """Constant price → 0 volatility."""
    trades = [TradeRecord(price=100.0, size=1.0, ts=0) for _ in range(10)]
    vol = _realized_vol(trades)
    assert vol is not None
    assert vol == 0.0


def test_realized_vol_two_prices():
    """Not enough data points → None."""
    trades = [
        TradeRecord(price=100.0, size=1.0, ts=0),
        TradeRecord(price=101.0, size=1.0, ts=0),
    ]
    assert _realized_vol(trades) is None  # needs at least 3


def test_realized_vol_positive():
    """Variable prices should give positive volatility."""
    trades = [
        TradeRecord(price=100.0, size=1.0, ts=0),
        TradeRecord(price=102.0, size=1.0, ts=0),
        TradeRecord(price=99.0, size=1.0, ts=0),
        TradeRecord(price=103.0, size=1.0, ts=0),
    ]
    vol = _realized_vol(trades)
    assert vol is not None
    assert vol > 0


# ── Percentile ─────────────────────────────────────────────────────────


def test_percentile_basic():
    data = [1.0, 2.0, 3.0, 4.0, 5.0]
    assert _percentile(data, 50) == 3.0
    assert _percentile(data, 0) == 1.0
    assert _percentile(data, 100) == 5.0


def test_percentile_p95():
    data = list(range(1, 101))  # 1..100
    data_float = [float(x) for x in data]
    p95 = _percentile(data_float, 95)
    assert 95 <= p95 <= 96


# ── Full feature computation ──────────────────────────────────────────


def test_compute_features_with_state():
    state = LatestState(window_seconds=60.0)

    # Add quote
    state.update_quote(QuoteEvent(
        provider="binance",
        symbol="BTCUSDT",
        bid=70000.0,
        ask=70002.0,
        bid_size=5.0,
        ask_size=3.0,
    ))

    # Add some trades
    for i in range(5):
        state.update_trade(TradeEvent(
            provider="binance",
            symbol="BTCUSDT",
            price=70000.0 + i * 10,
            size=1.0,
        ))

    features = compute_features(state, "BTCUSDT")

    # Mid
    assert features["mid"] == pytest.approx(70001.0)

    # Spread
    assert features["spread_abs"] == pytest.approx(2.0)
    assert features["spread_bps"] is not None
    assert features["spread_bps"] > 0

    # Trade count
    assert features["trade_count_10s"] == 5.0

    # Volume
    assert features["trade_volume_10s"] == 5.0

    # VWAP should be defined
    assert features["trade_vwap_10s"] is not None
    assert features["trade_vwap_60s"] is not None


def test_compute_features_no_data():
    state = LatestState(window_seconds=60.0)
    features = compute_features(state, "BTCUSDT")

    # All should be None
    assert features["mid"] is None
    assert features["spread_abs"] is None
    assert features["trade_vwap_10s"] is None


# ── Signal detection ──────────────────────────────────────────────────


def test_check_signal_long():
    """Strong positive return + volume + tight spread → long signal."""
    features = {
        "return_10s": 0.005,  # 0.5% (> 0.3% threshold)
        "trade_volume_10s": 5.0,  # > 1.0 threshold
        "spread_bps": 3.0,  # < 20 bps threshold
    }
    signal = check_signal(features, "BTCUSDT")
    assert signal is not None
    assert signal.direction == "long"
    assert signal.confidence > 0


def test_check_signal_short():
    """Strong negative return → short signal."""
    features = {
        "return_10s": -0.005,
        "trade_volume_10s": 5.0,
        "spread_bps": 3.0,
    }
    signal = check_signal(features, "BTCUSDT")
    assert signal is not None
    assert signal.direction == "short"


def test_check_signal_no_trigger():
    """Small return → no signal."""
    features = {
        "return_10s": 0.0001,
        "trade_volume_10s": 5.0,
        "spread_bps": 3.0,
    }
    signal = check_signal(features, "BTCUSDT")
    assert signal is None


def test_check_signal_wide_spread():
    """Wide spread → no signal (even with strong return)."""
    features = {
        "return_10s": 0.01,
        "trade_volume_10s": 5.0,
        "spread_bps": 50.0,  # > 20 bps
    }
    signal = check_signal(features, "BTCUSDT")
    assert signal is None


def test_check_signal_low_volume():
    """Low volume → no signal."""
    features = {
        "return_10s": 0.01,
        "trade_volume_10s": 0.1,  # < 1.0
        "spread_bps": 3.0,
    }
    signal = check_signal(features, "BTCUSDT")
    assert signal is None
