"""请求调度器(§5.6)。

按 capability 分别维护令牌桶。Phase 0:基础实现;Phase 1 接入批量合并、优先级队列。
"""
from __future__ import annotations

import asyncio
import time
from dataclasses import dataclass

from .capabilities import Cap, CapabilitySet


@dataclass
class _Bucket:
    capacity: int        # 每分钟令牌数
    tokens: float
    last_refill: float   # 单位:秒

    def consume(self, n: int = 1) -> float:
        """尝试消费 n 个令牌,返回需要等待的秒数(0 表示无需等待)。"""
        now = time.monotonic()
        elapsed = now - self.last_refill
        # 60s 内补满 capacity,匀速补
        refill = elapsed * (self.capacity / 60.0)
        self.tokens = min(self.capacity, self.tokens + refill)
        self.last_refill = now

        if self.tokens >= n:
            self.tokens -= n
            return 0.0
        deficit = n - self.tokens
        # 还需多少秒才能补齐
        wait = deficit / (self.capacity / 60.0)
        # 不预扣,留给下一次再竞争(避免饿死优先级高的请求)
        return wait


class Scheduler:
    """每个 capability 一个桶。"""

    def __init__(self, capset: CapabilitySet) -> None:
        self._capset = capset
        self._buckets: dict[Cap, _Bucket] = {}
        self._locks: dict[Cap, asyncio.Lock] = {}
        for cap, lim in capset.all().items():
            if lim.rpm:
                self._buckets[cap] = _Bucket(
                    capacity=lim.rpm,
                    tokens=lim.rpm,
                    last_refill=time.monotonic(),
                )
                self._locks[cap] = asyncio.Lock()

    async def acquire(self, cap: Cap, n: int = 1) -> None:
        """阻塞直到拿到 n 个令牌。无桶 = 不限流(由调用方保证)。"""
        bucket = self._buckets.get(cap)
        if bucket is None:
            return
        lock = self._locks[cap]
        async with lock:
            while True:
                wait = bucket.consume(n)
                if wait == 0:
                    return
                await asyncio.sleep(wait)
