"""Unit tests for app/core/rate_limiter.py — burst protection and rate_limited_response."""

import pytest


class TestRateLimitMiddleware:
    """Tests for RateLimitMiddleware utility methods (no DB/Redis)."""

    @pytest.mark.asyncio
    async def test_burst_allows_first_requests(self):
        """30 calls in quick succession all return True."""
        from app.core.rate_limiter import RateLimitMiddleware

        mw = RateLimitMiddleware(object())
        for _ in range(30):
            assert mw._check_burst("test_burst_allows") is True

    @pytest.mark.asyncio
    async def test_burst_rejects_31st(self):
        """31st call after 30 fast calls returns False."""
        from app.core.rate_limiter import RateLimitMiddleware

        mw = RateLimitMiddleware(object())
        for _ in range(30):
            assert mw._check_burst("test_burst_rejects") is True
        assert mw._check_burst("test_burst_rejects") is False

    @pytest.mark.asyncio
    async def test_burst_window_expires(self):
        """After 1+ second, old entries expire and new calls allowed."""
        from app.core.rate_limiter import RateLimitMiddleware
        import time
        from unittest.mock import patch

        mw = RateLimitMiddleware(object())
        with patch("app.core.rate_limiter.time.time") as mock_time:
            mock_time.return_value = 1000.0
            for _ in range(30):
                assert mw._check_burst("test2") is True
            assert mw._check_burst("test2") is False  # 31st blocked
            # Advance past window
            mock_time.return_value = 1001.5
            assert mw._check_burst("test2") is True  # window expired

    @pytest.mark.asyncio
    async def test_burst_isolates_tenants(self):
        """Different tenant IDs have independent windows."""
        from app.core.rate_limiter import RateLimitMiddleware
        from unittest.mock import patch

        mw = RateLimitMiddleware(object())
        with patch("app.core.rate_limiter.time.time") as mock_time:
            mock_time.return_value = 1000.0
            for _ in range(30):
                assert mw._check_burst("tenant_a") is True
            assert mw._check_burst("tenant_a") is False  # tenant_a capped
            assert mw._check_burst("tenant_b") is True  # tenant_b unaffected

    @pytest.mark.asyncio
    async def test_rate_limited_response_status(self):
        """Returns 429 status code."""
        from app.core.rate_limiter import RateLimitMiddleware

        mw = RateLimitMiddleware(object())
        resp = mw._rate_limited_response(limit=100, remaining=0)
        assert resp.status_code == 429

    @pytest.mark.asyncio
    async def test_rate_limited_response_headers(self):
        """Has Retry-After: 86400, X-RateLimit-Limit, X-RateLimit-Remaining."""
        from app.core.rate_limiter import RateLimitMiddleware

        mw = RateLimitMiddleware(object())
        resp = mw._rate_limited_response(limit=1000, remaining=0)
        assert resp.headers.get("Retry-After") == "86400"
        assert resp.headers.get("X-RateLimit-Limit") == "1000"
        assert resp.headers.get("X-RateLimit-Remaining") == "0"

    @pytest.mark.asyncio
    async def test_rate_limited_response_body(self):
        """Body has success=False, error.code=RATE_LIMITED."""
        from app.core.rate_limiter import RateLimitMiddleware

        mw = RateLimitMiddleware(object())
        resp = mw._rate_limited_response(limit=100, remaining=0)
        body = resp.body
        import json

        data = json.loads(body)
        assert data["success"] is False
        assert data["error"]["code"] == "RATE_LIMITED"

    @pytest.mark.asyncio
    async def test_check_burst_empty_window(self):
        """Fresh tid returns True."""
        from app.core.rate_limiter import RateLimitMiddleware

        mw = RateLimitMiddleware(object())
        assert mw._check_burst("brand_new_tenant") is True
