from django.db.models import Q, Count
from django.utils.translation import gettext as _
from rest_framework.decorators import action
from rest_framework.exceptions import PermissionDenied

from common.api import JMSModelViewSet
from orgs.utils import current_org
from .permission import PermissionViewSet
from ..filters import RoleFilter
from ..models import Role, SystemRole, OrgRole, RoleBinding
from ..serializers import RoleSerializer, RoleUserSerializer

__all__ = [
    'RoleViewSet', 'SystemRoleViewSet', 'OrgRoleViewSet',
    'SystemRolePermissionsViewSet', 'OrgRolePermissionsViewSet',
]


class RoleViewSet(JMSModelViewSet):
    queryset = Role.objects.all()
    serializer_classes = {
        'default': RoleSerializer,
        'users': RoleUserSerializer,
    }
    ordering = ('-builtin', 'name')
    filterset_class = RoleFilter
    search_fields = ('name', 'builtin')
    rbac_perms = {
        'users': 'rbac.view_rolebinding'
    }

    def perform_destroy(self, instance):
        from orgs.utils import tmp_to_root_org
        if instance.builtin:
            error = _("Internal role, can't be destroy")
            raise PermissionDenied(error)

        with tmp_to_root_org():
            if instance.users.count() >= 1:
                error = _("The role has been bound to users, can't be destroy")
                raise PermissionDenied(error)
        return super().perform_destroy(instance)

    def perform_create(self, serializer):
        super(RoleViewSet, self).perform_create(serializer)
        self.set_permissions_if_need(serializer.instance)

    def set_permissions_if_need(self, instance):
        if not isinstance(instance, Role):
            return
        clone_from = self.request.query_params.get('clone_from')
        if not clone_from:
            return
        clone = Role.objects.filter(id=clone_from).first()
        if not clone:
            return
        instance.permissions.set(clone.get_permissions())

    def filter_builtins(self, queryset):
        keyword = self.request.query_params.get('search')
        if not keyword:
            return queryset

        builtins = list(self.get_queryset().filter(builtin=True))
        matched = [role.id for role in builtins if keyword in role.display_name]
        if not matched:
            return queryset
        queryset = list(queryset.exclude(id__in=matched))
        return queryset + builtins

    def filter_queryset(self, queryset):
        queryset = super().filter_queryset(queryset)
        queryset = queryset.order_by(*self.ordering)
        queryset = self.filter_builtins(queryset)
        return queryset

    def set_users_amount(self, queryset):
        """设置角色的用户绑定数量,以减少查询"""
        ids = [role.id for role in queryset]
        queryset = Role.objects.filter(id__in=ids).order_by(*self.ordering)
        org_id = current_org.id
        if current_org.is_root():
            q = Q(role__scope=Role.Scope.system) | Q(role__scope=Role.Scope.org)
        else:
            q = Q(role__scope=Role.Scope.system) | Q(role__scope=Role.Scope.org, org_id=org_id)
        role_bindings = RoleBinding.objects_raw.filter(q).values_list('role_id').annotate(
            user_count=Count('user_id', distinct=True)
        )
        role_user_amount_mapper = {role_id: user_count for role_id, user_count in role_bindings}
        queryset = queryset.annotate(permissions_amount=Count('permissions', distinct=True))
        queryset = list(queryset)
        for role in queryset:
            role.users_amount = role_user_amount_mapper.get(role.id, 0)
        return queryset

    def get_serializer(self, *args, **kwargs):
        if len(args) == 1 and kwargs.get('many', False):
            queryset = self.set_users_amount(args[0])
            args = (queryset,)
        return super().get_serializer(*args, **kwargs)

    def perform_update(self, serializer):
        instance = serializer.instance
        if instance.builtin:
            error = _("Internal role, can't be update")
            raise PermissionDenied(error)
        return super().perform_update(serializer)

    @action(methods=['GET'], detail=True)
    def users(self, *args, **kwargs):
        role = self.get_object()
        queryset = role.users
        return self.get_paginated_response_from_queryset(queryset)


class SystemRoleViewSet(RoleViewSet):
    perm_model = SystemRole

    def get_queryset(self):
        qs = super().get_queryset().filter(scope='system')
        return qs


class OrgRoleViewSet(RoleViewSet):
    perm_model = OrgRole

    def get_queryset(self):
        qs = super().get_queryset().filter(scope='org')
        return qs


class BaseRolePermissionsViewSet(PermissionViewSet):
    model = None
    role_pk = None
    filterset_fields = []
    http_method_names = ['get', 'option']
    check_disabled = False

    def get_queryset(self):
        role_id = self.kwargs.get(self.role_pk)
        if not role_id:
            return self.model.objects.none()

        role = self.model.objects.get(id=role_id)
        self.scope = role.scope
        self.check_disabled = role.builtin
        queryset = role.get_permissions() \
            .prefetch_related('content_type')
        return queryset


# Sub view set
class SystemRolePermissionsViewSet(BaseRolePermissionsViewSet):
    role_pk = 'system_role_pk'
    model = SystemRole
    rbac_perms = (
        ('get_tree', 'rbac.view_permission'),
    )


# Sub view set
class OrgRolePermissionsViewSet(BaseRolePermissionsViewSet):
    role_pk = 'org_role_pk'
    model = OrgRole
    rbac_perms = (
        ('get_tree', 'rbac.view_permission'),
    )