import asyncio
import time
from collections import deque
from typing import Callable, TypeVar

T = TypeVar("T")


class RateLimiter:
    """
    Sliding-window rate limiter.
    """

    def __init__(self, max_calls: int, period: float):
        self.max_calls = max_calls
        self.period = period
        self.calls: deque[float] = deque()
        self._lock = asyncio.Lock()

    async def acquire(self) -> None:
        async with self._lock:
            now = time.monotonic()

            while self.calls and self.calls[0] <= now - self.period:
                self.calls.popleft()

            if len(self.calls) < self.max_calls:
                self.calls.append(now)
                return

            wait_for = self.period - (now - self.calls[0])
            await asyncio.sleep(wait_for)

            now = time.monotonic()
            self.calls.append(now)

    async def run_sync(self, fn: Callable[[], T]) -> T:
        await self.acquire()
        return await asyncio.to_thread(fn)
