from django.utils import timezone
from django.db import transaction
from rest_framework.views import APIView
from apps.system.models import User
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.wf.filters import TicketFilterSet
from rest_framework.response import Response
from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, \
    RetrieveModelMixin, UpdateModelMixin
from apps.wf.serializers import CustomFieldCreateUpdateSerializer, CustomFieldSerializer, StateSerializer, \
    StateDetailSerializer, TicketAddNodeEndSerializer, TicketAddNodeSerializer, TicketCloseSerializer, \
    TicketCreateSerializer, TicketDeliverSerializer, TicketDestorySerializer, TicketFlowSerializer, \
    TicketHandleSerializer, TicketRetreatSerializer, \
    TicketSerializer, TransitionSerializer, WorkflowSerializer, \
    TicketListSerializer, TicketDetailSerializer, WorkflowCloneSerializer, TicketStateUpateSerializer, TicketFlowSimpleSerializer
from rest_framework.viewsets import GenericViewSet, ModelViewSet
from rest_framework.decorators import action
from apps.wf.models import CustomField, Ticket, Workflow, State, Transition, TicketFlow
from apps.utils.mixins import CreateUpdateCustomMixin, CreateUpdateModelAMixin
from apps.wf.services import WfService
from rest_framework.exceptions import ParseError, NotFound
from rest_framework import status
from django.db.models import Count, Case, When, IntegerField, F
from rest_framework.serializers import Serializer
from apps.utils.snowflake import idWorker
import importlib
from apps.wf.tasks import run_task

# Create your views here.


class WorkflowKeyInitView(APIView):
    perms_map = {'get': '*'}

    def get(self, request, key=None):
        """
        新建工单初始化-通过key

        新建工单初始化
        """
        ret = {}
        try:
            wf = Workflow.objects.get(key=key)
        except Exception:
            raise NotFound('获取工作流失败')
        start_state = WfService.get_workflow_start_state(wf)
        transitions = WfService.get_state_transitions(start_state)
        ret['workflow'] = wf.id
        ret['transitions'] = TransitionSerializer(instance=transitions, many=True).data
        field_list = CustomFieldSerializer(instance=WfService.get_workflow_custom_fields(wf), many=True).data
        for i in field_list:
            if i['field_key'] in start_state.state_fields:
                i['field_attribute'] = start_state.state_fields[i['field_key']]
            else:
                i['field_attribute'] = State.STATE_FIELD_READONLY
        ret['field_list'] = field_list
        return Response(ret)


class WorkflowViewSet(CustomModelViewSet):
    queryset = Workflow.objects.all()
    serializer_class = WorkflowSerializer
    search_fields = ['name', 'description', 'key']
    filterset_fields = ['key', 'cate']
    ordering_fields = ['create_time', 'key', 'cate']

    @action(methods=['get'], detail=False, perms_map={'get': '*'})
    def cates(self, request, pk=None):
        """
        工作流分类
        """
        return Response(Workflow.objects.filter(cate__isnull=False).values_list('cate', flat=True).distinct())

    @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, is_deleted=False).order_by('sort'), many=True)
        return Response(serializer.data)

    @action(methods=['get'], detail=True, perms_map={'get': '*'})
    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'] = wf.id
        ret['transitions'] = TransitionSerializer(instance=transitions, many=True).data
        field_list = CustomFieldSerializer(instance=WfService.get_workflow_custom_fields(wf), many=True).data
        for i in field_list:
            if i['field_key'] in start_state.state_fields:
                i['field_attribute'] = start_state.state_fields[i['field_key']]
            else:
                i['field_attribute'] = State.STATE_FIELD_READONLY
        ret['field_list'] = field_list
        return Response(ret)

    @action(methods=['post'], detail=True, perms_map={'post': 'workflow.clone'},
            pagination_class=None, serializer_class=WorkflowCloneSerializer)
    @transaction.atomic
    def clone(self, request, pk=None):
        """工作流复制

        工作流复制
        """
        wf = self.get_object()
        sr = WorkflowCloneSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        vdata = sr.validated_data
        wf_new = Workflow()
        for f in Workflow._meta.fields:
            if f.name not in ['id', 'create_by', 'update_by', 'key', 'name', 'create_time', 'update_time']:
                setattr(wf_new, f.name, getattr(wf, f.name, None))
        wf_new.id = idWorker.get_id()
        wf_new.key = vdata['key']
        wf_new.name = vdata['name']
        wf_new.create_by = request.user
        wf_new.save()
        stas_dict = {}
        for s in State.objects.filter(workflow=wf):
            sta = State()
            sta.id = idWorker.get_id()
            sta.workflow = wf_new
            for f in State._meta.fields:
                if f.name not in ['workflow', 'create_time', 'update_time', 'id']:
                    setattr(sta, f.name, getattr(s, f.name))
            sta.save()
            stas_dict[s.id] = sta  # 保存一下, 后续备用
        for c in CustomField.objects.filter(workflow=wf):
            cf = CustomField()
            cf.id = idWorker.get_id()
            cf.workflow = wf_new
            for f in CustomField._meta.fields:
                if f.name not in ['workflow', 'create_time', 'update_time', 'id']:
                    setattr(sta, f.name, getattr(s, f.name))
            cf.save()
        for t in Transition.objects.filter(workflow=wf):
            tr = Transition()
            tr.id = idWorker.get_id()
            tr.workflow = wf_new
            for f in Transition._meta.fields:
                if f.name not in ['workflow', 'create_time', 'update_time', 'id']:
                    setattr(tr, f.name, getattr(t, f.name))
            tr.source_state = stas_dict[t.source_state.id]
            tr.destination_state = stas_dict[t.destination_state.id]
            ce = tr.condition_expression
            for i in ce:
                i['target_state'] = stas_dict[i['target_state']].id
            tr.condition_expression = ce
            tr.save()
        return Response()
    
    @action(methods=['get'], detail=False, perms_map={'get': '*'})
    def ticket_count(self, request, pk=None):
        """工作流下的工单数量统计

        工作流下的工单数量统计
        """
        queryset = self.filter_queryset(self.get_queryset())
        result = Ticket.objects.filter(workflow__in=queryset).annotate(
            workflow_name=F('workflow__name'), workflow_cate=F('workflow__cate')).values(
            'workflow', 'workflow_name', 'workflow_cate').annotate(
            count_done=Count(Case(When(state__type=2, then=1), output_field=IntegerField())),
            count_processing=Count(Case(When(state__type=1, then=1), output_field=IntegerField())),
        )
        return Response(list(result))


class StateViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, CustomGenericViewSet):
    perms_map = {'get': '*', 'post': 'workflow.update',
                 'put': 'workflow.update', 'delete': 'workflow.update'}
    queryset = State.objects.all()
    serializer_class = StateSerializer
    retrieve_serializer_class = StateDetailSerializer
    search_fields = ['name']
    filterset_fields = ['workflow']
    ordering = ['sort']


class TransitionViewSet(CreateModelMixin, UpdateModelMixin, RetrieveModelMixin, DestroyModelMixin, CustomGenericViewSet):
    perms_map = {'get': '*', 'post': 'workflow.update',
                 'put': 'workflow.update', 'delete': 'workflow.update'}
    queryset = Transition.objects.all()
    serializer_class = TransitionSerializer
    select_related_fields = ['source_state', 'destination_state']
    search_fields = ['name']
    filterset_fields = ['workflow']
    ordering = ['id']


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

    def get_serializer_class(self):
        if self.action in ['create', 'update']:
            return CustomFieldCreateUpdateSerializer
        return super().get_serializer_class()


class TicketViewSet(CreateUpdateCustomMixin, CreateModelMixin, ListModelMixin, RetrieveModelMixin, CustomGenericViewSet):
    perms_map = {'get': '*', 'post': '*'}
    queryset = Ticket.objects.all()
    serializer_class = TicketSerializer
    search_fields = ['title']
    select_related_fields = ['workflow', 'state']
    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
        elif self.action == 'deliver':
            return TicketDeliverSerializer
        return super().get_serializer_class()

    def filter_queryset(self, queryset):
        if not self.detail and not self.request.query_params.get('category', None):
            raise ParseError('请指定查询分类')
        return super().filter_queryset(queryset)

    @transaction.atomic
    def create(self, request, *args, **kwargs):
        """
        新建工单
        """
        rdata = request.data
        serializer = self.get_serializer(data=rdata)
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data  # 校验之后的数据
        transition = vdata.get("transition", None)
        workflow = vdata['workflow']
        ticket_data = vdata['ticket_data']
        ticket = WfService.handle_ticket(ticket=None, transition=transition, 
                                         workflow=workflow, new_ticket_data=ticket_data,
                                         oinfo=rdata, handler=request.user)
        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': '*'})
    @transaction.atomic
    def handle(self, request, pk=None):
        """
        处理工单
        """
        ticket = self.get_object()
        serializer = TicketHandleSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data
        new_ticket_data = ticket.ticket_data
        new_ticket_data.update(**vdata['ticket_data'])
        ticket = WfService.handle_ticket(ticket=ticket, transition=vdata['transition'],
                                            new_ticket_data=new_ticket_data, handler=request.user,
                                            suggestion=vdata.get('suggestion', ''))
        return Response(TicketSerializer(instance=ticket).data)

    @action(methods=['post'], detail=True, perms_map={'post': '*'})
    @transaction.atomic
    def deliver(self, request, pk=None):
        """
        转交工单
        """
        ticket = self.get_object()
        rdata = request.data
        serializer = self.get_serializer(data=rdata)
        serializer.is_valid(raise_exception=True)
        vdata = serializer.validated_data  # 校验之后的数据
        if not ticket.state.enable_deliver:
            raise ParseError('不允许转交')
        ticket.participant_type = State.PARTICIPANT_TYPE_PERSONAL
        ticket.participant = vdata['target_user']
        ticket.save()
        tf = TicketFlow.objects.create(ticket=ticket, state=ticket.state,
                                    ticket_data=WfService.get_ticket_all_field_value(ticket),
                                    suggestion=vdata.get('suggestion', ''), participant_type=State.PARTICIPANT_TYPE_PERSONAL,
                                    intervene_type=Transition.TRANSITION_INTERVENE_TYPE_DELIVER,
                                    participant=request.user, transition=None)
        WfService.send_ticket_notice(ticketflow=tf)
        return Response()

    @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)
        data = StateSerializer(instance=steps, many=True).data
        for i in data:
            if i['id'] == ticket.state.id:
                i['checked'] = True
        return Response(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 = TicketFlowSimpleSerializer(instance=flowlogs.select_related('participant', 'state', 'transition', 'participant__employee'), 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.select_related('source_state', 'destination_state'), many=True).data)

    @action(methods=['get'], detail=True, perms_map={'get': '*'})
    def available_actions(self, request, pk=None):
        """
        获取当前用户对工单可执行的操作及接单状态。

        前端应使用该接口决定是否展示处理按钮，避免自行解释 participant
        后与后端实际处理权限不一致。原 transitions 接口保留用于兼容。
        """
        ticket = self.get_object()
        permission_result = WfService.ticket_handle_permission_check(
            ticket, request.user
        )
        transitions = []
        if permission_result.get('permission', False):
            transition_queryset = WfService.get_ticket_transitions(
                ticket
            ).select_related('source_state', 'destination_state')
            transitions = TransitionSerializer(
                instance=transition_queryset, many=True
            ).data
        return Response({
            'permission': permission_result.get('permission', False),
            'need_accept': permission_result.get('need_accept', False),
            'message': permission_result.get('msg', ''),
            'in_add_node': ticket.in_add_node,
            'transitions': transitions,
        })

    @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()
            # 接单日志
            # 更新工单流转记录
            tf = 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)
            WfService.send_ticket_notice(ticketflow=tf)
            return Response()
        else:
            raise ParseError('无需接单')

    @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 ParseError('非创建人不可撤回')
        if not ticket.state.enable_retreat:
            raise ParseError('该状态不可撤回')
        WfService.retreat(ticket, request.data.get('suggestion', ''), request.user, request.user)
        return Response()

    @action(methods=['post'], detail=True, perms_map={'post': '*'}, serializer_class=TicketAddNodeSerializer)
    def add_node(self, request, pk=None):
        """
        加签
        """
        data = request.data
        sr = TicketAddNodeSerializer(data=data)
        sr.is_valid(raise_exception=True)
        ticket = self.get_object()
        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', '')  # 加签说明
        tf = 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)
        WfService.send_ticket_notice(ticketflow=tf)
        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()
        if ticket.in_add_node is False:
            raise ParseError('该工单不在加签状态中')
        elif ticket.participant != request.user.id:
            raise ParseError('非当前加签人')
        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', '')  # 加签意见
        tf = 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)
        WfService.send_ticket_notice(ticketflow=tf)
        return Response()

    @action(methods=['post'], detail=True, perms_map={'post': '*'},
            serializer_class=TicketCloseSerializer)
    @transaction.atomic
    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)
            if end_state.on_reach_func:  # 如果有到达方法还需要进行处理
                module, func = end_state.on_reach_func.rsplit(".", 1)
                m = importlib.import_module(module)
                f = getattr(m, func)
                f(ticket=ticket)  # 同步执行
            return Response()
        else:
            return Response('工单不可关闭', status=status.HTTP_400_BAD_REQUEST)

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

    @action(methods=['post'], detail=True, perms_map={'post': '*'},
            serializer_class=Serializer)
    def retry_script(self, request, pk=None):
        """重试脚本

        重试脚本
        """
        ticket = self.get_object()
        if not ticket.script_run_last_result:
            ticket.script_run_last_result = True
            ticket.save()
            run_task.delay(ticket.id)
        return Response()

    @action(methods=['put'], detail=True, perms_map={'put': 'ticket.state_update'},
            serializer_class=TicketStateUpateSerializer)
    def state(self, request, pk=None):
        """强制修改工单状态

        强制修改工单状态
        """
        sr = TicketStateUpateSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        vdata = sr.validated_data
        ticket = self.get_object()
        WfService.update_ticket_state(ticket, vdata['state'], vdata.get('suggestion', ''), request.user, vdata['need_log'])
        return Response()


class TicketFlowViewSet(ListModelMixin, RetrieveModelMixin, CustomGenericViewSet):
    """
    工单日志
    """
    perms_map = {'get': '*'}
    queryset = TicketFlow.objects.all()
    list_serializer_class = TicketFlowSimpleSerializer
    serializer_class = TicketFlowSerializer
    search_fields = ['suggestion']
    select_related_fields = ['participant', 'state', 'transition']
    filterset_fields = ['ticket']
    ordering = ['-create_time']
