import time

from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.responses import JSONResponse

from app.config import settings
from app.utils.redis import get_redis


RATE_LIMITS = {
    "POST:/api/auth/login": (10, 60),
    "POST:/api/auth/change-password": (5, 60),
    "POST:/api/generation-records/optimize": (20, 60),
    "POST:/api/generation-records/*/generate": (10, 60),
    "POST:/api/generation-records/*/retry": (10, 60),
}


def _match_rate_limit(path: str, method: str) -> tuple[int, int] | None:
    key = f"{method}:{path}"
    if key in RATE_LIMITS:
        return RATE_LIMITS[key]
    # Check wildcard patterns
    for pattern, limit in RATE_LIMITS.items():
        pattern_method, pattern_path = pattern.split(":", 1)
        if method != pattern_method:
            continue
        pattern_parts = pattern_path.split("/")
        key_parts = path.split("/")
        if len(pattern_parts) != len(key_parts):
            continue
        match = True
        for pp, kp in zip(pattern_parts, key_parts):
            if pp != "*" and pp != kp:
                match = False
                break
        if match:
            return limit
    return None


class RateLimitMiddleware(BaseHTTPMiddleware):
    async def dispatch(
        self, request: Request, call_next: RequestResponseEndpoint
    ) -> Response:
        if not settings.RATE_LIMIT_ENABLED:
            return await call_next(request)

        limit_info = _match_rate_limit(request.url.path, request.method)
        if not limit_info:
            return await call_next(request)

        max_requests, window = limit_info
        # Use user_id from token or IP as identifier
        identifier = request.client.host if request.client else "unknown"

        redis = get_redis()
        if not redis:
            return await call_next(request)

        redis_key = f"ratelimit:{request.method}:{request.url.path}:{identifier}"

        try:
            current = await redis.incr(redis_key)
            if current == 1:
                await redis.expire(redis_key, window)
            if current > max_requests:
                ttl = await redis.ttl(redis_key)
                return JSONResponse(
                    status_code=429,
                    content={"detail": "请求过于频繁，请稍后重试"},
                    headers={"Retry-After": str(max(ttl, 1))},
                )
        except Exception:
            pass  # Redis unavailable, skip rate limiting

        return await call_next(request)
