import logging
import os

import sentry_sdk
from asgiref.sync import iscoroutinefunction, markcoroutinefunction
from django.core.cache import cache
from django.http import JsonResponse
from django.urls import get_resolver
from django.utils.decorators import async_only_middleware

from aiservice.common_utils import is_codeflash_employee

RATE_LIMIT_WINDOW_MS = int(os.getenv("RATE_LIMIT_WINDOW_MS", "60000"))
RATE_LIMIT_MAX = int(os.getenv("RATE_LIMIT_MAX", "40"))


# Note: This rate limiting solution works only with a single server.
# It will not work correctly if multiple servers are used, as the cache is local to each server.
# TODO: Implement a distributed caching solution (e.g., Redis) for multi-server environments.
# TODO: ENTERPRISE FEATURE - Implement queue prioritization for premium/enterprise users
#       - Check user subscription tier from Subscriptions model (plan_type field)
#       - Premium users should bypass rate limits or have higher limits
#       - Implement priority queue: enterprise > premium > free tier
#       - Consider using asyncio.PriorityQueue or celery with priority tasks
#       - Reference: authapp/models.py:Subscriptions.plan_type, CFAPIKeys.tier


@async_only_middleware
class RateLimitMiddleware:
    def __init__(self, get_response) -> None:
        self.get_response = get_response
        if iscoroutinefunction(self.get_response):
            logging.debug("RateLimitMiddleware is async.")
            markcoroutinefunction(self)  # type: ignore[arg-type]
        self.restricted_paths = [
            "/" + str(pattern.pattern)
            for pattern in get_resolver().url_patterns
            if str(pattern.pattern).startswith("ai/")
        ]

    async def __call__(self, request):
        if any(request.path.startswith(path) for path in self.restricted_paths):
            user_id = getattr(request, "user", None)
            if not user_id:
                return JsonResponse({"error": "Authentication required for rate-limited endpoint"}, status=401)
            if is_codeflash_employee(user_id):
                return await self.get_response(request)

            path = request.path
            user_key = f"ratelimit:user:{user_id}:{path}"

            request_count_user = await cache.aget(user_key, 0)
            if request_count_user >= RATE_LIMIT_MAX:
                sentry_sdk.capture_message(
                    "Rate limit exceeded",
                    level="warning",
                    extras={"user_id": user_id, "path": path, "method": request.method, "limiterType": "user"},
                )
                return JsonResponse({"error": "Rate limit exceeded"}, status=429)

            await cache.aset(user_key, request_count_user + 1, timeout=RATE_LIMIT_WINDOW_MS // 1000)

        return await self.get_response(request)
