from rest_framework import serializers
from .models import *
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission, Group
from .signals import save_usuario, save_cliente, update_usuario, update_cliente
from django.dispatch import Signal
save_usuario = Signal()
save_cliente = Signal()
update_usuario = Signal()
update_cliente = Signal()

User = get_user_model()


class PermissionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Permission
        fields = ['id', 'name', 'codename', 'content_type']

# class AuthGroupPermissionSerializer(serializers.ModelSerializer):
#     group = serializers.SerializerMethodField(read_only=True)
#     permission = serializers.SerializerMethodField(read_only=True)
#     class Meta:
#         model = PermissionsMixin
#         fields = "__all__"
    
#     def get_group(self, obj):
#         group_name = Group.objects.get(id=obj.group_id).name
#         return str(group_name)
    
#     def get_permission(self, obj):
#         permission_name = Permission.objects.get(id=obj.permission_id).name
#         return str(permission_name)

class GroupUpdateSerializer(serializers.ModelSerializer):
    name = serializers.CharField(required=False)
    permissions = serializers.CharField(required=False)
    
    class Meta:
        model = Group
        fields = ['name','permissions']

class GroupSerializer(serializers.ModelSerializer):
    permissions = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = Group
        fields = ["id", "name", "permissions"]

    def get_permissions(self, obj):
        """Fetch permissions from `auth_group_permissions`."""
        # Connect to DB to fetch data from auth group permissions
        client = MongoClient(settings.DATABASES["default"]["CLIENT"]["host"])
        db = client[settings.DATABASES["default"]["NAME"]]
        permissions_in_group = list(db.auth_group_permissions.find({"group_id": obj.id}))

        # Get permission name from AuthPermission model
        permissions = Permission.objects.filter(id__in=[perm["permission_id"] for perm in permissions_in_group])
        return PermissionSerializer(permissions, many=True).data


class UserActivateInactivateSerializer(serializers.Serializer):
    pass

class UserUpdateSerializer(serializers.ModelSerializer):
    
    groups_ids = serializers.CharField(required=False)
    permissions_ids = serializers.CharField(required=False)
    data_nascimento = serializers.DateField(required=False)
    email = serializers.EmailField(required=False)
    class Meta:
        model = User
        fields = ['first_name', 'last_name', 'bio', 'email', 'telefone', 'foto', 'data_nascimento', 'is_active', 'groups_ids', 'permissions_ids']
        extra_kwargs = {
            'password': {'write_only': True, 'required': False},
            'groups_ids': {'write_only': True, 'required': False},
            'permissions_ids': {'write_only': True, 'required': False},
        }
        

class UserCreateSerializer(serializers.ModelSerializer):
    
    groups_ids = serializers.CharField(required=False)
    permissions_ids = serializers.CharField(required=False)
    data_nascimento = serializers.DateField(required=False)
    email = serializers.EmailField(required=True)
    class Meta:
        model = User
        fields = ['first_name', 'last_name', 'bio', 'email', 'password', 'telefone', 'foto', 'data_nascimento', 'groups_ids', 'permissions_ids']
        extra_kwargs = {
            'password': {'write_only': True, 'required': True},
            'groups_ids': {'write_only': True, 'required': False},
            'permissions_ids': {'write_only': True, 'required': False},
        }

class UsuarioSerializer(serializers.ModelSerializer):
    
    # groups = serializers.ListField(
    #     child=serializers.IntegerField(), 
    #     write_only=True, 
    #     required=False
    # )
    
    # permissions = serializers.ListField(
    #     child=serializers.IntegerField(), 
    #     read_only=True, 
    #     required=False
    # )
    
    groups = serializers.SerializerMethodField(read_only=True)
    
    permissions = serializers.SerializerMethodField(read_only=True)
    
    groups_ids = serializers.CharField(write_only=True, required=False)
    permissions_ids = serializers.CharField(write_only=True, required=False)
    
    def get_groups(self, obj):
        serializer = GroupSerializer(obj.groups, many=True)
        return [{'group_id': item['id'], 'group_name': item['name']} for item in serializer.data]
    
    def get_permissions(self, obj):
        serializer = PermissionSerializer(obj.user_permissions, many=True)
        return [{'permission_id': item['id'], 'permission_name': item['name']} for item in serializer.data]
    
    class Meta:
        model = User
        exclude = ['user_permissions', 'is_staff', 'is_superuser', 'last_login', 'date_joined']
        extra_kwargs = {
            'is_superuser': {'read_only': True},
            'email': {'required': True},
            'password': {'required': True, 'write_only': True},  # ✅ Password should remain write-only
        }
        
    def validate(self, attrs):
        
        instance_email = self.instance.email if self.instance else None
        
        if 'email' in attrs:
            if User.mongo_objects.mongo_find_one({'email': attrs['email']}) and attrs['email'] != instance_email:
                raise serializers.ValidationError("Email already exists. It must be unique.")
        if 'groups_ids' in attrs:
            attrs['groups'] = [int(x) for x in attrs.pop('groups_ids').split(',')]
        if 'permissions_ids' in attrs:
            attrs['user_permissions'] = [int(x) for x in attrs.pop('permissions_ids').split(',')]
        return super().validate(attrs)

    def create(self, validated_data):
        """Delegate user creation entirely to CustomUserManager."""

        
        return User.objects.create_user(**validated_data)

    def update(self, instance, validated_data):
        """Delegate updates to CustomUserManager, especially for password handling."""
        password = validated_data.pop("password", None)
        
        instance = super().update(instance, validated_data)

        if password:
            instance.set_password(password)
            instance.save()

        return instance


class ClienteUpdateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Cliente
        exclude = ['id', 'created_at', 'updated_at', 'last_login']   
        extra_kwargs = {
            'email': {'required': False},
            'password': {'required': False, 'write_only': True},
            'nome': {'required': False},

        }


from v1.serializers import NeighborhoodSerializer, CitySerializer, StateSerializer
class ClienteSerializer(serializers.ModelSerializer):
    
    id = serializers.CharField(read_only=True)
    last_login = serializers.DateTimeField(read_only=True)
    created_at = serializers.DateTimeField(read_only=True)
    updated_at = serializers.DateTimeField(read_only=True)
    
    password = serializers.CharField(write_only=True)
    
    bairro = serializers.SerializerMethodField()
    cidade = serializers.SerializerMethodField()
    estado = serializers.SerializerMethodField()
    
    bairro_id = serializers.IntegerField(write_only=True, required=False)
    cidade_id = serializers.IntegerField(write_only=True, required=False)
    estado_id = serializers.IntegerField(write_only=True, required=False)

    def get_bairro(self, obj):
        serializer = NeighborhoodSerializer(obj.bairro, many=True)
        return [{'neighborhood_id': item['neighborhood_id'], 'neighborhood_name': item['neighborhood_name']} for item in serializer.data]
    
    def get_cidade(self, obj):
        serializer = CitySerializer(obj.cidade, many=True)
        return [{'city_id': item['city_id'], 'city_name': item['city_name']} for item in serializer.data]

    def get_estado(self, obj):
        serializer = StateSerializer(obj.estado, many=True)
        return [{'state_id': item['state_id'], 'state_name': item['state_name']} for item in serializer.data]
    
    class Meta:
        model = Cliente
        fields = '__all__'
        extra_kwargs = {
            'email': {'required': True},
            'nome': {'required': True},
            'password': {'required': False, 'write_only': True, 'allow_blank': True, 'allow_null': True},
            'is_active': {'default': True},
        }
        
    
    def validate(self, attrs):
        if 'email' in attrs:
            print("Here")
            if Cliente.objects.mongo_find_one({'email': attrs['email']}):
                raise serializers.ValidationError("Email já cadastrado")
        return super().validate(attrs)

    
    
    def create(self, validated_data):
        
        password = validated_data.pop('password', None)
            
        if 'cidade_id' in validated_data:
            validated_data['cidade'] = [validated_data.pop('cidade_id')]
        
        if 'bairro_id' in validated_data:
            validated_data['bairro'] = [validated_data.pop('bairro_id')]
            
        if 'estado_id' in validated_data:
            validated_data['estado_id'] = [validated_data.pop('estado_id')]
            
        validated_data['is_active'] = True
        validated_data['created_at'] = datetime.now()
        validated_data['updated_at'] = datetime.now()
        
        print(f"Validated data: {validated_data}")
            
        cliente = Cliente(**validated_data)
        

        if password:
            cliente.set_password(password)
            cliente.save()
        
        # Call the signals receiver after creating the user
        save_cliente.send(sender=Cliente, instance=cliente)
        
        return cliente

    def update(self, instance, validated_data):
       
        password = validated_data.pop('password', None)
            
        if 'cidade_id' in validated_data:
            validated_data['cidade_id'] = [validated_data.pop('cidade_id')]
        if 'bairro_id' in validated_data:
            validated_data['bairro_id'] = [validated_data.pop('bairro_id')]
        if 'estado' in validated_data:
            validated_data['estado_id'] = [validated_data.pop('estado_id')]
            
        validated_data['updated_at'] = datetime.now()
        
        print(f"Validated data: {validated_data}")
        
        cliente = super().update(instance, validated_data)
        
        if password:
            cliente.set_password(password)
            cliente.save()
        
        
        # Call the signals receiver after updating the user
        update_cliente.send(sender=Cliente, instance=cliente)
        
        return cliente
        


class ClienteAuthLogoutSerializer(serializers.Serializer):
    pass

class ClienteAuthCheckSerializer(serializers.Serializer):
    token = serializers.CharField(write_only=True, required=True)




from django.contrib.auth.hashers import check_password

class ClienteAuthSerializer(serializers.Serializer):
    email = serializers.EmailField()
    password = serializers.CharField(write_only=True)

    def validate(self, data):
        email = data.get("email")
        password = data.get("password")

        print(f"🔹 ClienteAuthSerializer - Email: {email}")

        try:
            user = Cliente.objects.get(email=email)
            print(f"✅ Found Cliente: {user.email}")

            if not check_password(password, user.password):
                print("❌ Password does not match stored hash")
                raise serializers.ValidationError("Invalid email or password")

            return {"user": user}

        except Cliente.DoesNotExist:
            print("❌ Cliente not found")
            raise serializers.ValidationError("Invalid email or password")






class UsuarioSimpleSerializer(serializers.ModelSerializer):
    
    class Meta:
        model = User
        fields = ['id', 'first_name', 'last_name', 'email', 'telefone', 'foto', 'is_active']
        extra_kwargs = {'password': {'write_only': True}}

    def create(self, validated_data):
        # Use the custom manager to create a user
        password = validated_data.pop('password', None)
        user = User(**validated_data)
        if password:
            user.set_password(password)
        user.save()
        return user
    
    def update(self, instance, validated_data):
        # Use the custom manager to update a user
        password = validated_data.pop('password', None)
        user = super().update(instance, validated_data)
        if password:
            user.set_password(password)
        user.save()
        return user


from rest_framework_simplejwt.serializers import TokenRefreshSerializer
from rest_framework.exceptions import ValidationError
from rest_framework_simplejwt.tokens import RefreshToken
from django.conf import settings
from pymongo import MongoClient
import jwt
from rest_framework_simplejwt.settings import api_settings
from django.contrib.auth import get_user_model
from bson import ObjectId
from datetime import datetime

User = get_user_model()

class CustomTokenRefreshSerializer(TokenRefreshSerializer):
    def validate(self, attrs):
        # Step 1: Check if "refresh" exists in attrs
        if "refresh" not in attrs:
            print("❌ 'refresh' key is missing in request data")
            raise ValidationError("'refresh' token is missing.")

        refresh_token = attrs["refresh"]
        print(f"🔎 Received refresh token: {refresh_token}")

        # Step 2: Decode refresh token to extract JTI and user email
        try:
            decoded_token = jwt.decode(
                refresh_token,
                settings.SECRET_KEY,
                algorithms=[api_settings.ALGORITHM]
            )
            jti = decoded_token.get("jti")
            token_type = decoded_token.get("token_type")
            user_email = decoded_token.get("email")
            print(f"✅ JTI extracted: {jti}")
            print(f"✅ User email extracted: {user_email}")
            print(f"✅ Token Type: {token_type}")
            if token_type != "refresh":
                print("❌ Invalid token type.")
                raise ValidationError("Invalid token type.")
        except jwt.ExpiredSignatureError:
            print("❌ Token has expired.")
            raise ValidationError("Refresh token has expired.")
        except jwt.InvalidTokenError:
            print("❌ Invalid token.")
            raise ValidationError("Invalid refresh token.")

        # Step 3: Connect to MongoDB and check blacklist
        try:
            client = MongoClient(settings.DATABASES["default"]["CLIENT"]["host"])
            db = client[settings.DATABASES["default"]["NAME"]]
            outstanding_tokens = db.token_blacklist_outstandingtoken
            blacklisted_tokens = db.token_blacklist_blacklistedtoken

            # Check if token exists in OutstandingToken collection
            token_in_db = outstanding_tokens.find_one({"jti": jti})
            if not token_in_db:
                print("❌ Old refresh token not found in database.")
                raise ValidationError("Invalid refresh token.")

            token_id = token_in_db.get("id")
            print(f"✅ Found token_id: {token_id}")

            # Check if token is blacklisted using token_id
            is_blacklisted = blacklisted_tokens.find_one({"token_id": token_id}) is not None
            if is_blacklisted:
                print("⚠️ Token is already blacklisted.")
                raise ValidationError("This token is already blacklisted.")
            else:
                print("✅ Token is not blacklisted.")
        except Exception as e:
            print(f"❌ Failed to check token blacklist: {e}")
            raise ValidationError(f"Error checking token blacklist. {e}")

        # Step 4: Fetch user using the user email instead of user ID
        try:
            user = User.objects.get(email=user_email)
            print(f"✅ User fetched successfully: {user.email}")
        except User.DoesNotExist:
            print("❌ User not found.")
            raise ValidationError("User not found.")
        except Exception as e:
            print(f"❌ Failed to fetch user: {e}")
            raise ValidationError("Failed to fetch user.")

        # Step 5: Generate new access and refresh tokens
        try:
            new_refresh = RefreshToken.for_user(user)
            new_access = str(new_refresh.access_token)
            # Update outstanding token with new access token and access_jti
            new_decoded_token = jwt.decode(
                str(new_refresh),
                settings.SECRET_KEY,
                algorithms=[api_settings.ALGORITHM]
            )
            new_jti = new_decoded_token.get("jti")
            new_access_decoded_token = jwt.decode(
                str(new_access),
                settings.SECRET_KEY,
                algorithms=[api_settings.ALGORITHM]
            )
            new_access_jti = new_access_decoded_token.get("jti")
            print(f"New Access JTI: {new_access_jti}")
            outstanding_tokens.update_one(
                {"token": str(new_refresh)},
                {"$set": {
                    "access_jti": new_access_jti,
                    "access_token": new_access
                }}
            )
            print("✅ New access and refresh tokens generated.")
        except Exception as e:
            print(f"❌ Failed to generate new tokens: {e}")
            raise ValidationError("Failed to generate new tokens.")

        # Step 6: Blacklist the old refresh token AFTER generating new tokens
        try:
            if not blacklisted_tokens.find_one({"token_id": token_id}):
                # get the last id from the collection
                last_id = blacklisted_tokens.find_one(sort=[("token_id", -1)])
                if last_id:
                    new_id = last_id["id"] + 1
                else:
                    new_id = 1
                # Insert token_id and blacklisted_at as ISODate into the blacklist
                blacklisted_tokens.insert_one({
                    "id": new_id,
                    "token_id": token_id,
                    "blacklisted_at": datetime.now()
                })
                # Update outstanding token to include blacklist_id and blacklisted at information
                outstanding_tokens.update_one(
                    {"id": token_id},
                    {"$set": {
                        "blacklist_id": new_id,
                        "blacklisted_at": datetime.now()
                    }}
                )
                print(f"✅ Old refresh token blacklisted successfully.")
            else:
                print("⚠️ Token is already in the blacklist.")
        except Exception as e:
            print(f"❌ Failed to blacklist token: {e}")
            raise ValidationError("Failed to blacklist the token.")

        # Step 7: Return new tokens in the response
        return {
            "access": new_access,
            "refresh": str(new_refresh)
        }


class UserActionLogSerializer(serializers.ModelSerializer):
    id = serializers.CharField(read_only=True)
    user = serializers.CharField(source='user.id', read_only=True)  # ✅ Ensure it returns MongoDB ID

    class Meta:
        model = UserActionLog
        fields = '__all__'

    # def get_id(self, obj):
    #     """ Return MongoDB _id as a string for the id field """
    #     print(f"Id: {obj.id}")
    #     return str(obj.id) if isinstance(obj.id, ObjectId) else None

    # def to_representation(self, instance):
    #     """ Convert ObjectId fields to strings """
    #     ret = super().to_representation(instance)
    #     for key, value in ret.items():
    #         if isinstance(value, ObjectId):
    #             ret[key] = str(value)
    #     return ret
