from django.db import transaction
from django.db.models import Prefetch
from django.shortcuts import render
from rest_framework.decorators import action
from rest_framework.exceptions import ParseError
from rest_framework.mixins import DestroyModelMixin, ListModelMixin, UpdateModelMixin
from rest_framework.response import Response
from rest_framework.serializers import Serializer
from django.db.models import Sum
from django.utils import timezone

from apps.mtm.models import Material
from apps.pm.models import Mtask
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.utils.mixins import BulkCreateModelMixin

from .filters import StLogFilter, SfLogFilter, WMaterialFilter, MlogFilter, HandoverFilter
from .models import SfLog, SfLogExp, StLog, WMaterial, Mlog, Handover, Mlogb, AttLog, OtherLog
from .serializers import (SflogExpSerializer, SfLogSerializer, StLogSerializer, WMaterialSerializer, MlogRevertSerializer,
                          MlogSerializer, MlogRelatedSerializer, DeptBatchSerializer, HandoverSerializer,
                          GenHandoverSerializer, GenHandoverWmSerializer, MlogAnaSerializer, AttLogSerializer, OtherLogSerializer)
from .services import mlog_submit, update_mtask, handover_submit, mlog_revert
from apps.monitor.services import create_auditlog, delete_auditlog
# Create your views here.


class StLogViewSet(CustomModelViewSet):
    """
    list:异常记录

    异常记录
    """
    queryset = StLog.objects.all()
    serializer_class = StLogSerializer
    select_related_fields = ['mgroup']
    filterset_class = StLogFilter
    ordering = ['-start_time']

    def destroy(self, request, *args, **kwargs):
        obj: StLog = self.get_object()
        if obj.is_shutdown:
            raise ParseError('停机记录不可删除')
        return super().destroy(request, *args, **kwargs)


class SfLogViewSet(UpdateModelMixin, ListModelMixin, DestroyModelMixin, CustomGenericViewSet):
    """
    list:值班记录

    值班记录
    """
    perms_map = {'get': '*', 'put': 'sflog.update', 'delete': 'sflog.delete'}
    queryset = SfLog.objects.all()
    serializer_class = SfLogSerializer
    select_related_fields = ['mgroup', 'shift', 'team', 'leader']
    filterset_class = SfLogFilter
    search_fields = ['note']
    ordering = ['-start_time']

    @action(methods=['get'], detail=True, perms_map={'get': '*'})
    def init_test(self, request, pk=None):
        """
        初始化检测录入

        初始化检测录入
        """
        from apps.qm.models import QuaStat, TestItem
        from apps.qm.serializers import QuaStatSerializer
        obj = self.get_object()
        test_materials = Material.objects.filter(
            id__in=obj.mgroup.test_materials).order_by('sort', '-create_time')
        for material in test_materials:
            testitems = TestItem.objects.filter(
                id__in=material.testitems).order_by('sort', '-create_time')
            for testitem in testitems:
                params = {'material': material,
                          'testitem': testitem, 'sflog': obj}
                QuaStat.objects.get_or_create(
                    **params, defaults={**params, **{'create_by': request.user, 'belong_dept': obj.mgroup.belong_dept}})
        qs = QuaStat.objects.filter(sflog=obj).order_by(
            'material__sort', 'material__create_time', 'testitem__sort', 'testitem__create_time')
        sr = QuaStatSerializer(instance=qs, many=True)
        return Response(sr.data)


class SfLogExpViewSet(ListModelMixin, UpdateModelMixin, CustomGenericViewSet):
    """
    list:异常值班处理

    异常值班处理
    """
    queryset = SfLogExp.objects.all()
    serializer_class = SflogExpSerializer
    select_related_fields = ['sflog', 'sflog__mgroup', 'stlog']
    filterset_fields = ['sflog', 'stlog']


class WMaterialViewSet(ListModelMixin, CustomGenericViewSet):
    """
    list: 车间库存

    车间库存
    """
    perms_map = {'get': '*'}
    queryset = WMaterial.objects.all()
    serializer_class = WMaterialSerializer
    select_related_fields = ['material', 'belong_dept', 'material__process']
    search_fields = ['material__name',
                     'material__number', 'material__specification']
    filterset_class = WMaterialFilter

    @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=DeptBatchSerializer)
    @transaction.atomic
    def batchs(self, request):
        """获取车间的批次号(废弃)

        获取车间的批次号
        """
        sr = DeptBatchSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        vdata = sr.validated_data
        batchs = WMaterial.objects.filter(
            belong_dept__name=vdata['belong_dept_name'], count__gt=0).values_list('batch', flat=True).distinct()
        return Response(list(batchs))


class MlogViewSet(CustomModelViewSet):
    """
    list: 生产日志

    生产日志
    """
    queryset = Mlog.objects.all()
    serializer_class = MlogSerializer
    select_related_fields = ['create_by', 'update_by', 'mtask',
                             'handle_user', 'handle_user_2', 'equipment', 'equipment_2']
    prefetch_related_fields = ['handle_users',
                               'material_outs', 'b_mlog', 'equipments']
    filterset_class = MlogFilter

    @transaction.atomic
    def perform_create(self, serializer):
        ins = serializer.save()
        data = MlogSerializer(ins).data
        create_auditlog('create', ins, data)

    @transaction.atomic
    def perform_destroy(self, instance):
        if instance.submit_time is not None:
            raise ParseError('日志已提交不可变动')
        delete_auditlog(instance, instance.id)
        instance.delete()

    @transaction.atomic
    def perform_update(self, serializer):
        ins = serializer.instance
        val_old = MlogSerializer(instance=ins).data
        serializer.save()
        val_new = MlogSerializer(instance=ins).data
        create_auditlog('update', ins, val_new, val_old)

    @action(methods=['post'], detail=True, perms_map={'post': 'mlog.submit'}, serializer_class=Serializer)
    @transaction.atomic
    def submit(self, request, *args, **kwargs):
        """日志提交(变动车间库存)

        日志提交
        """
        ins: Mlog = self.get_object()
        vdata_old = MlogSerializer(ins).data
        now = timezone.now()
        if ins.submit_time:
            raise ParseError('该日志已提交!')
        if ins.mtask and ins.mtask.state == Mtask.MTASK_STOP:
            raise ParseError('该任务已停止!')
        mlog_submit(ins, self.request.user, now)
        if ins.mtask:
            update_mtask(ins.mtask)
        vdata_new = MlogSerializer(ins).data
        create_auditlog('submit', ins, vdata_new,
                        vdata_old, now, self.request.user)
        return Response(MlogSerializer(instance=ins).data)

    @action(methods=['post'], detail=True, perms_map={'post': 'mlog.submit'}, serializer_class=MlogRevertSerializer)
    @transaction.atomic
    def revert(self, request, *args, **kwargs):
        """撤回日志提交

        撤回日志提交
        """
        ins: Mlog = self.get_object()
        user = request.user
        if ins.submit_time is None:
            raise ParseError('日志未提交不可撤销')
        if user != ins.submit_user:
            raise ParseError('非提交人不可撤销!')
        now = timezone.now()
        mlog_revert(ins, user, now)
        if ins.mtask:
            update_mtask(ins.mtask)
        create_auditlog('revert', ins, {}, {}, now, user,
                        request.data.get('change_reason', ''))
        return Response(MlogSerializer(instance=ins).data)

    @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MlogRelatedSerializer)
    @transaction.atomic
    def related_first(self, request, *args, **kwargs):
        """获取相关任务的第一道工序日志

        获取相关任务的第一道工序日志
        """
        sr = MlogRelatedSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        vdata = sr.validated_data
        mtask = vdata['mtask']
        if mtask.utask:
            mtasks = mtask.related
            mlogs = Mlog.objects.filter(mtask__in=mtasks).order_by(
                'mtask__mgroup__process__sort', 'batch', 'create_time')
        data = MlogSerializer(instance=mlogs, many=True).data
        res_data = []
        for ind, val in enumerate(data):
            if ind == 0:
                res_data.append(val)
            else:
                before = data[ind-1]
                if val['batch'] != before['batch']:
                    res_data.append(val)
        return Response(res_data)

    @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MlogAnaSerializer)
    def ana(self, request):
        """核心统计数据

        核心统计数据
        """
        sr = MlogAnaSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        vdata = sr.validated_data
        mlogs = Mlog.objects.exclude(submit_time=None)
        if vdata.get('belong_dept_name', ''):
            mlogs = mlogs.filter(
                mgroup__belong_dept__name=vdata['belong_dept_name'])
        if vdata.get('material_cate', ''):
            mlogs = mlogs.filter(
                material_out__cate=vdata['material_cate'])
        if vdata.get('start_date', ''):
            mlogs = mlogs.filter(handle_date__gte=vdata['start_date'])
        if vdata.get('end_date', ''):
            mlogs = mlogs.filter(handle_date__lte=vdata['end_date'])
        res = mlogs.aggregate(
            count_real=Sum('count_real'),
            count_ok=Sum('count_ok'),
            count_notok=Sum('count_notok'),
            count_n_zw=Sum('count_n_zw'),
            count_n_tw=Sum('count_n_tw'),
            count_n_qp=Sum('count_n_qp'),
            count_n_wq=Sum('count_n_wq'),
            count_n_dl=Sum('count_n_dl'),
            count_n_pb=Sum('count_n_pb'),
            count_n_dxt=Sum('count_n_dxt'),
            count_n_jsqx=Sum('count_n_jsqx'),
            count_n_qt=Sum('count_n_qt'))
        for i in res:
            if res[i] is None:
                res[i] = 0
        return Response(res)


class HandoverViewSet(CustomModelViewSet):
    """
    list: 交接记录

    交接记录
    """
    queryset = Handover.objects.all()
    serializer_class = HandoverSerializer
    select_related_fields = ['send_user',
                             'send_dept', 'recive_user', 'recive_dept']
    filterset_class = HandoverFilter

    def perform_destroy(self, instance):
        user = self.request.user
        if instance.submit_time is not None:
            raise ParseError('日志已提交不可变动')
        if instance.send_user != user and instance.recive_user != user and instance.create_by != user:
            raise ParseError('非交送人和接收人不可删除该记录')
        return super().perform_destroy(instance)

    @action(methods=['post'], detail=True, perms_map={'post': 'handover.submit'}, serializer_class=Serializer)
    @transaction.atomic
    def submit(self, request, *args, **kwargs):
        """交接记录提交(变动车间库存)

        交接记录提交
        """
        ins: Handover = self.get_object()
        user = self.request.user
        if user != ins.recive_user:
            raise ParseError('非接收人不可提交')
        if ins.submit_time is None:
            handover_submit(ins, user, None)
        return Response()

    @action(methods=['post'], detail=False, perms_map={'post': 'handover.create'}, serializer_class=GenHandoverWmSerializer)
    @transaction.atomic
    def gen_by_wm(self, request):
        """从车间库存生成交接记录

        从车间库存生成交接记录
        """
        sr = GenHandoverWmSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        vdata = sr.validated_data
        user = request.user
        send_date, send_mgroup, send_user, recive_dept, recive_user, wm, count = vdata['send_date'], vdata['send_mgroup'], vdata[
            'send_user'], vdata['recive_dept'], vdata['recive_user'], vdata['wm'], vdata['count']
        if send_mgroup.belong_dept != wm.belong_dept:
            raise ParseError('送料工段错误!')
        handover = Handover.objects.create(
            send_date=send_date,
            send_user=send_user,
            recive_dept=recive_dept,
            recive_user=recive_user,
            send_mgroup=send_mgroup,
            send_dept=wm.belong_dept,
            batch=wm.batch,
            material=wm.material,
            count=count,
            wm=wm,
            create_by=user
        )
        return Response({'handover': handover.id})

    @action(methods=['post'], detail=False, perms_map={'post': 'handover.create'}, serializer_class=GenHandoverSerializer)
    @transaction.atomic
    def gen_by_mlog(self, request):
        """从生产日志生成交接记录

        从生产日志生成交接记录
        """
        sr = GenHandoverSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        vdata = sr.validated_data
        user = request.user
        send_date, send_user, recive_dept, recive_user = vdata['send_date'], vdata[
            'send_user'], vdata['recive_dept'], vdata['recive_user']
        for mlog in vdata['mlogs']:
            Handover.objects.create(
                send_date=send_date,
                send_user=send_user,
                recive_dept=recive_dept,
                recive_user=recive_user,
                send_dept=mlog.mgroup.belong_dept,
                batch=mlog.batch,
                material=mlog.material_out,
                count=mlog.count_real,
                count_eweight=mlog.count_real_eweight,
                mlog=mlog,
                send_mgroup=mlog.mgroup,
                create_by=user
            )
        return Response()


class AttlogViewSet(CustomModelViewSet):
    """
    list: 车间到岗

    车间到岗
    """
    queryset = AttLog.objects.all()
    serializer_class = AttLogSerializer
    select_related_fields = ['user', 'post', 'sflog']
    filterset_fields = ['sflog__mgroup',
                        'sflog__mgroup__belong_dept__name', 'sflog__work_date', 'sflog__mgroup__cate', 'sflog__mgroup__need_enm']
    ordering = ['-sflog__work_date', 'create_time']


class OtherLogViewSet(CustomModelViewSet):
    """
    list: 其他生产记录

    其他生产记录
    """
    queryset = OtherLog.objects.all()
    serializer_class = OtherLogSerializer
    filterset_fields = {
        "product": ["exact"],
        "handle_date": ["exact", "gte", "lte"]
    }
    search_fields = ['product']
    ordering = ['-handle_date', '-create_time']
