from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group, Permission
from django.db.models import Q
from rest_framework import serializers

from apps.catalogos.models import Oficina
from apps.catalogos.serializers import OficinaSerializer
from apps.usuarios.models import Profile
from apps.usuarios.permissions import ADMIN_GROUP_NAME, is_admin_user

User = get_user_model()


class GroupSerializer(serializers.ModelSerializer):
    class Meta:
        model = Group
        fields = ["id", "name"]


class PermissionSerializer(serializers.ModelSerializer):
    app_label = serializers.CharField(source="content_type.app_label", read_only=True)
    model = serializers.CharField(source="content_type.model", read_only=True)

    class Meta:
        model = Permission
        fields = ["id", "name", "codename", "app_label", "model"]


class UserSerializer(serializers.ModelSerializer):
    groups = GroupSerializer(many=True, read_only=True)

    class Meta:
        model = User
        fields = [
            "id",
            "username",
            "email",
            "first_name",
            "last_name",
            "is_active",
            "is_staff",
            "is_superuser",
            "groups",
            "last_login",
            "date_joined",
        ]
        read_only_fields = ["is_superuser", "last_login", "date_joined"]


class UserCreateSerializer(serializers.ModelSerializer):
    password = serializers.CharField(write_only=True, min_length=8)
    groups = serializers.ListField(
        child=serializers.CharField(),
        required=False,
        allow_empty=True,
        write_only=True,
        error_messages={
            "not_a_list": "El campo 'groups' debe ser una lista de nombres de grupos.",
        },
    )
    oficinas = serializers.PrimaryKeyRelatedField(
        source="profile.oficinas",
        queryset=Oficina.objects.all(),
        many=True,
        required=False,
        write_only=True,
    )

    class Meta:
        model = User
        fields = [
            "id",
            "username",
            "email",
            "first_name",
            "last_name",
            "is_active",
            "is_staff",
            "password",
            "groups",
            "oficinas",
        ]

    def validate_password(self, value):
        if not all(
            (
                any(character.islower() for character in value),
                any(character.isupper() for character in value),
                any(character.isdigit() for character in value),
                any(not character.isalnum() and not character.isspace() for character in value),
            )
        ):
            raise serializers.ValidationError(
                "La contraseña debe contener al menos una letra minúscula, una letra mayúscula, un número y un carácter especial."
            )
        return value

    def create(self, validated_data):
        password = validated_data.pop("password")
        group_names = validated_data.pop("groups", None)
        profile_data = validated_data.pop("profile", {})
        oficinas = profile_data.get("oficinas", [])

        resolved_groups = None
        if group_names is not None:
            resolved_groups = list(Group.objects.filter(name__in=group_names))
            missing = sorted(set(group_names) - set([g.name for g in resolved_groups]))
            if missing:
                raise serializers.ValidationError(
                    {"detail": "Algunos grupos no existen.", "missing": missing}
                )

        user = User(**validated_data)
        user.set_password(password)
        user.save()

        if resolved_groups is not None:
            user.groups.set(resolved_groups)

        profile, _ = Profile.objects.get_or_create(user=user)
        if "oficinas" in profile_data:
            profile.oficinas.set(oficinas)
        return user


class UserUpdateSerializer(serializers.ModelSerializer):
    groups = serializers.ListField(
        child=serializers.CharField(),
        required=False,
        allow_empty=True,
        write_only=True,
        error_messages={
            "not_a_list": "El campo 'groups' debe ser una lista de nombres de grupos.",
        },
    )
    oficinas = serializers.PrimaryKeyRelatedField(
        source="profile.oficinas",
        queryset=Oficina.objects.all(),
        many=True,
        required=False,
        write_only=True,
    )

    class Meta:
        model = User
        fields = [
            "id",
            "username",
            "email",
            "first_name",
            "last_name",
            "is_active",
            "is_staff",
            "groups",
            "oficinas",
        ]

    def update(self, instance, validated_data):
        group_names = validated_data.pop("groups", None)
        profile_data = validated_data.pop("profile", None)

        resolved_groups = None
        if group_names is not None:
            current_admin = is_admin_user(instance)
            will_be_admin = ADMIN_GROUP_NAME in group_names or instance.is_superuser

            if current_admin and not will_be_admin:
                admin_group = Group.objects.filter(name=ADMIN_GROUP_NAME).first()
                if admin_group:
                    admins_count = (
                        User.objects.filter(is_active=True)
                        .filter(Q(is_superuser=True) | Q(groups=admin_group))
                        .distinct()
                        .count()
                    )
                    if admins_count <= 1:
                        raise serializers.ValidationError(
                            {
                                "detail": "No se puede remover el rol 'Administrador' porque quedarías sin administradores activos."
                            }
                        )

            resolved_groups = list(Group.objects.filter(name__in=group_names))
            missing = sorted(set(group_names) - set([g.name for g in resolved_groups]))
            if missing:
                raise serializers.ValidationError(
                    {"detail": "Algunos grupos no existen.", "missing": missing}
                )

        user = super().update(instance, validated_data)

        if resolved_groups is not None:
            user.groups.set(resolved_groups)

        profile, _ = Profile.objects.get_or_create(user=user)

        if profile_data is not None and "oficinas" in profile_data:
            oficinas = profile_data.get("oficinas", [])
            profile.oficinas.set(oficinas)

        return user


class UserAdminSerializer(UserSerializer):
    oficinas = serializers.SerializerMethodField()

    class Meta(UserSerializer.Meta):
        fields = UserSerializer.Meta.fields + ["oficinas"]

    def get_oficinas(self, obj):
        if getattr(obj, "is_superuser", False):
            oficinas = Oficina.objects.filter(activa=True).order_by("codigo")
            return OficinaSerializer(oficinas, many=True).data

        profile = getattr(obj, "profile", None)
        if profile is None:
            return []
        oficinas = profile.oficinas.filter(activa=True).order_by("codigo")
        return OficinaSerializer(oficinas, many=True).data


class ProfileSerializer(serializers.ModelSerializer):
    user = UserSerializer(read_only=True)

    class Meta:
        model = Profile
        fields = ["id", "user", "telefono", "avatar", "created_at", "updated_at"]
        read_only_fields = ["created_at", "updated_at"]


class MeResponseSerializer(UserSerializer):
    telefono = serializers.SerializerMethodField()
    oficinas = serializers.SerializerMethodField()
    permissions = serializers.SerializerMethodField()

    class Meta(UserSerializer.Meta):
        fields = UserSerializer.Meta.fields + ["telefono", "oficinas", "permissions"]

    def get_telefono(self, obj):
        profile = getattr(obj, "profile", None)
        if profile is None:
            return ""
        return getattr(profile, "telefono", "") or ""

    def get_oficinas(self, obj):
        if getattr(obj, "is_superuser", False):
            oficinas = Oficina.objects.filter(activa=True).order_by("codigo")
            return OficinaSerializer(oficinas, many=True).data

        profile = getattr(obj, "profile", None)
        if profile is None:
            return []
        oficinas = profile.oficinas.filter(activa=True).order_by("codigo")
        return OficinaSerializer(oficinas, many=True).data

    def get_permissions(self, obj):
        if not obj:
            return []
        perms = obj.get_all_permissions() if hasattr(obj, "get_all_permissions") else set()
        return sorted(list(perms))


class MeUpdateSerializer(serializers.Serializer):
    first_name = serializers.CharField(required=False, allow_blank=True)
    last_name = serializers.CharField(required=False, allow_blank=True)
    email = serializers.EmailField(required=False, allow_blank=True)
    telefono = serializers.CharField(required=False, allow_blank=True)

    def update(self, instance, validated_data):
        user = instance
        profile, _ = Profile.objects.get_or_create(user=user)

        for f in ("first_name", "last_name", "email"):
            if f in validated_data:
                setattr(user, f, validated_data[f])
        user.save()

        if "telefono" in validated_data:
            profile.telefono = validated_data["telefono"]
            if profile.created_by_id is None:
                profile.created_by = user
            profile.updated_by = user
            profile.save()

        return user

    def create(self, validated_data):
        raise NotImplementedError()


class SetRolesSerializer(serializers.Serializer):
    groups = serializers.ListField(
        child=serializers.CharField(),
        required=False,
        default=list,
        allow_empty=True,
        error_messages={
            "not_a_list": "El campo 'groups' debe ser una lista de nombres de grupos.",
        },
    )


class SetOficinasSerializer(serializers.Serializer):
    oficinas = serializers.ListField(
        child=serializers.IntegerField(),
        required=False,
        default=list,
        allow_empty=True,
        error_messages={
            "not_a_list": "El campo 'oficinas' debe ser una lista de IDs de oficinas.",
        },
    )


class SetGroupPermissionsSerializer(serializers.Serializer):
    permissions = serializers.ListField(
        child=serializers.CharField(),
        required=False,
        default=list,
        allow_empty=True,
        error_messages={
            "not_a_list": "El campo 'permissions' debe ser una lista de strings tipo 'app_label.codename'.",
        },
    )
