import logging

from django.contrib.auth import (
    get_user_model,
    password_validation,
)
from django.contrib.auth.models import AbstractBaseUser
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from rest_framework.authtoken.models import Token
from rest_framework.exceptions import ValidationError

# Get the UserModel
UserModel = get_user_model()
logger = logging.getLogger("vq_django_auth.serializers")


def validate_new_passwords(user: AbstractBaseUser, attrs: dict[str, str]) -> None:
    if attrs["new_password1"] != attrs["new_password2"]:
        raise ValidationError(
            code="password_mismatch",
            detail=_("The two password fields didn’t match."),
        )
    password_validation.validate_password(
        password=attrs["new_password1"],
        user=user,
    )


class LoginSerializer(serializers.Serializer[dict[str, str]]):
    email = serializers.EmailField()
    password = serializers.CharField(style={"input_type": "password"})

    def to_internal_value(self, data: dict[str, str]) -> dict[str, str]:
        data = super().to_internal_value(data)

        # handle case where email is not the `USERNAME_FIELD`
        # Django's default is `username`
        data[UserModel.USERNAME_FIELD] = data["email"]  # type: ignore[attr-defined]
        return data


class TokenSerializer(serializers.ModelSerializer[Token]):
    """
    Serializer for Token model.
    """

    class Meta:
        model = Token
        fields = ("key",)


class UserDetailsSerializer(serializers.ModelSerializer[AbstractBaseUser]):
    """
    User model w/o password
    """

    class Meta:
        extra_fields = []
        # see https://github.com/iMerica/dj-rest-auth/issues/181
        # UserModel.XYZ causing attribute error while importing other
        # classes from `serializers.py`. So, we need to check whether the auth model has
        # the attribute or not
        if hasattr(UserModel, "USERNAME_FIELD"):
            extra_fields.append(UserModel.USERNAME_FIELD)
        if hasattr(UserModel, "EMAIL_FIELD"):
            extra_fields.append(UserModel.EMAIL_FIELD)
        if hasattr(UserModel, "first_name"):
            extra_fields.append("first_name")
        if hasattr(UserModel, "last_name"):
            extra_fields.append("last_name")
        model = UserModel
        fields = ("pk", *extra_fields)
        read_only_fields = fields


class PasswordResetSerializer(serializers.Serializer[dict[str, str]]):
    """
    Serializer for requesting a password reset e-mail.
    """

    email = serializers.EmailField()


class PasswordResetConfirmSerializer(serializers.Serializer[dict[str, str]]):
    """
    Serializer for confirming a password reset attempt.
    """

    new_password1 = serializers.CharField(max_length=128)
    new_password2 = serializers.CharField(max_length=128)
    uid = serializers.CharField()
    token = serializers.CharField()

    def validate(self, attrs: dict[str, str]) -> dict[str, str]:
        validate_new_passwords(user=self.context["request"].user, attrs=attrs)
        return attrs


class PasswordChangeSerializer(serializers.Serializer[dict[str, str]]):
    old_password = serializers.CharField(max_length=128)
    new_password1 = serializers.CharField(max_length=128)
    new_password2 = serializers.CharField(max_length=128)

    set_password_form = None

    def validate_old_password(self, value: str) -> str:
        user = self.context["request"].user
        invalid_password_conditions = (
            value,
            user,
            not user.check_password(value),
        )

        if all(invalid_password_conditions):
            err_msg = _(
                "Your old password was entered incorrectly. Please enter it again."
            )
            raise serializers.ValidationError(err_msg)
        return value

    def validate(self, attrs: dict[str, str]) -> dict[str, str]:
        validate_new_passwords(user=self.context["request"].user, attrs=attrs)
        return attrs
