import typing
from collections import defaultdict

from django.db.models.query import QuerySet
from rest_framework import serializers

from apps.authentication.api.v1.serializers import serializer as auth_serializer
from apps.authorization.models import (
    Role,
    Permissions,
    UserRelations,
    Page
)


class PageSerializer(serializers.ModelSerializer):
    """ Serialize every front-end page """

    class Meta:
        model = Page
        fields = [
            'id',
            'name',
            'code',
            'is_active'
        ]

    def to_representation(self, instance):
        """ custom output of serializer """
        representation = super().to_representation(instance)
        representation['permissions'] = PermissionSerializer(
            Permissions.objects.filter(page=instance),
            many=True
        ).data

        return representation


class PermissionSerializer(serializers.ModelSerializer):
    """ Serialize permissions """

    class Meta:
        model = Permissions
        fields = [
            'id',
            'name',
            'description',
            'category',
            'page',
            'is_active',
            'modify_state'
        ]

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        representation['page'] = instance.page.name

        return representation

    @classmethod
    def permissions_structure_output(cls, permissions) -> typing.List[dict]:
        """ set a structure for permissions """

        if isinstance(permissions, QuerySet):
            permissions = permissions.select_related('page')

        grouped = defaultdict(set)
        modifier_states = defaultdict(set)

        for permission in permissions:
            if getattr(permission, 'is_active', True):

                page_name = getattr(permission.page, 'name', None)

                if page_name:
                    grouped[page_name].add(permission.name)
                    grouped[f'modify_state_{page_name}'].add(permission.modify_state)
                    modifier_states[page_name].add(permission.modify_state)

        structure = []
        for page, access in grouped.items():
            if page.startswith('modify_state_'):
                continue

            modify_states = grouped.get(f'modify_state_{page}', set())

            if False not in modify_states:
                continue

            structure.append({
                'page_name': page,
                'page_access': sorted(list(access)),
            })
        return structure


class RoleSerializer(serializers.ModelSerializer):
    """ Serialize roles of user """

    class Meta:
        model = Role
        fields = [
            'id',
            'role_name',
            'parent_role',
            'description',
            'type',
            'permissions'  # noqa
        ]
        extra_kwargs = {
            'permissions': {'required': False}  # permissions not required for some roles  # noqa
        }

    def to_representation(self, instance):
        """
        using @to_representation for many_to_many permissions in response
        """
        representation = super().to_representation(instance)
        representation['type'] = auth_serializer.OrganizationTypeSerializer(instance.type).data
        representation['permissions'] = PermissionSerializer(instance.permissions, many=True).data
        if instance.parent_role:
            representation['parent_role'] = {'name': instance.parent_role.role_name, 'id': instance.parent_role.id}
        return representation


class UserRelationSerializer(serializers.ModelSerializer):
    """ Serialize relations of user like: organizations, roles, permissions """

    class Meta:
        model = UserRelations
        fields = [
            'id',
            'user',
            'organization',
            'role',
            'permissions',  # noqa
        ]

        extra_kwargs = {
            'organization': {
                'required': False
            },
            'permissions': {  # noqa
                'required': False
            },
            'role': {
                'required': False
            }
        }

    def to_representation(self, instance):
        """ custom output for serializer """

        representation = super().to_representation(instance)
        if isinstance(instance, UserRelations):
            if instance.user:
                representation['user'] = auth_serializer.UserSerializer(instance.user).data
            if instance.organization:
                representation['organization'] = {"id": instance.organization.id, "name": instance.organization.name}
            if instance.role:
                representation['role'] = {
                    "id": instance.role.id, "role_name": instance.role.role_name, 'type': {
                        'key': instance.role.type.key
                    }
                }
            if instance.permissions:  # noqa
                # set permissions by a default structure like:
                # 'page permission':[element permissions]
                permissions = instance.permissions.filter(is_active=True)
                representation['permissions'] = PermissionSerializer.permissions_structure_output(
                    list(permissions) + list(instance.role.permissions.all())
                )

        return representation

    def update(self, instance, validated_data):
        """ update user relation object """

        instance.role = validated_data.get('role', instance.role)

        instance.organization = validated_data.get('organization', instance.organization)
        instance.save()

        if validated_data.get('permissions'):
            instance.permissions.clear()
            instance.permissions.add(*(validated_data.get('permissions', instance.permissions)))
        return instance
