"""
Backend de Autenticación Seguro — Core
========================================
Backend que integra bloqueo de cuenta por intentos fallidos (OWASP A07).
"""

import logging

from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend

from .models import LoginAttempt

logger = logging.getLogger("apps.core.security")
User = get_user_model()


class SecureModelBackend(ModelBackend):
    """
    Backend de autenticación que:
    1. Verifica si la cuenta está bloqueada antes de autenticar.
    2. Registra intentos fallidos.
    3. Limpia intentos tras login exitoso.
    4. Bloquea tras ACCOUNT_LOGIN_MAX_ATTEMPTS intentos fallidos
       durante ACCOUNT_LOGIN_LOCKOUT_MINUTES minutos.
    """

    def authenticate(self, request, username=None, password=None, **kwargs):
        # SimpleJWT pasa el campo como email= (USERNAME_FIELD del modelo)
        if username is None:
            username = kwargs.get("email")
        if username is None or password is None:
            return None

        # Obtener IP del request
        ip_address = self._get_client_ip(request) if request else "0.0.0.0"
        user_agent = ""
        if request:
            user_agent = request.META.get("HTTP_USER_AGENT", "")[:500]

        # Verificar si la cuenta está bloqueada
        if LoginAttempt.is_locked(username):
            lockout_minutes = getattr(settings, "ACCOUNT_LOGIN_LOCKOUT_MINUTES", 30)
            logger.warning(
                "Cuenta bloqueada por intentos fallidos: %s desde %s",
                username,
                ip_address,
            )
            LoginAttempt.record_attempt(
                username=username,
                ip_address=ip_address,
                user_agent=user_agent,
                successful=False,
            )
            return None

        # Intentar autenticación estándar
        user = super().authenticate(request, username=username, password=password, **kwargs)

        if user is not None:
            # Login exitoso → limpiar intentos fallidos
            LoginAttempt.record_attempt(
                username=username,
                ip_address=ip_address,
                user_agent=user_agent,
                successful=True,
            )
            LoginAttempt.clear_failures(username)
            logger.info("Login exitoso: %s desde %s", username, ip_address)
            return user
        else:
            # Login fallido → registrar intento
            LoginAttempt.record_attempt(
                username=username,
                ip_address=ip_address,
                user_agent=user_agent,
                successful=False,
            )
            failures = LoginAttempt.get_recent_failures(username)
            max_attempts = getattr(settings, "ACCOUNT_LOGIN_MAX_ATTEMPTS", 5)
            logger.warning(
                "Login fallido: %s desde %s (intento %d/%d)",
                username,
                ip_address,
                failures,
                max_attempts,
            )
            return None

    @staticmethod
    def _get_client_ip(request):
        """Extrae la IP real del cliente, considerando proxies."""
        x_forwarded_for = request.META.get("HTTP_X_FORWARDED_FOR")
        if x_forwarded_for:
            # Tomar la primera IP (la del cliente original)
            return x_forwarded_for.split(",")[0].strip()
        return request.META.get("REMOTE_ADDR", "0.0.0.0")
