"""Common account serializers"""
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from django.contrib.auth import password_validation as password_validators
from fcm_django.models import FCMDevice
from rest_framework import exceptions
from rest_framework import serializers
from rest_framework import validators as rest_validators

from account import models, tasks
from utils import exceptions as utils_exceptions
from utils import methods as utils_methods


# User serializers
class UserSerializer(serializers.ModelSerializer):
    """User serializer."""
    # RESPONSE
    email_confirmed = serializers.BooleanField(read_only=True)
    fullname = serializers.CharField(source='get_full_name', read_only=True)

    # REQUEST
    username = serializers.CharField(
        validators=(rest_validators.UniqueValidator(queryset=models.User.objects.all()),),
        required=False)
    first_name = serializers.CharField(required=False, write_only=True)
    last_name = serializers.CharField(required=False, write_only=True)
    image_url = serializers.URLField(required=False)
    cropped_image_url = serializers.URLField(required=False)
    email = serializers.EmailField(
        validators=(rest_validators.UniqueValidator(queryset=models.User.objects.all()),),
        required=False)
    newsletter = serializers.BooleanField(required=False)

    class Meta:
        model = models.User
        fields = [
            'username',
            'first_name',
            'last_name',
            'fullname',
            'cropped_image_url',
            'image_url',
            'email',
            'email_confirmed',
            'newsletter',
        ]

    def validate_email(self, value):
        """Validate email value"""
        if value == self.instance.email:
            raise serializers.ValidationError(detail='Equal email address.')
        return value

    def validate_username(self, value):
        """Custom username validation"""
        valid = utils_methods.username_validator(username=value)
        if not valid:
            raise utils_exceptions.NotValidUsernameError()
        return value

    def update(self, instance, validated_data):
        """Override update method"""
        old_email = instance.email
        instance = super().update(instance, validated_data)
        if 'email' in validated_data:
            instance.email_confirmed = False
            instance.email = old_email
            instance.unconfirmed_email = validated_data['email']
            instance.save()
            # Send verification link on user email for change email address
            if settings.USE_CELERY:
                tasks.change_email_address.delay(
                    user_id=instance.id,
                    country_code=self.context.get('request').country_code,
                    emails=[validated_data['email'],])
            else:
                tasks.change_email_address(
                    user_id=instance.id,
                    country_code=self.context.get('request').country_code,
                    emails=[validated_data['email'],])
        return instance


class UserBaseSerializer(serializers.ModelSerializer):
    """Serializer is used to display brief information about the user."""

    fullname = serializers.CharField(source='get_full_name', read_only=True)

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

        model = models.User
        fields = (
            'id',
            'username',
            'fullname',
            'first_name',
            'last_name',
            'email',
            'cropped_image_url',
            'image_url',
        )
        read_only_fields = fields


class UserShortSerializer(UserSerializer):
    """Compact serializer for model  User."""

    class Meta(UserSerializer.Meta):
        """Meta class."""
        fields = [
            'id',
            'fullname',
            'email',
        ]


class ChangePasswordSerializer(serializers.ModelSerializer):
    """Serializer for model User."""

    password = serializers.CharField(write_only=True)
    old_password = serializers.CharField(write_only=True)

    class Meta:
        """Meta class"""
        model = models.User
        fields = (
            'password',
            'old_password',
        )

    def validate(self, attrs):
        """Override validate method"""
        password = attrs.get('password')
        old_password = attrs.get('old_password')
        try:
            # Check old password
            if not self.instance.check_password(raw_password=old_password):
                raise serializers.ValidationError(_('Old password mismatch.'))
            # Compare new password with the old ones
            if self.instance.check_password(raw_password=password):
                raise serializers.ValidationError(_('Password is already in use'))
            # Validate password
            password_validators.validate_password(password=password)
        except serializers.ValidationError as e:
            raise serializers.ValidationError({'detail': e.detail})
        else:
            if settings.USE_CELERY:
                tasks.send_password_changed_email(
                    user_id=self.instance.id,
                    country_code=self.context.get('request').country_code)
            else:
                tasks.send_password_changed_email(
                    user_id=self.instance.id,
                    country_code=self.context.get('request').country_code)
            return attrs

    def update(self, instance, validated_data):
        """Override update method"""
        # Update user password from instance
        instance.set_password(validated_data.get('password'))
        instance.save()
        return instance


class ChangeEmailSerializer(serializers.ModelSerializer):
    """Change user email serializer"""

    class Meta:
        """Meta class"""
        model = models.User
        fields = (
            'email',
        )

    def validate_email(self, value):
        """Validate email value"""
        if value == self.instance.email:
            raise serializers.ValidationError()
        return value

    def validate(self, attrs):
        """Override validate method"""
        email_confirmed = self.instance.email_confirmed
        if not email_confirmed:
            raise serializers.ValidationError()
        return attrs

    def update(self, instance, validated_data):
        """
        Override update method
        """
        instance.email = validated_data.get('email')
        instance.email_confirmed = False
        instance.save()
        # Send verification link on user email for change email address
        if settings.USE_CELERY:
            tasks.confirm_new_email_address.delay(instance.id)
        else:
            tasks.confirm_new_email_address(instance.id)
        return instance


# Firebase Cloud Messaging serializers
class FCMDeviceSerializer(serializers.ModelSerializer):
    """FCM Device model serializer"""
    class Meta:
        model = FCMDevice
        fields = ('id', 'name', 'registration_id', 'device_id',
                  'active', 'date_created', 'type')
        read_only_fields = ('id', 'date_created',)
        extra_kwargs = {'active': {'default': True}}

    def validate(self, attrs):
        regid = attrs.get('registration_id')
        dtype = attrs.get('type')
        if regid and dtype and self.Meta.model.objects.filter(
                registration_id=regid).exclude(type=dtype).count():
            raise exceptions.ValidationError(
                {'registration_id': 'This field must be unique.'})
        return attrs

    def __init__(self, *args, **kwargs):
        super(FCMDeviceSerializer, self).__init__(*args, **kwargs)
        self.fields['type'].help_text = (
            'Should be one of ["%s"]' %
            '", "'.join([i for i in self.fields['type'].choices]))

    def create(self, validated_data):
        user = self.context['request'].user
        if not user.is_anonymous:
            validated_data['user'] = user
        device = FCMDevice.objects.create(**validated_data)
        return device

    def update(self, instance, validated_data):

        user = self.context['request'].user
        if not user.is_anonymous:
            instance.user = user
            instance.save()
        else:
            instance.user = None
            instance.save()
        return instance
