from __future__ import annotations

import time
from dataclasses import dataclass, field
from typing import Dict, Tuple

from fastapi import HTTPException, Request


@dataclass
class FixedWindowRateLimiter:
    """
    固定窗口限流（内存版）。

    约束：
    - 适用于单进程/单实例；多进程/多实例下不共享计数（V1 可接受）
    - 窗口粒度：按分钟 bucket（window_seconds 建议为 60）
    """

    limit: int
    window_seconds: int
    _counters: Dict[Tuple[str, int], int] = field(default_factory=dict)
    _last_gc_bucket: int = 0

    def _bucket(self, now_ts: float) -> int:
        return int(now_ts // float(self.window_seconds))

    def _gc(self, current_bucket: int) -> None:
        # 每隔一段时间清理一次，避免 dict 无限增长（保留最近 3 个 bucket）
        if self._last_gc_bucket == current_bucket:
            return
        self._last_gc_bucket = current_bucket
        keep_from = current_bucket - 2
        to_delete = [k for k in self._counters.keys() if k[1] < keep_from]
        for k in to_delete:
            self._counters.pop(k, None)

    def allow(self, *, key: str, now_ts: float) -> None:
        bucket = self._bucket(now_ts)
        self._gc(bucket)

        k = (str(key), int(bucket))
        n = int(self._counters.get(k, 0)) + 1
        self._counters[k] = n
        if n > int(self.limit):
            raise HTTPException(status_code=429, detail="rate_limited")


_reco_rate_limiter = FixedWindowRateLimiter(limit=10, window_seconds=60)


async def rate_limit_reco_by_ip(request: Request) -> None:
    """
    推荐接口限流：按 IP，1 分钟 10 次。
    """

    ip = "unknown"
    if request.client and request.client.host:
        ip = str(request.client.host)

    _reco_rate_limiter.allow(key=ip, now_ts=time.time())

