# coding: utf-8


import logging

from django.db.models import Q
from django.db.models.functions import Coalesce
from tastypie.authorization import Authorization

from idm.api.exceptions import BadRequest
from idm.api.frontend.apifields import (SystemForeignKey, GroupForeignKey, UserForeignKey, RoleForeignKey,
                                        RoleNodeForeignKey)
from idm.api.frontend.base import FrontendApiResource
from idm.api.frontend.forms import InconsistencyForm
from idm.api.frontend.utils import OrderingAlias, OrderingField, fix_user_field_filters
from idm.core.workflow.exceptions import Forbidden
from idm.core.models import System
from idm.inconsistencies.models import Inconsistency

log = logging.getLogger(__name__)


class InconsistencyResource(FrontendApiResource):
    """
    Ресурс несоответствий
    """
    system = SystemForeignKey()
    user = UserForeignKey()
    group = GroupForeignKey()
    our_role = RoleForeignKey(attribute='our_role')
    role = RoleForeignKey()
    node = RoleNodeForeignKey()

    class Meta(FrontendApiResource.Meta):
        abstract = False
        authorization = Authorization()
        object_class = Inconsistency
        queryset = (
            Inconsistency.objects.select_related('system', 'user', 'group', 'node', 'our_role').
            select_related(
                'user',
                'group',
                'system',
                'node__system',
                'our_role__user',
                'our_role__group',
                'our_role__node',
                'our_role__node__system',
                'our_role__system',
                'our_role__parent__user',
                'our_role__parent__group',
                'our_role__parent__node__system',
                'our_role__parent__system',
            ).prefetch_related(
                'role__user',
                'role__group',
                'role__node',
                'role__node__system',
                'role__system',
                'role__parent__user',
                'role__parent__group',
                'role__parent__node__system',
                'role__parent__system',
            ).
            order_by('id')
        )
        resource_name = 'inconsistencies'
        list_allowed_methods = ('get',)
        detail_allowed_methods = ('get', 'delete')
        fields = ['id', 'system',
                  'ident_type',
                  'remote_username', 'remote_group',
                  'remote_path', 'remote_data', 'remote_fields',
                  'user', 'group', 'node', 'our_role', 'role',
                  'type', 'state',
                  'added', 'updated']
        ordering = ['type', 'state', 'added', 'updated', 'user', 'group', 'subject']
        ordering_aliases = {
            'user': OrderingAlias(OrderingField(
                Coalesce('our_role__user__username', 'user__username', 'remote_username'),
                name='user'
            )),
            'group': OrderingAlias(OrderingField(
                Coalesce('our_role__group__external_id', 'group__external_id', 'remote_group'),
                name='group',
            )),
            'subject': OrderingAlias(
                OrderingField(
                    Coalesce(
                        'our_role__user__username',
                        'user__username',
                        'remote_username',
                    ),
                    name='subject_str',
                ),
                OrderingField(
                    Coalesce(
                        'our_role__group__external_id',
                        'group__external_id',
                        'remote_group'
                    ),
                    name='subject_int'
                )
            )
        }

    def build_filters(self, request, filters=None):
        requester = self.get_requester(request)

        fix_user_field_filters(filters, ['user'])

        form = InconsistencyForm(filters)
        if not form.is_valid():
            raise BadRequest(form.errors)

        systems = System.objects.permitted_for(requester, 'core.idm_view_roles')
        qset_filters = Q(system__in=systems)
        query = form.cleaned_data

        if query['state']:
            qset_filters &= Q(state__in=query['state'])

        if query['type']:
            qset_filters &= Q(type__in=query['type'])

        if query['system']:
            qset_filters &= Q(system=query['system'])

        if query['user_type']:
            local_user_q = Q(user__username__in=query['user'], user__type=query['user_type'])
            remote_user_q = Q(remote_username__in=query['user'], remote_subject_type=query['user_type'])
        else:
            local_user_q = Q(user__username__in=query['user'])
            remote_user_q = Q(remote_username__in=query['user'])

        if query['subject']:
            subject_q = (
                remote_user_q |
                local_user_q |
                Q(remote_group__in=query['group']) |
                Q(group__external_id__in=query['group'])
            )
            qset_filters &= subject_q
        elif query['user'] or query['group']:
            if query['user']:
                qset_filters &= remote_user_q | local_user_q
            if query['group']:
                qset_filters &= Q(remote_group__in=query['group']) | Q(group__external_id__in=query['group'])
        elif query['user_type']:
            qset_filters &= Q(user__type=query['user_type']) | Q(remote_subject_type=query['user_type'])

        if query['new_role']:
            qset_filters &= Q(role=query['new_role'])

        if query['our_role']:
            qset_filters &= Q(our_role=query['our_role'])

        if query['role']:
            qset_filters &= Q(node__rolenodeclosure_parents__parent=query['role'])

        return qset_filters

    def apply_filters(self, request, applicable_filters, **kwargs):
        return self.get_object_list(request).filter(applicable_filters)

    def dehydrate(self, bundle):
        bundle.data['human_state'] = bundle.obj.get_state_display()
        bundle.data['human_type'] = bundle.obj.get_type_display()
        bundle.data['remote_node'] = bundle.obj.get_node_ident()
        return bundle

    def delete_detail(self, request, **kwargs):
        requester = self.get_requester(request)
        if not requester.impersonated.has_perm('core.resolve_inconsistencies'):
            raise Forbidden('Only superuser can delete inconsistencies')
        return super(InconsistencyResource, self).delete_detail(request, **kwargs)
