from typing import Optional, List

from django.db.models import Q

from idm.celery_app import app
from idm.core import exceptions
from idm.core.constants.approverequest import APPROVEREQUEST_DECISION, PriorityType
from idm.core.constants.role import ROLE_STATE
from idm.core.models import ApproveRequest, System, RoleNode
from idm.framework.requester import Requester
from idm.framework.task import BaseTask
from idm.users.models import User


class ApproveRequestBulk(BaseTask):
    queryset = ApproveRequest.objects.select_related(
        'approve__role_request__role__system',
        'approve__role_request__role__user',
        'approve__role_request__role__node',
        'approve__role_request__role',
        'approver',
    )

    def init(
            self,
            requester_pk: int,
            priority_type: str,
            decision: str,
            username: str = None,
            system_slug: str = None,
            path: str = None,
            role_ids: List[int] = None,
            comment: str = None,
            chunk_size: int = None,
    ):
        qs_filters = Q(
            approver__pk=requester_pk,
            approve__approved__isnull=True,
            approve__role_request__role__state__in=ROLE_STATE.STATES_FOR_DISCUSSION,
        ) & PriorityType(priority_type).queryset_filter

        if username:
            qs_filters &= Q(approve__role_request__role__user__username=username)

        if system_slug:
            system = System.objects.get(slug=system_slug)
            qs_filters &= Q(approve__role_request__role__system__slug=system_slug)
            if path:
                path = RoleNode.objects.get_node_by_value_path(system, path)
                qs_filters &= Q(approve__role_request__role__node__rolenodeclosure_parents__parent=path)

        if role_ids:
            qs_filters &= Q(approve__role_request__role__in=role_ids)

        approve_request_ids = self.queryset.filter(qs_filters).values_list('pk', flat=True)

        self.log.info(f'Set decision {decision} for {len(approve_request_ids)} approve requests')
        if chunk_size:
            for offset in range(0, len(approve_request_ids), chunk_size):
                ApproveRequestBulk.delay(
                    step='process_approve_requests',
                    requester_pk=requester_pk,
                    approve_request_ids=approve_request_ids[offset:offset + chunk_size],
                    decision=decision,
                    comment=comment,
                )
            return

        return {
            'step': 'process_approve_requests',
            'requester_pk': requester_pk,
            'approve_request_ids': approve_request_ids,
            'decision': decision,
            'comment': comment,
        }

    def process_approve_requests(
            self,
            requester_pk: int,
            approve_request_ids: List[int],
            decision: str,
            comment: Optional[str] = None,
    ):

        requester = Requester(
            impersonated=User.objects.get(pk=requester_pk),
            impersonator=None,
            allowed_systems=None,
        )
        for approve_request in self.queryset.filter(pk__in=approve_request_ids):
            if decision == APPROVEREQUEST_DECISION.APPROVE:
                method = approve_request.set_approved
            elif decision == APPROVEREQUEST_DECISION.DECLINE:
                method = approve_request.set_declined
            elif decision == APPROVEREQUEST_DECISION.IGNORE:
                method = approve_request.set_ignored

            try:
                method(requester=requester, comment=comment)
            except exceptions.RoleStateSwitchError:
                # роль нельзя было подтверждать, т.к. она уже в другом состоянии.
                # просто не обращаем внимания
                self.log.info(f'Approve request {approve_request.id} couldn\'t set state {decision}, skip')


ApproveRequestBulk = app.register_task(ApproveRequestBulk())
