"""
users/serializers/auth.py
==========================
Serializers de autenticación: login, registro docente y registro alumno.
"""

from django.utils import timezone
from rest_framework import serializers
from rest_framework_simplejwt.tokens import RefreshToken

from users.models import CustomUser, PerfilDocente, PerfilAlumno, RegistrationToken
from shared.utils.tokens import TokenService
from shared.utils.encryption import get_encryption_service


class CustomTokenObtainSerializer(serializers.Serializer):
    """
    Serializer de login propio — NO hereda de TokenObtainPairSerializer
    para evitar que simplejwt busque internamente por username_field.
    Usa nuestro manager get_by_email() que trabaja con el hash del email.
    """

    email    = serializers.EmailField(write_only=True)
    password = serializers.CharField(write_only=True, style={'input_type': 'password'})

    def validate(self, attrs):
        email    = attrs.get('email', '').lower().strip()
        password = attrs.get('password', '')

        # 1. Buscar usuario por hash del email
        user = CustomUser.objects.get_by_email(email)

        if user is None or not user.is_active:
            raise serializers.ValidationError({
                'non_field_errors': 'Credenciales inválidas.'
            })

        # 2. Verificar bloqueo por intentos fallidos
        if user.is_locked:
            remaining = max(0, int((user.locked_until - timezone.now()).total_seconds() // 60))
            raise serializers.ValidationError({
                'non_field_errors': f'Cuenta bloqueada. Intenta de nuevo en {remaining} minutos.'
            })

        # 3. Verificar contraseña
        if not user.check_password(password):
            user.register_failed_login()
            raise serializers.ValidationError({
                'non_field_errors': 'Credenciales inválidas.'
            })

        # 4. Login exitoso — resetear intentos fallidos
        user.reset_login_attempts()

        # 5. Docentes no aprobados no pueden entrar
        if user.role == 'docente' and not user.is_approved:
            raise serializers.ValidationError({
                'non_field_errors': 'Tu solicitud aún no ha sido aprobada por el administrador.'
            })

        # 6. Generar tokens JWT con claims personalizados
        refresh = RefreshToken.for_user(user)
        refresh['role']        = user.role
        refresh['is_approved'] = user.is_approved
        refresh['nickname']    = user.nickname or ''

        return {
            'access':  str(refresh.access_token),
            'refresh': str(refresh),
            'user': {
                'id':          user.id,
                'role':        user.role,
                'is_approved': user.is_approved,
                'nickname':    user.nickname or '',
            }
        }


class RegisterDocenteSerializer(serializers.Serializer):
    """
    Registro de docente. Queda pendiente hasta que el admin aprueba.
    """

    email           = serializers.EmailField()
    password        = serializers.CharField(min_length=8, write_only=True)
    nombre_completo = serializers.CharField(min_length=3, max_length=200)
    institucion     = serializers.CharField(min_length=3, max_length=200)
    departamento    = serializers.CharField(max_length=200)
    cargo           = serializers.CharField(max_length=100, required=False, allow_blank=True)

    def validate_email(self, value):
        if CustomUser.objects.get_by_email(value) is not None:
            raise serializers.ValidationError('Este email ya está registrado.')
        return value.lower().strip()

    def validate_password(self, value):
        import re
        if not re.search(r'[A-Z]', value):
            raise serializers.ValidationError('La contraseña debe tener al menos una mayúscula.')
        if not re.search(r'\d', value):
            raise serializers.ValidationError('La contraseña debe tener al menos un número.')
        return value

    def create(self, validated_data):
        from django.db import transaction

        with transaction.atomic():
            user = CustomUser.objects.create_user(
                email=validated_data['email'],
                password=validated_data['password'],
                role='docente',
                is_approved=False,
                is_active=True,
            )

            perfil = PerfilDocente(user=user)
            perfil.nombre_completo = validated_data['nombre_completo']
            perfil.institucion     = validated_data['institucion']
            perfil.departamento    = validated_data['departamento']
            perfil.cargo           = validated_data.get('cargo', '')
            perfil.save()

        return user


class RegisterAlumnoSerializer(serializers.Serializer):
    """
    Registro de alumno mediante token seguro generado por el docente.
    """

    token_registro  = serializers.CharField(min_length=10)
    email           = serializers.EmailField()
    password        = serializers.CharField(min_length=8, write_only=True)
    nombre_completo = serializers.CharField(min_length=3, max_length=200)

    _registration_token = None

    def validate_email(self, value):
        if CustomUser.objects.get_by_email(value) is not None:
            raise serializers.ValidationError('Este email ya está registrado.')
        return value.lower().strip()

    def validate_password(self, value):
        import re
        if not re.search(r'[A-Z]', value):
            raise serializers.ValidationError('La contraseña debe tener al menos una mayúscula.')
        if not re.search(r'\d', value):
            raise serializers.ValidationError('La contraseña debe tener al menos un número.')
        return value

    def validate_token_registro(self, value):
        import hashlib
        token_hash = hashlib.sha256(value.strip().encode('utf-8')).hexdigest()

        try:
            reg_token = RegistrationToken.objects.select_related('docente').get(
                token_hash=token_hash
            )
        except RegistrationToken.DoesNotExist:
            raise serializers.ValidationError(
                'Código de registro inválido. Verifica que lo hayas copiado correctamente.'
            )

        if reg_token.is_used:
            raise serializers.ValidationError(
                'Este código ya fue utilizado. Solicita un nuevo código a tu docente.'
            )

        if reg_token.is_expired:
            raise serializers.ValidationError(
                'Este código ha expirado (validez: 72 horas). Solicita un nuevo código.'
            )

        if not reg_token.docente.is_active or not reg_token.docente.is_approved:
            raise serializers.ValidationError(
                'El docente que generó este código no tiene una cuenta activa.'
            )

        self._registration_token = reg_token
        return value

    def create(self, validated_data):
        from django.db import transaction
        from academic.models import Inscripcion

        reg_token = self._registration_token

        with transaction.atomic():
            user = CustomUser.objects.create_user(
                email=validated_data['email'],
                password=validated_data['password'],
                role='alumno',
                is_approved=True,
                is_active=True,
            )

            perfil = PerfilAlumno(
                user=user,
                invitado_por=reg_token.docente
            )
            perfil.nombre_completo = validated_data['nombre_completo']
            perfil.save()

            # Si el token tiene sección asociada → inscribir automáticamente
            if reg_token.seccion:
                Inscripcion.objects.get_or_create(
                    alumno=user,
                    seccion=reg_token.seccion,
                    defaults={'activo': True}
                )

            reg_token.mark_as_used(user)

        return user
