from __future__ import annotations

import time
from pathlib import Path

import pytest

from src.rate_limiter import RateLimit, RateLimiter


@pytest.mark.asyncio
async def test_acquire_waits_for_refill_without_deadlock() -> None:
    limiter = RateLimiter()
    limiter._meta_limits["insights"] = RateLimit(  # pylint: disable=protected-access
        requests_per_hour=3600,
        requests_per_minute=60,
        burst_limit=1,
    )

    await limiter.acquire("act_123/insights", timeout=2.0)

    started = time.perf_counter()
    info = await limiter.acquire("act_123/insights", timeout=2.0)
    elapsed = time.perf_counter() - started

    assert info.remaining >= 0
    assert 0.8 <= elapsed < 2.0


def test_update_limits_from_headers_rejects_code_execution_payload() -> None:
    limiter = RateLimiter()
    sentinel = Path("tests") / ".tmp_rate_limiter_pwned.txt"
    if sentinel.exists():
        sentinel.unlink()
    payload = f"__import__('os').system('echo pwned > {sentinel.as_posix()}')"

    limiter.update_limits_from_headers({"x-app-usage": payload})

    assert not sentinel.exists()


def test_update_limits_from_headers_safely_adjusts_and_recovers_limits() -> None:
    limiter = RateLimiter()
    baseline_rpm = limiter._meta_limits["insights"].requests_per_minute  # pylint: disable=protected-access

    limiter.update_limits_from_headers(
        {"x-app-usage": '{"call_count": 90, "total_time": 20, "total_cputime": 10}'}
    )
    reduced_rpm = limiter._meta_limits["insights"].requests_per_minute  # pylint: disable=protected-access
    assert reduced_rpm < baseline_rpm

    limiter.update_limits_from_headers(
        {"x-app-usage": '{"call_count": 10, "total_time": 10, "total_cputime": 5}'}
    )
    restored_rpm = limiter._meta_limits["insights"].requests_per_minute  # pylint: disable=protected-access
    assert restored_rpm == baseline_rpm


@pytest.mark.asyncio
async def test_acquire_refreshes_existing_bucket_after_dynamic_limit_change() -> None:
    limiter = RateLimiter()
    endpoint = "act_123/insights"

    await limiter.acquire(endpoint, timeout=2.0)
    bucket_key = limiter._get_bucket_key("insights", None)  # pylint: disable=protected-access
    bucket_before = limiter._buckets[bucket_key]  # pylint: disable=protected-access
    initial_capacity = bucket_before.capacity

    limiter.update_limits_from_headers(
        {"x-app-usage": '{"call_count": 99, "total_time": 99, "total_cputime": 99}'}
    )

    await limiter.acquire(endpoint, timeout=2.0)
    bucket_after = limiter._buckets[bucket_key]  # pylint: disable=protected-access
    assert bucket_after.capacity < initial_capacity
