"""Users serializers."""

# Utilities
import jwt

# Django
from django.conf import settings
from django.contrib.auth import password_validation
from django.utils.translation import gettext_lazy as _

# Django REST Framework
from rest_framework import serializers
from rest_framework.validators import UniqueValidator

# Models
from {{project_slug}}.users.models import User, Profile

# Serializers
from {{project_slug}}.users.serializers.profiles import ProfileModelSerializer

# Tasks
from {{project_slug}}.users.tasks import send_confirmation_email, send_password_recovery_email


class UserModelSerializer(serializers.ModelSerializer):
    """User model serializer."""

    profile = ProfileModelSerializer(read_only=True)
    username = serializers.CharField(read_only=True)
    email = serializers.CharField(read_only=True)

    class Meta:
        """Meta class."""

        model = User
        fields = (
            "first_name",
            "last_name",
            "email",
            "profile",
            "username",
        )


class UserUpdateModelSerializer(serializers.ModelSerializer):
    """User model serializer."""

    profile = ProfileModelSerializer(read_only=True)
    username = serializers.CharField(read_only=True)
    email = serializers.CharField(read_only=True)

    class Meta:
        """Meta class."""

        model = User
        fields = (
            "first_name",
            "last_name",
            "email",
            "profile",
            "username",
        )


class UserSignUpSerializer(serializers.Serializer):
    """User sign up serializer.
    Handle sign up data validation and user/profile creation.
    """

    email = serializers.EmailField(
        validators=[UniqueValidator(queryset=User.objects.all())]
    )

    username = serializers.CharField(
        min_length=4,
        max_length=20,
        validators=[UniqueValidator(queryset=User.objects.all())],
    )

    # Password
    password = serializers.CharField(min_length=8, max_length=64)
    password_confirmation = serializers.CharField(min_length=8, max_length=64)

    # Name
    first_name = serializers.CharField(min_length=2, max_length=30)
    last_name = serializers.CharField(min_length=2, max_length=30)

    def validate(self, data):
        """Verify passwords match."""
        passwd = data["password"]
        passwd_conf = data["password_confirmation"]
        if passwd != passwd_conf:
            raise serializers.ValidationError(_("Passwords don't match."))
        password_validation.validate_password(passwd)
        return data

    def create(self, data):
        """Handle user and profile creation."""
        data.pop("password_confirmation")
        user = User.objects.create_user(**data, is_verified=False)
        profile = Profile(user=user)
        profile.save()
        send_confirmation_email.delay(user_pk=user.pk)
        return user


class AccountVerificationSerializer(serializers.Serializer):
    """Account verification serializer."""

    token = serializers.CharField()

    def validate_token(self, data):
        """Verify token is valid."""
        try:
            payload = jwt.decode(data, settings.SECRET_KEY, algorithms=["HS256"])
        except jwt.ExpiredSignatureError:
            raise serializers.ValidationError(_("Verification link has expired."))
        except jwt.PyJWTError:
            raise serializers.ValidationError(_("Invalid token"))
        if payload["type"] != "email_confirmation":
            raise serializers.ValidationError(_("Invalid token"))

        self.context["payload"] = payload
        return data

    def save(self):
        """Update user's verified status."""
        payload = self.context["payload"]
        user = User.objects.get(username=payload["user"])
        user.is_verified = True
        user.save()


class PasswordUpdateSerializer(serializers.Serializer):
    """Password Update serializer"""

    current_password = serializers.CharField(required=True)
    new_password = serializers.CharField(required=True)
    confirm_password = serializers.CharField(required=True)

    def validate(self, data):
        if data["new_password"] != data["confirm_password"]:
            raise serializers.ValidationError(_("New passwords do not match."))
        password_validation.validate_password(data["new_password"])
        return data


class PasswordRecoverySerializer(serializers.Serializer):
    """Password recovery Serializer"""

    email = serializers.EmailField()

    def validate(self, data):
        try:
            user = User.objects.get(email=data["email"])
            self.context["user"] = user
        except User.DoesNotExist:
            raise serializers.ValidationError(
                _("No user found with the provided email.")
            )
        return data

    def create(self, data):
        user = self.context["user"]
        send_password_recovery_email.delay(user.pk)

        return True


class PasswordResetSerializer(serializers.Serializer):
    """Password reset serializer"""

    token = serializers.CharField()
    password = serializers.CharField(min_length=8, max_length=64)
    password_confirmation = serializers.CharField(min_length=8, max_length=64)

    def validate(self, data):
        """Verify passwords match."""
        passwd = data["password"]
        passwd_conf = data["password_confirmation"]
        if passwd != passwd_conf:
            raise serializers.ValidationError(_("Passwords don't match."))
        password_validation.validate_password(passwd)
        return data

    def validate_token(self, data):
        """Verify token is valid."""
        try:
            payload = jwt.decode(data, settings.SECRET_KEY, algorithms=["HS256"])
        except jwt.ExpiredSignatureError:
            raise serializers.ValidationError(_("Recovery link has expired."))
        except jwt.PyJWTError:
            raise serializers.ValidationError(_("Invalid token"))
        if payload["type"] != "password_recovery":
            raise serializers.ValidationError(_("Invalid token"))

        self.context["payload"] = payload
        return data

    def create(self, data):
        """Update user's password."""
        payload = self.context["payload"]
        user = User.objects.get(username=payload["user"])
        user.set_password(data["password"])
        user.save()
        return data


class UserLookUpSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("username",)
