from rest_framework import serializers
from authentication.models import User
from django.utils.encoding import smart_str,DjangoUnicodeDecodeError
from django.contrib.auth.tokens import PasswordResetTokenGenerator
from rest_framework import serializers
from .models import User, CodeConfirmation, usertype
import os
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.core.mail import EmailMultiAlternatives
import random
from utils.helper import failed_response
from rest_framework import status
from django.utils.http import urlsafe_base64_decode
from django.utils.encoding import force_str
from django.conf import settings
from django.core.mail import send_mail


class UserRegistrationSerializer(serializers.ModelSerializer):
    usertype_id = serializers.IntegerField()

    class Meta:
        model = User
        fields = ['first_name', 'last_name', 'username', 'email', 'password', 'usertype_id', 'pan_number', 'phone_number']
        extra_kwargs = {
            'password': {'write_only': True}
        }

    def create(self, validated_data):
        usertype_id = validated_data.pop('usertype_id')
        if not usertype.objects.filter(id=usertype_id).exists():
            raise failed_response(msg="User Type id doesn't exist", code=status.HTTP_404_NOT_FOUND)
        
        user = User.objects.create_user(**validated_data)
        user.usertype_id = usertype_id
        user.save()
        return user

def send_verification_email(email, code):
    subject = 'Verify Your Email'
    
    context = {
        'code': code,
        'expiry_time': '30 minutes'
    }
    
    html_message = render_to_string('otp_email.html', context)
    
    plain_message = strip_tags(html_message)
    
    from_email = settings.EMAIL_HOST_USER
    email_message = EmailMultiAlternatives(
        subject,
        plain_message,
        from_email,
        [email]
    )
    
    email_message.attach_alternative(html_message, "text/html")
    
    email_message.send(fail_silently=False)

        
class UserLoginSerializer(serializers.Serializer):
    username = serializers.CharField(required = False,allow_blank = True)
    email = serializers.EmailField(required = False,allow_blank = True)
    password = serializers.CharField(style = {'input_type':'password'})

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

        if not username and not email:
            raise failed_response(msg="Either username or email is required", code=status.HTTP_400_BAD_REQUEST)
        if not password:
            raise failed_response(msg="Password is required", code=status.HTTP_400_BAD_REQUEST)
        return attrs
    
class UserChangePasswordSerializer(serializers.Serializer):
    password = serializers.CharField(max_length = 255,style = {'input_type':'password'},write_only = True)
    password2 = serializers.CharField(max_length = 255,style = {'input_type':'password'},write_only = True)

    class Meta:
        fields = ['password','password2']

    def validate(self, attrs):
        password = attrs.get('password')
        password2 = attrs.get('password2')
        user = self.context.get('user')
        if password != password2:
            raise failed_response("Password and confirm password do not match", code=status.HTTP_400_BAD_REQUEST)
        user.set_password(password)
        user.save()
        return attrs
    
class SendPasswordResetEmailSerializer(serializers.Serializer):
    email = serializers.EmailField(max_length=255)

    class Meta:
        fields = ['email']

    def validate_email(self, value):
        if not User.objects.filter(email=value).exists():
            raise serializers.ValidationError("User with this email does not exist.")
        return value

    

class UserPasswordResetSerializer(serializers.Serializer):
    password = serializers.CharField(max_length=255, style={'input_type': 'password'}, write_only=True)
    password2 = serializers.CharField(max_length=255, style={'input_type': 'password'}, write_only=True)

    class Meta:
        fields = ['password', 'password2']

    def validate(self, attrs):
        try:
            password = attrs.get('password')
            password2 = attrs.get('password2')
            uid = self.context.get('uid')
            token = self.context.get('token')
            if password != password2:
                raise failed_response("Password and Confirm Password do not match", code=status.HTTP_400_BAD_REQUEST)
            user_id = force_str(urlsafe_base64_decode(uid))
            user = User.objects.get(id=user_id)
            if not PasswordResetTokenGenerator().check_token(user, token):
                raise failed_response("Token is either invalid or expired", code=status.HTTP_400_BAD_REQUEST)
            user.set_password(password)
            user.save()
            return attrs

        except DjangoUnicodeDecodeError as identifier:
            PasswordResetTokenGenerator().check_token(user, token)
            raise failed_response(msg='Token is not valid or expired', code=status.HTTP_400_BAD_REQUEST)


class SendCodeSerializer(serializers.ModelSerializer):
    email = serializers.EmailField()
    code = serializers.CharField(max_length=6, read_only=True)
    type = serializers.ChoiceField(choices=CodeConfirmation.TYPE_CHOICES)

    class Meta:
        model = CodeConfirmation
        fields = ['email', 'code', 'type']

    def validate_email(self, value):
        if not User.objects.filter(email=value).exists():
            raise serializers.ValidationError("User with this email does not exist.")
        return value

    def create(self, validated_data):
        email = validated_data['email']
        confirmation_type = validated_data['type']
        CodeConfirmation.objects.filter(email=email, type=confirmation_type, pending=True).update(pending=False)
        code = str(random.randint(100000, 999999))
        validated_data['code'] = code
        code_confirmation, created = CodeConfirmation.objects.update_or_create(
            email=email,
            type=confirmation_type,
            defaults={'code': code, 'pending': True}
        )
        
        self.send_otp_email(email, code)
        return code_confirmation

    def send_otp_email(self, email, code):
        subject = 'Your OTP Code'
        html_message = render_to_string('otp_email.html', {'code': code})
        plain_message = strip_tags(html_message)
        from_email = os.environ.get('EMAIL_USER')
        email_message = EmailMultiAlternatives(subject, plain_message, from_email, [email])
        email_message.attach_alternative(html_message, "text/html")
        email_message.send(fail_silently=False)

class VerifyCodeSerializer(serializers.Serializer):
    email = serializers.EmailField()
    code = serializers.CharField(max_length=6)
    type = serializers.ChoiceField(choices=CodeConfirmation.TYPE_CHOICES)
    new_email = serializers.EmailField(required=False)

    def validate(self, data):
        email = data.get('email')
        code = data.get('code')
        new_email = data.get('new_email')
        confirmation_type = data.get('type')
        
        if confirmation_type == CodeConfirmation.EMAIL_CHANGE:
            if email == new_email:
                raise failed_response(msg="New email can't be the same as the current email.", code=status.HTTP_400_BAD_REQUEST)
            if User.objects.filter(email=new_email).exists():
                raise failed_response(msg="New email is already in use.", code=status.HTTP_400_BAD_REQUEST)

        if not CodeConfirmation.objects.filter(email=email, code=code, pending=True, type=confirmation_type).exists():
            raise failed_response(msg="Invalid code or email.", code=status.HTTP_400_BAD_REQUEST)
        
        return data

    def save(self):
        email = self.validated_data['email']
        code = self.validated_data['code']
        confirmation_type = self.validated_data['type']
        confirmation = CodeConfirmation.objects.get(
            email=email, code=code, pending=True, type=confirmation_type
        )
        
        confirmation.pending = False
        confirmation.save()

        if confirmation_type == CodeConfirmation.EMAIL_CHANGE:
            new_email = self.validated_data['new_email']
            user = User.objects.get(email=email)
            user.email = new_email
            user.save()

        elif confirmation_type == CodeConfirmation.EMAIL_CONFIRMATION:
            user = User.objects.get(email=email)
            user.is_active = True
            user.save()

        return confirmation
    
class EmailSerializer(serializers.Serializer):
    subject = serializers.CharField(max_length=255)
    message = serializers.CharField()
    recipient_list = serializers.ListField(
        child=serializers.EmailField()
    )

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = [
            'first_name', 'last_name', 'middle_name', 'username', 
            'email', 'profile_img', 'password'
        ]
        extra_kwargs = {
            'password': {'write_only': True},
            'profile_img': {'required': False},
        }

    def update(self, instance, validated_data):
        password = validated_data.pop('password', None)
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        if password:
            instance.set_password(password)
        instance.save()
        return instance

class UserTypeSerializer(serializers.ModelSerializer):
    class Meta:
        model = usertype
        fields = ['type','subtype']

class UserDetailSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['username', 'email', 'first_name', 'last_name', 'phone_number', 'verified', 'pan_number']
