import re
from rest_framework import serializers
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from .models import Users, Roles, User_Roles, Role_Permissions
from tenants.models import Tenants
from django.utils.crypto import get_random_string


# --- Core User Serializer ---
class UsersSerializer(serializers.ModelSerializer):
    class Meta:
        model = Users
        # Using __all__ for simplicity, but we will override password handling
        fields = [
            "user_id",
            "username",
            "email",
            "password",
            "is_staff",
            "is_active",
            "created_at",
        ]
        read_only_fields = ["user_id", "created_at", "updated_at"]
        extra_kwargs = {"password": {"write_only": True}}

    def create(self, validated_data):
        # Create a new user with a hashed password
        user = Users.objects.create_user(**validated_data)
        return user

    def update(self, instance, validated_data):
        # Update user fields, and hash the new password if provided
        if "password" in validated_data:
            password = validated_data.pop("password")
            instance.set_password(password)
        return super().update(instance, validated_data)


# This is a nested serializer for handling permissions within a role
# It is used for reading (GET) a role to display its permissions.
class RolePermissionsSerializer(serializers.ModelSerializer):
    # This field is used to create the permission string like "module_name:action"
    permission_string = serializers.SerializerMethodField()

    class Meta:
        model = Role_Permissions
        fields = ["module_name", "action", "permission_string"]

    def get_permission_string(self, obj):
        return f"{obj.module_name}:{obj.action}"


# --- Roles Serializer ---
class RolesSerializer(serializers.ModelSerializer):
    # This field is used for writing (creating) a role. It expects a list of
    # permission strings from the frontend, e.g., ["complaints:read", "polls:create"].
    # write_only=True ensures this field is not included when a role is read.
    permissions = serializers.ListField(
        child=serializers.CharField(), write_only=True, required=False
    )

    # This field is used for reading (fetching) a role. It will display the
    # associated permissions using the nested RolePermissionsSerializer.
    role_permissions = RolePermissionsSerializer(
        many=True, read_only=True, source="role_permissions_set"
    )

    class Meta:
        model = Roles
        fields = [
            "role_id",
            "role_name",
            "description",
            "permissions",
            "role_permissions",
        ]
        read_only_fields = ["role_id"]

    def create(self, validated_data):
        # Pop the permissions data from validated_data to handle it separately
        permissions_data = validated_data.pop("permissions", [])

        # Create the new role first with the remaining validated data
        role = Roles.objects.create(**validated_data)

        # Now, iterate over the list of permission strings and create a
        # Role_Permissions object for each one.
        for permission_string in permissions_data:
            try:
                # Assuming the permission string is in the format "module_name:action"
                module_name, action = permission_string.split(":")
                Role_Permissions.objects.create(
                    role=role, module_name=module_name, action=action
                )
            except ValueError:
                # Raise a validation error if the format is incorrect
                raise serializers.ValidationError(
                    f"Invalid permission format: {permission_string}. Expected 'module:action'."
                )

        return role


# --- User Roles Serializer ---
class UserRolesSerializer(serializers.ModelSerializer):
    user = serializers.StringRelatedField(read_only=True)
    role = serializers.StringRelatedField(read_only=True)
    tenant = serializers.StringRelatedField(read_only=True)

    class Meta:
        model = User_Roles
        fields = "__all__"
        read_only_fields = ["assigned_at"]


# This serializer is for creating/updating the User_Roles relationship
class UserRolesCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = User_Roles
        fields = ["user", "role", "tenant"]


class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)

        # Add custom claims to the token payload
        token["username"] = user.username

        # Get user's roles and tenants
        user_roles_query = user.user_roles_set.select_related("role", "tenant").all()

        # If the user has no roles, this will be an empty list, which is correct
        roles_data = []
        if user_roles_query:
            for ur in user_roles_query:
                roles_data.append(
                    {
                        "role_name": ur.role.role_name,
                        "tenant_id": str(ur.tenant.tenant_id),
                        "tenant_name": ur.tenant.name,
                    }
                )

        token["roles"] = roles_data

        return token


# New: Serializer for public citizen signup
class UserSignupSerializer(serializers.ModelSerializer):
    class Meta:
        model = Users
        fields = ("username", "email", "password")
        extra_kwargs = {
            "password": {"write_only": True}
        }  # prevent password from being read back

    def validate(self, data):
        """
        Validates password strength and that password and confirm_password match.
        """
        password = data.get("password")
        confirm_password = data.get("confirm_password")

        # Check if passwords match
        if password and confirm_password and password != confirm_password:
            raise serializers.ValidationError("Passwords do not match.")

        # Check for password strength requirements
        if password:
            # At least one uppercase letter
            if not re.search(r"[A-Z]", password):
                raise serializers.ValidationError(
                    "Password must contain at least one uppercase letter."
                )

            # At least one lowercase letter
            if not re.search(r"[a-z]", password):
                raise serializers.ValidationError(
                    "Password must contain at least one lowercase letter."
                )

            # At least one digit
            if not re.search(r"[0-9]", password):
                raise serializers.ValidationError(
                    "Password must contain at least one number."
                )

            # Or you can use a simpler, more common regex:
            if not re.search(r'[!@#$%^&*()_+\-=\[\]{};\':"\\|,.<>/?`~]', password):
                raise serializers.ValidationError(
                    "Password must contain at least one special character, e.g., !@#$%^&*()."
                )

            # You might also want to add a minimum length check
            if len(password) < 8:
                raise serializers.ValidationError(
                    "Password must be at least 8 characters long."
                )

        return data

    def create(self, validated_data):
        # Override the default create method to handle the password hashing
        password = validated_data.pop("password", None)
        user = Users.objects.create(**validated_data)
        if password is not None:
            user.set_password(password)
            user.save()
        return user


#  Serializer for Officer Invitation ---
class OfficerInvitationSerializer(serializers.Serializer):
    """
    Serializer to handle the creation of a new officer user and their
    initial role assignment. This is used by an admin user.
    """

    # The officer's email, which will be used to create their user account
    email = serializers.EmailField()

    # The primary key of the role to assign to the officer
    # It validates that the provided role_id exists in the Roles model
    role_id = serializers.PrimaryKeyRelatedField(
        queryset=Roles.objects.all(),
        source="role",  # This links the field to the 'role' field in the validated data
    )

    # The primary key of the tenant (ministry) to link the officer to
    # It validates that the provided tenant_id exists in the Tenants model
    tenant_id = serializers.PrimaryKeyRelatedField(
        queryset=Tenants.objects.all(),
        source="tenant",  # This links the field to the 'tenant' field in the validated data
    )

    def validate_email(self, value):
        """
        Check that the email is not already in use by an active user.
        """
        # The user is considered 'active' if they have a password assigned,
        # but for this flow, we will check if the email exists at all.
        if Users.objects.filter(email=value).exists():
            raise serializers.ValidationError("A user with this email already exists.")
        return value

    def create(self, validated_data):
        """
        Creates a new user account without a password and assigns their initial
        role and tenant.
        """
        # Pop the role and tenant objects from the validated data
        # 'validated_data' now contains: {'email': '...', 'role': <Role object>, 'tenant': <Tenant object>}
        role = validated_data.pop("role")
        tenant = validated_data.pop("tenant")
        email = validated_data.get("email")

        # Create the new user.
        # We set an unusable password and is_active to False.
        # We also set is_staff to True to distinguish them from citizens.
        random_password = get_random_string(16)
        user = Users.objects.create_user(
            email=email,
            username=email,  # Use email as the default username
            password=random_password,
            is_active=False,
            is_staff=True,
        )

        # Create the User_Roles entry to link the user, role, and tenant.
        User_Roles.objects.create(user=user, role=role, tenant=tenant)

        # Return the created user object
        return user


class OfficerPasswordSetupSerializer(serializers.Serializer):
    """
    Serializer to handle the setting of a new password by an officer
    after they have been invited.
    """

    password = serializers.CharField(
        write_only=True, required=True, style={"input_type": "password"}, min_length=8
    )
    confirm_password = serializers.CharField(
        write_only=True, required=True, style={"input_type": "password"}
    )

    def validate(self, data):
        """
        Validates password strength and that password and confirm_password match.
        """
        password = data.get("password")
        confirm_password = data.get("confirm_password")

        # Check if passwords match
        if password and confirm_password and password != confirm_password:
            raise serializers.ValidationError("Passwords do not match.")

        # Check for password strength requirements
        if password:
            # At least one uppercase letter
            if not re.search(r"[A-Z]", password):
                raise serializers.ValidationError(
                    "Password must contain at least one uppercase letter."
                )

            # At least one lowercase letter
            if not re.search(r"[a-z]", password):
                raise serializers.ValidationError(
                    "Password must contain at least one lowercase letter."
                )

            # At least one digit
            if not re.search(r"[0-9]", password):
                raise serializers.ValidationError(
                    "Password must contain at least one number."
                )

            # At least one special character
            if not re.search(r'[!@#$%^&*()_+\-=\[\]{};\':"\\|,.<>/?`~]', password):
                raise serializers.ValidationError(
                    "Password must contain at least one special character, e.g., !@#$%^&*()."
                )

        return data


# permission serializer
class PermissionsSyncSerializer(serializers.Serializer):
    permissions = serializers.ListField(
        child=serializers.DictField(),  # expects {"module_name": "...", "action": "..."}
        required=True,
    )
