"""In-process sliding-window rate limiter for the auth endpoints.

Single-process only (Passenger/WSGI or one uvicorn worker). Under Docker Compose
the edge proxy owns rate limiting instead; these limits are the backstop for
deployments that run without one.
"""
import time
from collections import deque


class SlidingWindowLimiter:
    """Allows `limit + burst` hits per `window_seconds` per key."""

    def __init__(self, limit: int, burst: int, window_seconds: float = 60.0, max_keys: int = 10_000):
        self.limit = limit
        self.burst = burst
        self.window_seconds = window_seconds
        self.max_keys = max_keys
        self._hits: dict[str, deque[float]] = {}

    def allow(self, key: str) -> bool:
        now = time.monotonic()
        cutoff = now - self.window_seconds
        hits = self._hits.get(key)
        if hits is None:
            if len(self._hits) >= self.max_keys:
                self._prune(cutoff)
            hits = deque()
            self._hits[key] = hits
        while hits and hits[0] <= cutoff:
            hits.popleft()
        if len(hits) >= self.limit + self.burst:
            return False
        hits.append(now)
        return True

    def _prune(self, cutoff: float) -> None:
        for key in [k for k, h in self._hits.items() if not h or h[-1] <= cutoff]:
            del self._hits[key]
