"""
Test publisher rate limiting.
"""
from unittest.mock import AsyncMock

import pytest

from senpai.md_consumer.publisher import Publisher
from senpai.md_consumer.models import FeatureSnapshot, TradeSignal


@pytest.fixture
def mock_nc():
    """Mock NATS client."""
    nc = AsyncMock()
    nc.publish = AsyncMock()
    return nc


@pytest.fixture
def publisher(mock_nc):
    return Publisher(mock_nc)


@pytest.mark.asyncio
async def test_publish_features_respects_rate_limit(mock_nc, publisher):
    """Second publish for same symbol within rate window should be skipped."""
    snapshot = FeatureSnapshot(
        symbol="BTCUSDT",
        features={"mid": 70000.0},
    )

    # First publish should succeed
    result1 = await publisher.publish_features(snapshot)
    assert result1 is True

    # Immediate second publish should be rate-limited
    result2 = await publisher.publish_features(snapshot)
    assert result2 is False  # rate-limited

    # Only one actual NATS publish
    assert mock_nc.publish.call_count == 1


@pytest.mark.asyncio
async def test_publish_features_different_symbols(mock_nc, publisher):
    """Different symbols have independent rate limiters."""
    snap1 = FeatureSnapshot(symbol="BTCUSDT", features={"mid": 70000.0})
    snap2 = FeatureSnapshot(symbol="ETHUSDT", features={"mid": 2000.0})

    r1 = await publisher.publish_features(snap1)
    r2 = await publisher.publish_features(snap2)

    assert r1 is True
    assert r2 is True
    assert mock_nc.publish.call_count == 2


@pytest.mark.asyncio
async def test_publish_signal_no_rate_limit(mock_nc, publisher):
    """Signals are NOT rate limited."""
    signal = TradeSignal(
        symbol="BTCUSDT",
        direction="long",
        confidence=0.8,
        reason="test",
    )

    r1 = await publisher.publish_signal(signal)
    r2 = await publisher.publish_signal(signal)

    assert r1 is True
    assert r2 is True
    assert mock_nc.publish.call_count == 2


@pytest.mark.asyncio
async def test_publish_features_after_rate_window(mock_nc, publisher):
    """After rate window passes, publish should succeed again."""
    # Override min interval to something very small for testing
    publisher._min_interval = 0.01  # 10ms

    snapshot = FeatureSnapshot(
        symbol="BTCUSDT",
        features={"mid": 70000.0},
    )

    r1 = await publisher.publish_features(snapshot)
    assert r1 is True

    # Wait for rate window to pass
    import asyncio
    await asyncio.sleep(0.02)

    r2 = await publisher.publish_features(snapshot)
    assert r2 is True
    assert mock_nc.publish.call_count == 2


@pytest.mark.asyncio
async def test_publish_handles_nats_error(mock_nc, publisher):
    """NATS publish error should not raise, just return False."""
    mock_nc.publish.side_effect = Exception("NATS down")

    snapshot = FeatureSnapshot(
        symbol="BTCUSDT",
        features={"mid": 70000.0},
    )

    result = await publisher.publish_features(snapshot)
    assert result is False
