from django.db.models import query
from rest_framework.utils import serializer_helpers
from rest_framework.views import APIView
from apps.system.models import User
from apps.wf.filters import TicketFilterSet
from django.core.exceptions import AppRegistryNotReady
from rest_framework.response import Response
from rest_framework import serializers
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
from apps.wf.serializers import CustomFieldSerializer, StateSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, TicketCreateSerializer, TicketDestorySerializer, TicketFlowSerializer, TicketFlowSimpleSerializer, TicketHandleSerializer, TicketRetreatSerializer, TicketSerializer, TransitionSerializer, WorkflowSerializer, TicketListSerializer, TicketDetailSerializer
from django.shortcuts import get_object_or_404, render
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework.decorators import action, api_view
from apps.wf.models import CustomField, Ticket, Workflow, State, Transition, TicketFlow
from apps.system.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin, OptimizationMixin
from apps.wf.services import WfService
from rest_framework.exceptions import APIException, PermissionDenied
from rest_framework import status
from django.db.models import Count
from .scripts import GetParticipants


# Create your views here.
class FromCodeListView(APIView):
    def get(self, request, format=None):
        """
        获取处理人代码列表
        """
        return Response(GetParticipants.all_funcs)

class WorkflowViewSet(CreateUpdateModelAMixin, ModelViewSet):
    perms_map = {'get': '*', 'post': 'workflow_create',
                 'put': 'workflow_update', 'delete': 'workflow_delete'}
    queryset = Workflow.objects.all()
    serializer_class = WorkflowSerializer
    search_fields = ['name', 'description']
    filterset_fields = []
    ordering_fields = ['create_time']
    ordering = ['-create_time']    

    @action(methods=['get'], detail=True, perms_map={'get':'workflow_update'}, pagination_class=None, serializer_class=StateSerializer)
    def states(self, request, pk=None):
        """
        工作流下的状态节点
        """
        wf = self.get_object()
        serializer = self.serializer_class(instance=WfService.get_worlflow_states(wf), many=True)
        return Response(serializer.data)
    
    @action(methods=['get'], detail=True, perms_map={'get':'workflow_update'}, pagination_class=None, serializer_class=TransitionSerializer)
    def transitions(self, request, pk=None):
        """
        工作流下的流转规则
        """
        wf = self.get_object()
        serializer = self.serializer_class(instance=WfService.get_workflow_transitions(wf), many=True)
        return Response(serializer.data)
    
    @action(methods=['get'], detail=True, perms_map={'get':'workflow_update'}, pagination_class=None, serializer_class=CustomFieldSerializer)
    def customfields(self, request, pk=None):
        """
        工作流下的自定义字段
        """
        wf = self.get_object()
        serializer = self.serializer_class(instance=CustomField.objects.filter(workflow=wf), many=True)
        return Response(serializer.data)
    
    @action(methods=['get'], detail=True, perms_map={'get':'workflow_init'})
    def init(self, request, pk=None):
        """
        新建工单初始化
        """
        ret={}
        wf = self.get_object()
        start_state = WfService.get_workflow_start_state(wf)
        transitions = WfService.get_state_transitions(start_state)
        ret['workflow'] = pk
        ret['transitions'] = TransitionSerializer(instance=transitions, many=True).data
        field_list = CustomFieldSerializer(instance=WfService.get_workflow_custom_fields(wf), many=True).data
        ret['field_list'] = field_list
        return Response(ret)

class StateViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
    perms_map = {'*':'*'}
    queryset = State.objects.all()
    serializer_class = StateSerializer
    search_fields = ['name']
    filterset_fields = ['workflow']
    ordering = ['sort']

class TransitionViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
    perms_map = {'*':'*'}
    queryset = Transition.objects.all()
    serializer_class = TransitionSerializer
    search_fields = ['name']
    filterset_fields = ['workflow']
    ordering = ['id']

class CustomFieldViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, GenericViewSet):
    perms_map = {'*':'*'}
    queryset = CustomField.objects.all()
    serializer_class = CustomFieldSerializer
    search_fields = ['field_name']
    filterset_fields = ['workflow', 'field_type']
    ordering = ['sort']

class TicketViewSet(OptimizationMixin, CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet):
    perms_map = {'*':'*'}
    queryset = Ticket.objects.all()
    serializer_class = TicketSerializer
    search_fields = ['title']
    filterset_class = TicketFilterSet
    ordering = ['-create_time']

    def get_serializer_class(self):
        if self.action == 'create':
            return TicketCreateSerializer
        elif self.action == 'handle':
            return TicketHandleSerializer
        elif self.action == 'retreat':
            return TicketRetreatSerializer
        elif self.action == 'list':
            return TicketListSerializer
        elif self.action == 'retrieve':
            return TicketDetailSerializer
        return super().get_serializer_class()
    
    def get_queryset(self):
        if self.action=='list' and (not self.request.query_params.get('category', None)):
            raise APIException('请指定查询分类')
        return super().get_queryset()

    def create(self, request, *args, **kwargs):
        """
        新建工单
        """
        rdata = request.data
        serializer = self.get_serializer(data=rdata)
        serializer.is_valid(raise_exception=True)
        start_state = WfService.get_workflow_start_state(rdata['workflow'])
        transition = Transition.objects.get(pk=rdata['transition'])
        ticket_data = rdata['ticket_data']
        if transition.field_require_check:
            for key, value in start_state.state_fields.items(): #校验必填项
                if value == State.STATE_FIELD_REQUIRED:
                    if key not in ticket_data or not ticket_data[key]:
                        raise APIException('字段{}必填'.format(key))
        ticket = serializer.save(state=start_state, create_by=request.user, act_state=Ticket.TICKET_ACT_STATE_DRAFT, belong_dept=request.user.dept) # 先创建出来

        next_state = WfService.get_next_state_by_transition_and_ticket_info(ticket=ticket, transition=transition)
        participant_info = WfService.get_ticket_state_participant_info(state=next_state, ticket=ticket, ticket_data=ticket.ticket_data)
        destination_participant_type = participant_info.get('destination_participant_type', 0)
        destination_participant = participant_info.get('destination_participant', 0)
        multi_all_person = participant_info.get('multi_all_person', {})  # 多人需要全部处理情况
        sn = WfService.get_ticket_sn(ticket.workflow) # 流水号
        if next_state.type == State.STATE_TYPE_END:
            act_state = Ticket.TICKET_ACT_STATE_FINISH
        elif next_state.type == State.STATE_TYPE_START:
            act_state = Ticket.TICKET_ACT_STATE_DRAFT
        else:
            act_state = Ticket.TICKET_ACT_STATE_ONGOING
        title = rdata.get('title', '')
        title_template = ticket.workflow.title_template
        if title_template:
            all_ticket_data = {**rdata, **rdata['ticket_data']}
            title = title_template.format(**all_ticket_data)
        # 更新一下信息
        ticket.sn=sn
        ticket.title=title
        ticket.state=next_state
        ticket.participant=destination_participant
        ticket.participant_type=destination_participant_type
        ticket.act_state=act_state
        ticket.multi_all_person=multi_all_person
        ticket.save()
        # 新增流转记录
        TicketFlow.objects.create(ticket=ticket, state=start_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
                        suggestion=rdata.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
                        participant=ticket.create_by, transition=transition)
        # 开始状态需要抄送
        if start_state.participant_cc:
            TicketFlow.objects.create(ticket=ticket, state=ticket.start_state, 
                        participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
                        participant=None, participant_cc=start_state.participant_cc)
        # 目标状态需要抄送
        if next_state.participant_cc:
            TicketFlow.objects.create(ticket=ticket, state=next_state, 
                        participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
                        participant=None, participant_cc=next_state.participant_cc)
        return Response(TicketSerializer(instance=ticket).data)

    @action(methods=['get'], detail=False, perms_map={'get':'*'})
    def duty_agg(self, request, pk=None):
        """
        工单待办聚合
        """
        ret = {}
        queryset = Ticket.objects.filter(participant__contains=request.user.id, is_deleted=False)\
            .exclude(act_state__in=[Ticket.TICKET_ACT_STATE_FINISH, Ticket.TICKET_ACT_STATE_CLOSED])
        ret['total_count'] = queryset.count()
        ret['details'] = list(queryset.values('workflow', 'workflow__name').annotate(count = Count('workflow')))
        return Response(ret)

    @action(methods=['post'], detail=True, perms_map={'post':'*'})
    def handle(self, request, pk=None):
        """
        处理工单
        """
        try:
            ticket = Ticket.objects.get(pk=pk)
        except:
            raise APIException('工单不存在')
        data = request.data
        result = WfService.ticket_handle_permission_check(ticket, request.user)
        source_state = ticket.state
        source_ticket_data = ticket.ticket_data
        if result.get('permission') is False:
            raise PermissionDenied(result.get('msg'))
        # 校验表单必填项目
        transition = Transition.objects.get(pk=data['transition'])
        ticket_data = data['ticket_data']
        if transition.field_require_check:
            for key, value in ticket.state.state_fields.items(): 
                if value == State.STATE_FIELD_REQUIRED:
                    if key not in ticket_data or not ticket_data[key]:
                        raise APIException('字段{}必填'.format(key))
        destination_state = WfService.get_next_state_by_transition_and_ticket_info(ticket, transition, ticket_data, request)
        multi_all_person = ticket.multi_all_person
        if multi_all_person:
            multi_all_person[request.user.id] =dict(transition=transition.id)
            # 判断所有人处理结果是否一致
            if WfService.check_dict_has_all_same_value(multi_all_person):
                participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
                destination_participant_type = participant_info.get('destination_participant_type', 0)
                destination_participant = participant_info.get('destination_participant', 0)
                multi_all_person = {}
            else:
                # 处理人没有没有全部处理完成或者处理动作不一致
                destination_participant_type = ticket.participant_type
                destination_state = ticket.state  # 保持原状态
                destination_participant = []
                for key, value in multi_all_person.items():
                    if not value:
                        destination_participant.append(key)
        else:
            # 当前处理人类型非全部处理
            participant_info = WfService.get_ticket_state_participant_info(destination_state, ticket, data['ticket_data'])
            destination_participant_type = participant_info.get('destination_participant_type', 0)
            destination_participant = participant_info.get('destination_participant', 0)
            multi_all_person = participant_info.get('multi_all_person', {})

        # 更新工单信息：基础字段及自定义字段， add_relation字段 需要下个处理人是部门、角色等的情况
        ticket.state = destination_state
        ticket.participant_type = destination_participant_type
        ticket.participant = destination_participant
        ticket.multi_all_person = multi_all_person
        if destination_state.type == State.STATE_TYPE_END:
            ticket.act_state = Ticket.TICKET_ACT_STATE_FINISH
        elif destination_state.type == State.STATE_TYPE_START:
            ticket.act_state = Ticket.TICKET_ACT_STATE_DRAFT
        else:
            ticket.act_state = Ticket.TICKET_ACT_STATE_ONGOING

        if transition.attribute_type == Transition.TRANSITION_ATTRIBUTE_TYPE_REFUSE:
            ticket.act_state = Ticket.TICKET_ACT_STATE_BACK

        # 只更新必填和可选的字段
        for key, value in ticket.state.state_fields.items(): 
                if value in (State.STATE_FIELD_REQUIRED, State.STATE_FIELD_OPTIONAL):
                    source_ticket_data[key] = ticket_data[key]
        ticket.ticket_data = source_ticket_data
        ticket.save()

        # 更新工单流转记录
        TicketFlow.objects.create(ticket=ticket, state=source_state, ticket_data=WfService.get_ticket_all_field_value(ticket),
                        suggestion=data.get('suggestion',''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
                        participant=request.user, transition=transition)
        # 目标状态需要抄送
        if destination_state.participant_cc:
            TicketFlow.objects.create(ticket=ticket, state=destination_state, 
                        participant_type=0, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CC,
                        participant=None, participant_cc=destination_state.participant_cc)
        return Response(TicketSerializer(instance=ticket).data)
        

    @action(methods=['get'], detail=True, perms_map={'get':'*'})
    def flowsteps(self, request, pk=None):
        """
        工单流转step, 用于显示当前状态的step图(线性结构)
        """
        ticket = self.get_object()
        steps = WfService.get_ticket_steps(ticket)
        return Response(StateSerializer(instance=steps, many=True).data)

    @action(methods=['get'], detail=True, perms_map={'get':'*'})
    def flowlogs(self, request, pk=None):
        """
        工单流转记录
        """
        ticket = self.get_object()
        flowlogs = TicketFlow.objects.filter(ticket=ticket).order_by('-create_time')
        serializer = TicketFlowSerializer(instance=flowlogs, many=True)
        return Response(serializer.data)
    
    @action(methods=['get'], detail=True, perms_map={'get':'*'})
    def transitions(self, request, pk=None):
        """
        获取工单可执行的操作
        """
        ticket = self.get_object()
        transitions = WfService.get_ticket_transitions(ticket)
        return Response(TransitionSerializer(instance=transitions, many=True).data)

    @action(methods=['post'], detail=True, perms_map={'post':'*'})
    def accpet(self, request, pk=None):
        """
        接单,当工单当前处理人实际为多个人时(角色、部门、多人都有可能， 注意角色和部门有可能实际只有一人)
        """
        ticket = self.get_object()
        result = WfService.ticket_handle_permission_check(ticket, request.user)
        if result.get('need_accept', False):
            ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
            ticket.participant = request.user.id
            ticket.save()
            # 接单日志
            # 更新工单流转记录
            TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
                        suggestion='', participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_ATTRIBUTE_TYPE_ACCEPT,
                        participant=request.user, transition=None)
            return Response()
        else:
            raise APIException('无需接单')
    
    @action(methods=['post'], detail=True, perms_map={'post':'*'})
    def retreat(self, request, pk=None):
        """
        撤回工单，允许创建人在指定状态撤回工单至初始状态，状态设置中开启允许撤回
        """
        ticket = self.get_object()
        if ticket.create_by != request.user:
            raise APIException('非创建人不可撤回')
        if not ticket.state.enable_retreat:
            raise APIException('该状态不可撤回')
        start_state = WfService.get_workflow_start_state(ticket.workflow)
        ticket.state = start_state
        ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
        ticket.participant = request.user.id
        ticket.act_state = Ticket.TICKET_ACT_STATE_RETREAT
        ticket.save()
        # 更新流转记录
        suggestion = request.data.get('suggestion', '') # 撤回原因
        TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
                        suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_RETREAT,
                        participant=request.user, transition=None)
        return Response()
    
    @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TicketAddNodeSerializer)
    def add_node(self, request, pk=None):
        """
        加签
        """
        ticket = self.get_object()
        data = request.data
        add_user = User.objects.get(pk=data['toadd_user'])
        ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
        ticket.participant = add_user.id
        ticket.in_add_node = True
        ticket.add_node_man = request.user
        ticket.save()
        # 更新流转记录
        suggestion = request.data.get('suggestion', '') # 加签说明
        TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
                        suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_ADD_NODE,
                        participant=request.user, transition=None)
        return Response()

    @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TicketAddNodeEndSerializer)
    def add_node_end(self, request, pk=None):
        """
        加签完成
        """
        ticket = self.get_object()
        ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
        ticket.in_add_node = False
        ticket.participant = ticket.add_node_man.id
        ticket.add_node_man = None
        ticket.save()
        # 更新流转记录
        suggestion = request.data.get('suggestion', '') # 加签意见
        TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
                        suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_ADD_NODE_END,
                        participant=request.user, transition=None)
        return Response()
    

    @action(methods=['post'], detail=True, perms_map={'post':'*'}, serializer_class=TicketCloseSerializer)
    def close(self, request, pk=None):
        """
        关闭工单(创建人在初始状态)
        """
        ticket = self.get_object()
        if ticket.state.type == State.STATE_TYPE_START and ticket.create_by==request.user:
            end_state = WfService.get_workflow_end_state(ticket.workflow)
            ticket.state = end_state
            ticket.participant_type = 0
            ticket.participant = 0
            ticket.act_state = Ticket.TICKET_ACT_STATE_CLOSED
            ticket.save()
            # 更新流转记录
            suggestion = request.data.get('suggestion', '') # 关闭原因
            TicketFlow.objects.create(ticket=ticket, state=ticket.state, ticket_data=WfService.get_ticket_all_field_value(ticket),
                            suggestion=suggestion, participant_type=State.PARTICIPANT_TYPE_PERSONAL, intervene_type=Transition.TRANSITION_INTERVENE_TYPE_CLOSE,
                            participant=request.user, transition=None)
            return Response()
        else:
            return Response('工单不可关闭', status=status.HTTP_400_BAD_REQUEST)

    @action(methods=['post'], detail=False, perms_map={'post':'*'}, serializer_class=TicketDestorySerializer)
    def destory(self, request, pk=None):
        """
        批量物理删除
        """
        Ticket.objects.filter(id__in=request.data.get('ids', [])).delete(soft=False)
        return Response()



class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
    """
    工单日志
    """
    perms_map = {'*':'*'}
    queryset = TicketFlow.objects.all()
    serializer_class = TicketFlowSerializer
    search_fields = ['suggestion']
    filterset_fields = ['ticket']
    ordering = ['-create_time']