from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from django.contrib.auth.tokens import default_token_generator
from django.utils.encoding import force_bytes, force_str
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer

User = get_user_model()


class RegisterSerializer(serializers.ModelSerializer):
    password = serializers.CharField(
        write_only=True,
        min_length=8,
        validators=[validate_password],
    )
    first_name = serializers.CharField(max_length=100)
    last_name = serializers.CharField(max_length=100)
    patronymic = serializers.CharField(
        max_length=100, required=False, allow_blank=True
    )
    student_group = serializers.CharField(
        max_length=50, required=False, allow_blank=True, default=""
    )
    role = serializers.ChoiceField(
        choices=[User.Role.STUDENT, User.Role.TEACHER],
        default=User.Role.STUDENT,
        write_only=True,
    )
    managed_groups = serializers.ListField(
        child=serializers.CharField(max_length=50),
        required=False,
        allow_empty=True,
        write_only=True,
    )

    class Meta:
        model = User
        fields = (
            "email",
            "password",
            "first_name",
            "last_name",
            "patronymic",
            "student_group",
            "role",
            "managed_groups",
        )

    def validate_email(self, value):
        if User.objects.filter(email__iexact=value).exists():
            raise serializers.ValidationError("Пользователь с таким email уже есть.")
        return value.lower()

    def validate(self, attrs):
        role = attrs.get("role", User.Role.STUDENT)
        groups = attrs.get("managed_groups") or []
        if role == User.Role.TEACHER:
            cleaned = [str(g).strip() for g in groups if str(g).strip()]
            if not cleaned:
                raise serializers.ValidationError(
                    {
                        "managed_groups": "Укажите хотя бы одну учебную группу для преподавателя."
                    }
                )
            attrs["managed_groups"] = cleaned
        return attrs

    def create(self, validated_data):
        group = validated_data.pop("student_group", "") or ""
        role = validated_data.pop("role", User.Role.STUDENT)
        managed_groups = validated_data.pop("managed_groups", []) or []
        first_name = validated_data.pop("first_name")
        last_name = validated_data.pop("last_name")
        patronymic = validated_data.pop("patronymic", "") or ""
        password = validated_data.pop("password")
        email = validated_data["email"]

        base = email.split("@")[0].replace(".", "_")[:120]
        username = base
        n = 0
        while User.objects.filter(username=username).exists():
            n += 1
            username = f"{base}_{n}"

        user = User(
            email=email,
            username=username,
            first_name=first_name,
            last_name=last_name,
            role=role,
            is_active=True,
            is_verified=False,
        )
        user.set_password(password)
        user.save()
        self._pending_profile = {
            "patronymic": patronymic,
            "student_group": group,
        }
        self._managed_groups = managed_groups if role == User.Role.TEACHER else []
        return user


class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
    username_field = User.USERNAME_FIELD

    def validate(self, attrs):
        # Email без учёта регистра и пробелов (иначе «учётная запись не найдена»)
        field = self.username_field
        if field in attrs and attrs[field]:
            attrs[field] = attrs[field].strip().lower()
        data = super().validate(attrs)
        return data

    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)
        role = user.role
        if user.is_superuser:
            role = User.Role.ADMIN
        token["role"] = role
        token["email"] = user.email
        return token


class PasswordResetRequestSerializer(serializers.Serializer):
    email = serializers.EmailField()

    def save(self):
        request = self.context.get("request")
        email = self.validated_data["email"].lower()
        try:
            user = User.objects.get(email__iexact=email)
        except User.DoesNotExist:
            return
        uid = urlsafe_base64_encode(force_bytes(user.pk))
        token = default_token_generator.make_token(user)
        from django.conf import settings

        reset_link = (
            f"{settings.FRONTEND_URL.rstrip('/')}/reset-password/?uid={uid}&token={token}"
        )
        from django.core.mail import send_mail

        send_mail(
            subject="Восстановление пароля — портфолио",
            message=f"Перейдите по ссылке для сброса пароля:\n{reset_link}",
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[user.email],
            fail_silently=True,
        )


class PasswordResetConfirmSerializer(serializers.Serializer):
    uid = serializers.CharField()
    token = serializers.CharField()
    new_password = serializers.CharField(write_only=True, min_length=8)

    def validate_new_password(self, value):
        validate_password(value)
        return value

    def validate(self, attrs):
        try:
            uid = force_str(urlsafe_base64_decode(attrs["uid"]))
            user = User.objects.get(pk=uid)
        except (User.DoesNotExist, ValueError, TypeError):
            raise serializers.ValidationError({"uid": "Некорректная ссылка."})
        if not default_token_generator.check_token(user, attrs["token"]):
            raise serializers.ValidationError({"token": "Срок действия ссылки истёк."})
        self.context["reset_user"] = user
        return attrs

    def save(self):
        user = self.context["reset_user"]
        user.set_password(self.validated_data["new_password"])
        user.save(update_fields=["password"])
        return user
