# API 速率限制
# 提供灵活的API请求频率限制功能
# 支持按IP、按用户、按API端点限制

import logging
import time
from functools import wraps
from django.core.cache import cache
from django.http import JsonResponse
from django.conf import settings

logger = logging.getLogger(__name__)


class RateLimitExceeded(Exception):
    """速率限制超出异常"""
    pass


class RateLimiter:
    """速率限制器"""

    # 默认限制配置
    DEFAULT_LIMITS = {
        'default': {'requests': 100, 'window': 60},  # 100次/分钟
        'strict': {'requests': 10, 'window': 60},    # 10次/分钟
        'loose': {'requests': 1000, 'window': 60},   # 1000次/分钟
    }

    @classmethod
    def get_client_ip(cls, request):
        """获取客户端IP地址"""
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0].strip()
        else:
            ip = request.META.get('REMOTE_ADDR', '')
        return ip

    @classmethod
    def get_cache_key(cls, identifier, scope='default'):
        """
        生成缓存键

        Args:
            identifier: 标识符（IP、用户ID等）
            scope: 作用域（API端点名称等）

        Returns:
            str: 缓存键
        """
        return f"rate_limit:{scope}:{identifier}"

    @classmethod
    def is_rate_limited(cls, identifier, limit_config, scope='default'):
        """
        检查是否超过速率限制

        Args:
            identifier: 标识符
            limit_config: 限制配置 {'requests': 100, 'window': 60}
            scope: 作用域

        Returns:
            tuple: (是否限制, 剩余请求数, 重置时间)
        """
        requests_limit = limit_config['requests']
        time_window = limit_config['window']

        cache_key = cls.get_cache_key(identifier, scope)

        # 获取当前窗口的请求记录
        current_time = int(time.time())
        window_start = current_time - time_window

        # 使用列表存储请求时间戳
        request_times = cache.get(cache_key, [])

        # 过滤掉窗口外的请求
        request_times = [t for t in request_times if t > window_start]

        # 检查是否超限
        if len(request_times) >= requests_limit:
            # 计算重置时间
            oldest_request = min(request_times)
            reset_time = oldest_request + time_window
            remaining = 0
            is_limited = True
        else:
            # 添加当前请求
            request_times.append(current_time)
            remaining = requests_limit - len(request_times)
            reset_time = current_time + time_window
            is_limited = False

        # 更新缓存
        cache.set(cache_key, request_times, time_window + 10)

        return is_limited, remaining, reset_time

    @classmethod
    def get_limit_config(cls, limit_name='default'):
        """
        获取限制配置

        Args:
            limit_name: 限制名称或自定义配置

        Returns:
            dict: 限制配置
        """
        if isinstance(limit_name, dict):
            return limit_name

        # 从设置中获取
        custom_limits = getattr(settings, 'RATE_LIMITS', {})

        if limit_name in custom_limits:
            return custom_limits[limit_name]

        if limit_name in cls.DEFAULT_LIMITS:
            return cls.DEFAULT_LIMITS[limit_name]

        # 默认限制
        return cls.DEFAULT_LIMITS['default']


def rate_limit(limit='default', key_func=None, scope=None):
    """
    API速率限制装饰器

    Args:
        limit: 限制配置名称或字典 {'requests': 100, 'window': 60}
        key_func: 自定义键函数 func(request) -> str
        scope: 作用域名称（默认使用视图函数名）

    使用示例:
        @rate_limit(limit='strict')
        def my_api_view(request):
            ...

        @rate_limit(limit={'requests': 50, 'window': 60})
        def another_view(request):
            ...

        @rate_limit(key_func=lambda req: req.user.id, scope='user_actions')
        def user_api(request):
            ...
    """
    def decorator(view_func):
        @wraps(view_func)
        def wrapped_view(request, *args, **kwargs):
            # 获取限制配置
            limit_config = RateLimiter.get_limit_config(limit)

            # 确定作用域
            view_scope = scope or view_func.__name__

            # 确定标识符
            if key_func:
                identifier = key_func(request)
            elif request.user.is_authenticated:
                # 已登录用户使用用户ID
                identifier = f"user_{request.user.id}"
            else:
                # 未登录用户使用IP
                identifier = f"ip_{RateLimiter.get_client_ip(request)}"

            # 检查速率限制
            is_limited, remaining, reset_time = RateLimiter.is_rate_limited(
                identifier,
                limit_config,
                view_scope
            )

            # 添加速率限制头
            response_headers = {
                'X-RateLimit-Limit': str(limit_config['requests']),
                'X-RateLimit-Remaining': str(remaining),
                'X-RateLimit-Reset': str(reset_time),
            }

            if is_limited:
                # 超过限制
                retry_after = reset_time - int(time.time())

                logger.warning(
                    f"Rate limit exceeded for {identifier} "
                    f"on {view_scope}: {limit_config}"
                )

                response = JsonResponse({
                    'success': False,
                    'error': 'rate_limit_exceeded',
                    'message': f'请求过于频繁，请在 {retry_after} 秒后重试',
                    'retry_after': retry_after
                }, status=429)

                response_headers['Retry-After'] = str(retry_after)

                # 设置响应头
                for header, value in response_headers.items():
                    response[header] = value

                return response

            # 执行视图
            response = view_func(request, *args, **kwargs)

            # 添加速率限制头到响应
            for header, value in response_headers.items():
                response[header] = value

            return response

        return wrapped_view
    return decorator


def ip_rate_limit(limit='default', scope=None):
    """
    基于IP的速率限制装饰器

    Args:
        limit: 限制配置
        scope: 作用域

    示例:
        @ip_rate_limit(limit='strict')
        def api_view(request):
            ...
    """
    return rate_limit(
        limit=limit,
        key_func=lambda req: f"ip_{RateLimiter.get_client_ip(req)}",
        scope=scope
    )


def user_rate_limit(limit='default', scope=None):
    """
    基于用户的速率限制装饰器

    Args:
        limit: 限制配置
        scope: 作用域

    示例:
        @user_rate_limit(limit={'requests': 50, 'window': 3600})
        def api_view(request):
            ...
    """
    def key_func(request):
        if request.user.is_authenticated:
            return f"user_{request.user.id}"
        # 未登录用户降级到IP限制
        return f"ip_{RateLimiter.get_client_ip(request)}"

    return rate_limit(
        limit=limit,
        key_func=key_func,
        scope=scope
    )


# ==================== 中间件 ====================

class RateLimitMiddleware:
    """
    全局速率限制中间件

    在 settings.py 中配置:
        MIDDLEWARE = [
            ...
            'blog.rate_limit.RateLimitMiddleware',
        ]

        RATE_LIMIT_ENABLED = True
        GLOBAL_RATE_LIMIT = {'requests': 1000, 'window': 60}
    """

    def __init__(self, get_response):
        self.get_response = get_response
        self.enabled = getattr(settings, 'RATE_LIMIT_ENABLED', False)
        self.limit = getattr(settings, 'GLOBAL_RATE_LIMIT', {'requests': 1000, 'window': 60})

    def __call__(self, request):
        if self.enabled and request.path.startswith('/blog/api/'):
            # 只对API请求进行全局限制
            identifier = f"global_ip_{RateLimiter.get_client_ip(request)}"

            is_limited, remaining, reset_time = RateLimiter.is_rate_limited(
                identifier,
                self.limit,
                'global'
            )

            if is_limited:
                retry_after = reset_time - int(time.time())

                response = JsonResponse({
                    'success': False,
                    'error': 'rate_limit_exceeded',
                    'message': '全局请求频率超限，请稍后再试',
                    'retry_after': retry_after
                }, status=429)

                response['X-RateLimit-Limit'] = str(self.limit['requests'])
                response['X-RateLimit-Remaining'] = '0'
                response['X-RateLimit-Reset'] = str(reset_time)
                response['Retry-After'] = str(retry_after)

                return response

        response = self.get_response(request)
        return response
