from __future__ import annotations

import threading
import time


class TokenBucket:
    def __init__(self, rate_per_minute: float, capacity: float | None = None, clock=time.monotonic) -> None:
        self.rate_per_second = max(0.001, rate_per_minute / 60.0)
        self.capacity = float(capacity if capacity is not None else rate_per_minute)
        self._tokens = self.capacity
        self._updated = clock()
        self._clock = clock
        self._lock = threading.Lock()

    def acquire(self, tokens: float = 1.0, block: bool = True) -> bool:
        while True:
            with self._lock:
                now = self._clock()
                elapsed = max(0.0, now - self._updated)
                self._tokens = min(self.capacity, self._tokens + elapsed * self.rate_per_second)
                self._updated = now
                if self._tokens >= tokens:
                    self._tokens -= tokens
                    return True
                wait = (tokens - self._tokens) / self.rate_per_second
            if not block:
                return False
            time.sleep(min(wait, 0.05))

    @property
    def remaining(self) -> float:
        with self._lock:
            now = self._clock()
            elapsed = max(0.0, now - self._updated)
            return min(self.capacity, self._tokens + elapsed * self.rate_per_second)
