"""
Test state management — LatestState and RollingWindow.
"""
import time


from senpai.md_consumer.state import (
    LatestState,
    RollingWindow,
    TradeRecord,
)
from senpai.md_consumer.models import TradeEvent, QuoteEvent


# ── RollingWindow ──────────────────────────────────────────────────────


def test_rolling_window_add_trade():
    w = RollingWindow(window_seconds=60.0)
    t = TradeRecord(price=100.0, size=1.0, ts=time.monotonic())
    w.add_trade(t)
    assert len(w.trades) == 1
    assert w.trades[0].price == 100.0


def test_rolling_window_eviction():
    """Old records should be evicted."""
    w = RollingWindow(window_seconds=1.0)  # 1 second window

    old_ts = time.monotonic() - 2.0  # 2 seconds ago
    w.add_trade(TradeRecord(price=100.0, size=1.0, ts=old_ts))
    w.add_trade(TradeRecord(price=200.0, size=2.0, ts=time.monotonic()))

    # Old record should be evicted
    trades = list(w.trades)
    assert len(trades) == 1
    assert trades[0].price == 200.0


def test_rolling_window_trades_since():
    w = RollingWindow(window_seconds=60.0)
    now = time.monotonic()

    # Add trades at different times
    w.add_trade(TradeRecord(price=100.0, size=1.0, ts=now - 30))  # 30s ago
    w.add_trade(TradeRecord(price=200.0, size=2.0, ts=now - 5))  # 5s ago
    w.add_trade(TradeRecord(price=300.0, size=3.0, ts=now))  # now

    last_10s = w.trades_since(10.0)
    assert len(last_10s) == 2  # 5s ago + now
    assert last_10s[0].price == 200.0


def test_rolling_window_empty():
    w = RollingWindow(window_seconds=60.0)
    assert len(w.trades) == 0
    assert len(w.quotes) == 0
    assert w.trades_since(10.0) == []


# ── LatestState ────────────────────────────────────────────────────────


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

    event = TradeEvent(
        provider="binance",
        symbol="BTCUSDT",
        price=70500.0,
        size=1.5,
        side="buy",
    )
    state.update_trade(event)

    latest = state.get_latest_trade("BTCUSDT")
    assert latest is not None
    assert latest.price == 70500.0
    assert latest.side == "buy"
    assert state.event_count == 1


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

    event = QuoteEvent(
        provider="binance",
        symbol="BTCUSDT",
        bid=70000.0,
        ask=70001.0,
        bid_size=5.0,
        ask_size=3.0,
    )
    state.update_quote(event)

    latest = state.get_latest_quote("BTCUSDT")
    assert latest is not None
    assert latest.bid == 70000.0
    assert latest.ask == 70001.0


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

    state.update_trade(TradeEvent(
        provider="binance", symbol="BTCUSDT", price=100.0, size=1.0
    ))
    state.update_quote(QuoteEvent(
        provider="binance", symbol="ETHUSDT",
        bid=2000.0, ask=2001.0, bid_size=1.0, ask_size=1.0,
    ))

    assert "BTCUSDT" in state.symbols
    assert "ETHUSDT" in state.symbols


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

    state.update_trade(TradeEvent(
        provider="binance", symbol="BTCUSDT", price=70500.0, size=1.0
    ))
    state.update_quote(QuoteEvent(
        provider="binance", symbol="BTCUSDT",
        bid=70000.0, ask=70001.0, bid_size=1.0, ask_size=1.0,
    ))

    d = state.to_dict("BTCUSDT")
    assert d["symbol"] == "BTCUSDT"
    assert "latest_trade" in d
    assert "latest_quote" in d
    assert d["latest_trade"]["price"] == 70500.0


def test_latest_state_missing_symbol():
    state = LatestState(window_seconds=60.0)
    assert state.get_latest_trade("NOPE") is None
    assert state.get_latest_quote("NOPE") is None
