"""
security/middleware.py
=======================
Middlewares de seguridad propios de EduPlay.

1. RateLimitMiddleware:
   Bloquea IPs que hacen demasiadas peticiones a endpoints sensibles.
   Más granular que el throttling de DRF: actúa antes de que Django
   procese el request.

2. AuditMiddleware:
   Registra en AuditLog las acciones críticas que pasan por HTTP.
   Los WebSockets se auditan directamente en el consumer.

3. SessionUniquenessMiddleware:
   Verifica que el JWT pertenece a la sesión activa del usuario.
   Previene que tokens robados funcionen si el usuario cerró sesión.
"""

import logging
from django.http import JsonResponse
from django.utils import timezone
from django.utils.deprecation import MiddlewareMixin

logger = logging.getLogger('security')

# Endpoints donde el rate limit es más estricto (autenticación)
RATE_LIMIT_ENDPOINTS = {
    '/api/v1/auth/login/':             (5, 600),   # 5 intentos / 10 min
    '/api/v1/auth/register/alumno/':   (10, 600),  # 10 intentos / 10 min
    '/api/v1/auth/register/docente/':  (5, 3600),  # 5 intentos / hora
}


def _get_client_ip(request) -> str:
    forwarded = request.META.get('HTTP_X_FORWARDED_FOR')
    return forwarded.split(',')[0].strip() if forwarded else request.META.get('REMOTE_ADDR', '')


class RateLimitMiddleware(MiddlewareMixin):
    """
    Rate limiting por IP para endpoints de autenticación.
    Usa la BD para persistir contadores entre procesos.
    Para producción de alto volumen: migrar a Redis counters.
    """

    def process_request(self, request):
        path = request.path

        # Solo aplicar en endpoints configurados y solo en POST
        if path not in RATE_LIMIT_ENDPOINTS or request.method != 'POST':
            return None

        max_attempts, window_sec = RATE_LIMIT_ENDPOINTS[path]
        ip = _get_client_ip(request)

        try:
            from security.models import IPBlocklist
            from datetime import timedelta

            # Verificar si la IP está bloqueada
            bloqueo = IPBlocklist.objects.filter(
                ip=ip, bloqueada_hasta__gt=timezone.now()
            ).first()

            if bloqueo:
                logger.warning(f'IP bloqueada intentando acceder: {ip} → {path}')
                return JsonResponse({
                    'success': False,
                    'message': 'Demasiados intentos. Por favor espera antes de intentar de nuevo.',
                    'data': None, 'errors': None,
                }, status=429)

        except Exception as e:
            # Si la BD no está disponible, dejar pasar (fail open)
            logger.error(f'RateLimit middleware error: {e}')

        return None


class AuditMiddleware(MiddlewareMixin):
    """
    Registra acciones críticas en AuditLog.
    Solo actúa en respuestas exitosas de endpoints específicos.
    """

    # Mapeo endpoint → acción de auditoría (método, path) → acción
    AUDIT_MAP = {
        ('POST', '/api/v1/auth/login/'):            'LOGIN_OK',
        ('POST', '/api/v1/auth/logout/'):           'LOGOUT',
        ('POST', '/api/v1/auth/register/docente/'): 'REGISTRO_DOCENTE',
        ('POST', '/api/v1/auth/register/alumno/'):  'REGISTRO_ALUMNO',
        ('POST', '/api/v1/users/tokens/registration/'): 'TOKEN_GENERADO',
    }

    def process_response(self, request, response):
        key = (request.method, request.path)
        accion = self.AUDIT_MAP.get(key)

        if accion and response.status_code in (200, 201):
            try:
                from security.models import AuditLog
                usuario = getattr(request, 'user', None)
                if usuario and not usuario.is_authenticated:
                    usuario = None
                AuditLog.registrar(accion=accion, usuario=usuario, request=request)
            except Exception as e:
                logger.error(f'AuditMiddleware error: {e}')

        return response


class SessionUniquenessMiddleware(MiddlewareMixin):
    """
    Verifica que cada JWT corresponde a la sesión activa del usuario.
    Esto implementa "solo una sesión activa por usuario".

    Nota: Esta validación es costosa (1 query por request autenticado).
    Para producción de alto volumen, cachear el session_token en Redis.
    Desactivar si no se necesita unicidad de sesión.
    """

    # Endpoints excluidos de la validación (auth no tiene sesión aún)
    EXCLUDED = {'/api/v1/auth/login/', '/api/v1/auth/refresh/', '/api/v1/auth/register/'}

    def process_request(self, request):
        if request.path in self.EXCLUDED:
            return None

        # Solo aplica a requests autenticados con JWT
        auth_header = request.META.get('HTTP_AUTHORIZATION', '')
        if not auth_header.startswith('Bearer '):
            return None

        # La verificación real ocurre en el authenticator de simplejwt
        # Este middleware solo actuaría si se implementa session_token en el JWT claim
        # Por ahora es un placeholder para la funcionalidad futura
        return None
