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

from apps.inm.models import WareHouse, MaterialBatch, MIO, MIOItem, MIOItemw
from apps.inm.serializers import (
    MaterialBatchSerializer, WareHourseSerializer, MIOListSerializer, MIOItemSerializer, MioItemAnaSerializer,
    MIODoSerializer, MIOSaleSerializer, MIOPurSerializer, MIOOtherSerializer, MIOItemCreateSerializer, 
    MaterialBatchDetailSerializer, MIODetailSerializer, MIOItemTestSerializer, MIOItemPurInTestSerializer,
    MIOItemwSerializer)
from apps.inm.serializers2 import MIOItemwCreateUpdateSerializer
from apps.utils.viewsets import CustomGenericViewSet, CustomModelViewSet
from apps.inm.services import InmService, daoru_mb
from apps.utils.mixins import (BulkCreateModelMixin, BulkDestroyModelMixin, BulkUpdateModelMixin,
                               CustomListModelMixin)
from apps.utils.permission import has_perm
from .filters import MaterialBatchFilter, MioFilter
from apps.qm.serializers import FtestProcessSerializer
from apps.mtm.models import Material


# Create your views here.
class WarehouseVIewSet(CustomModelViewSet):
    """
    list: 仓库信息

    仓库信息
    """
    queryset = WareHouse.objects.all()
    serializer_class = WareHourseSerializer
    search_fields = ['name', 'number', 'place']
    ordering = ['create_time']

    def perform_destroy(self, instance):
        if MaterialBatch.objects.filter(warehouse=instance).exclude(count=0).exists():
            raise ParseError('该仓库存在物料')
        instance.delete()


class MaterialBatchViewSet(ListModelMixin, CustomGenericViewSet):
    """
    list: 物料批次

    物料批次
    """
    perms_map = {'get': '*'}
    queryset = MaterialBatch.objects.filter(count__gt=0)
    serializer_class = MaterialBatchSerializer
    retrieve_serializer_class = MaterialBatchDetailSerializer
    select_related_fields = ['warehouse', 'material', 'supplier']
    filterset_class = MaterialBatchFilter
    search_fields = ['material__name', 'material__number',
                     'material__model', 'material__specification', 'batch']
    ordering = ['-update_time']

    @action(methods=['post'], detail=False, serializer_class=serializers.Serializer, perms_map={'post': 'materialbatch.daoru'})
    @transaction.atomic
    def daoru(self, request, *args, **kwargs):
        """导入物料批次

        导入物料
        """
        daoru_mb(settings.BASE_DIR + request.data.get('path', ''))
        return Response()


class MioDoViewSet(BulkCreateModelMixin, BulkUpdateModelMixin, CustomGenericViewSet):
    perms_map = {'post': '*', 'put': 'mio.do'}
    queryset = MIO.objects.filter(
        type__in=[MIO.MIO_TYPE_DO_IN, MIO.MIO_TYPE_DO_OUT])
    serializer_class = MIODoSerializer

    def create(self, request, *args, **kwargs):
        """
        生产领料/入库

        生产领料/入库
        """
        return super().create(request, *args, **kwargs)


class MioSaleViewSet(BulkCreateModelMixin, BulkUpdateModelMixin, CustomGenericViewSet):
    perms_map = {'post': '*', 'put': 'mio.sale'}
    queryset = MIO.objects.filter(
        type__in=[MIO.MIO_TYPE_DO_IN, MIO.MIO_TYPE_DO_OUT])
    serializer_class = MIOSaleSerializer

    def create(self, request, *args, **kwargs):
        """
        销售出库

        销售出库
        """
        return super().create(request, *args, **kwargs)


class MioPurViewSet(BulkCreateModelMixin, BulkUpdateModelMixin, CustomGenericViewSet):
    perms_map = {'post': '*', 'put': 'mio.pur'}
    queryset = MIO.objects.filter(
        type__in=[MIO.MIO_TYPE_PUR_IN])
    serializer_class = MIOPurSerializer

    def create(self, request, *args, **kwargs):
        """
        采购入库

        采购入库
        """
        return super().create(request, *args, **kwargs)


class MioOtherViewSet(BulkCreateModelMixin, BulkUpdateModelMixin, CustomGenericViewSet):
    perms_map = {'post': '*', 'put': 'mio.other'}
    queryset = MIO.objects.filter(
        type__in=[MIO.MIO_TYPE_OTHER_OUT, MIO.MIO_TYPE_OTHER_IN])
    serializer_class = MIOOtherSerializer

    def create(self, request, *args, **kwargs):
        """
        其他出入库

        其他出入库
        """
        return super().create(request, *args, **kwargs)


class MIOViewSet(CustomModelViewSet):
    """
    list: 出入库记录

    出入库记录
    """
    queryset = MIO.objects.all()
    select_related_fields = ['create_by', 'belong_dept', 'do_user',
                             'submit_user', 'supplier', 'order', 'customer', 'pu_order', 'mgroup']
    prefetch_related_fields = ['materials']
    serializer_class = MIOListSerializer
    retrieve_serializer_class = MIODetailSerializer
    filterset_class = MioFilter
    search_fields = ['id', 'number', 'item_mio__batch', 'item_mio__material__name', 'item_mio__material__specification', 'item_mio__material__model']
    data_filter = True

    def get_serializer_class(self):
        if self.action in ['create', 'update', 'partial_update']:
            type = self.request.data.get('type')
            user = self.request.user
            if type in [MIO.MIO_TYPE_DO_IN, MIO.MIO_TYPE_DO_OUT]:
                if not has_perm(user, ['mio.do']):
                    raise PermissionDenied
                return MIODoSerializer
            elif type in [MIO.MIO_TYPE_OTHER_IN, MIO.MIO_TYPE_OTHER_OUT]:
                if not has_perm(user, ['mio.other']):
                    raise PermissionDenied
                return MIOOtherSerializer
            elif type == MIO.MIO_TYPE_SALE_OUT:
                if not has_perm(user, ['mio.sale']):
                    raise PermissionDenied
                return MIOSaleSerializer
            elif type == MIO.MIO_TYPE_PUR_IN:
                if not has_perm(user, ['mio.pur']):
                    raise PermissionDenied
                return MIOPurSerializer
        return super().get_serializer_class()

    def perform_destroy(self, instance):
        if instance.state != MIO.MIO_CREATE:
            raise ParseError('非创建中不可删除')
        return super().perform_destroy(instance)

    @action(methods=['post'], detail=True, perms_map={'post': 'mio.submit'}, serializer_class=serializers.Serializer)
    def submit(self, request, *args, **kwargs):
        """提交

        提交
        """
        ins = self.get_object()
        if ins.inout_date is None:
            raise ParseError('出入库日期未填写')
        if ins.state != MIO.MIO_CREATE:
            raise ParseError('记录状态异常')
        with transaction.atomic():
            ins.submit_time = timezone.now()
            ins.state = MIO.MIO_SUBMITED
            ins.submit_user = request.user
            ins.update_by = request.user
            ins.save()
            InmService.update_inm(ins)
        InmService.update_material_count(ins)
        return Response(MIOListSerializer(instance=ins).data)

    @action(methods=['post'], detail=True, perms_map={'post': 'mio.submit'}, serializer_class=serializers.Serializer)
    def revert(self, request, *args, **kwargs):
        """撤回

        撤回
        """
        ins = self.get_object()
        user = self.request.user
        if ins.state != MIO.MIO_SUBMITED:
            raise ParseError('记录状态异常')
        if ins.submit_user != user:
            raise ParseError('非提交人不可撤回')
        with transaction.atomic():
            ins.submit_time = None
            ins.state = MIO.MIO_CREATE
            ins.update_by = user
            ins.save()
            InmService.update_inm(ins, is_reverse=True)
        InmService.update_material_count(ins)
        return Response()


class MIOItemViewSet(CustomListModelMixin, BulkCreateModelMixin, BulkDestroyModelMixin, CustomGenericViewSet):
    """
    list: 出入库明细

    出入库明细
    """
    perms_map = {'get': '*', 'post': '*', 'delete': '*'}
    queryset = MIOItem.objects.all()
    serializer_class = MIOItemSerializer
    create_serializer_class = MIOItemCreateSerializer
    select_related_fields = ['warehouse', 'mio', 'material', 'test_user']
    filterset_fields = {
        "warehouse": ["exact"],
        "mio": ["exact"],
        "mio__state": ["exact"],
        "mio__type": ["exact", "in"],
        "mio__inout_date": ["gte", "lte", "exact"],
        "material": ["exact"],
        "test_date": ["isnull", "exact"]
    }
    ordering = ['create_time']

    def add_info_for_list(self, data):

        return data

    def perform_destroy(self, instance):
        if instance.mio.state != MIO.MIO_CREATE:
            raise ParseError('出入库记录非创建中不可删除')
        if has_perm(self.request.user, ['mio.update']) is False and instance.mio.create_by != self.request.user:
            raise PermissionDenied('无权限删除')
        return super().perform_destroy(instance)

    @action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=MIOItemTestSerializer)
    @transaction.atomic
    def test(self, request, *args, **kwargs):
        """半成品检验

        半成品检验
        """
        ins: MIOItem = self.get_object()
        if ins.material.tracking != Material.MA_TRACKING_BATCH:
            raise ParseError('该物料非批次管理')
        mio = ins.mio
        if ins.test_date:
            raise ParseError('该明细已检验')
        if mio.state != MIO.MIO_SUBMITED:
            raise ParseError('该出入库记录还未提交')
        sr = MIOItemTestSerializer(instance=ins, data=request.data)
        sr.is_valid(raise_exception=True)
        sr.save()
        # 开始变动库存
        InmService.update_mb_item(ins, -1, 'count_notok')
        return Response()
    
    @action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=serializers.Serializer)
    @transaction.atomic
    def test_revert(self, request, *args, **kwargs):
        """
        检验撤回
        """
        ins: MIOItem = self.get_object()
        if ins.test_date is None:
            raise ParseError('该明细还未检验')
        if ins.count_notok > 0:
            InmService.update_mb_item(ins, 1, 'count_notok')
        elif ins.count_notok == 0:
            pass
        ins.test_date = None
        ins.save()
        return Response()

    @action(methods=['post'], detail=True, perms_map={'post': 'mioitem.test'}, serializer_class=MIOItemPurInTestSerializer)
    @transaction.atomic
    def test_pur_in(self, request, *args, **kwargs):
        """入厂检验

        入厂检验
        """
        ins: MIOItem = self.get_object()
        if ins.material.tracking != Material.MA_TRACKING_BATCH:
            raise ParseError('该物料非批次管理')
        if ins.test_date:
            raise ParseError('该明细已检验')
        sr = MIOItemPurInTestSerializer(instance=ins, data=request.data)
        sr.is_valid(raise_exception=True)
        sr.save()
        return Response()

    @action(methods=['post'], detail=False, perms_map={'post': '*'}, serializer_class=MioItemAnaSerializer)
    def sale_out_ana(self, request, *args, **kwargs):
        """交付统计数据

        交付统计数据
        """
        sr = MIOItemTestSerializer(data=request.data)
        sr.is_valid(raise_exception=True)
        vdata = sr.validated_data
        mioitems = MIOItem.objects.filter(
            mio__type='sale_out', mio__state=MIO.MIO_SUBMITED)
        if vdata.get('material_cate'):
            mioitems = mioitems.filter(
                material__cate=vdata['material_cate'])
        if vdata.get('start_date', ''):
            mioitems = mioitems.filter(
                mio__inout_date__gte=vdata['start_date'])
        if vdata.get('end_date', ''):
            mioitems = mioitems.filter(mio__inout_date__lte=vdata['end_date'])
        res = mioitems.aggregate(count=Sum('count'))
        for i in res:
            if res[i] is None:
                res[i] = 0
        return Response(res)


class MIOItemwViewSet(CustomModelViewSet):
    perms_map = {'get': '*', 'post': 'mio.update', 'put': 'mio.update', 'delete': 'mio.update'}
    queryset = MIOItemw.objects.all()
    serializer_class = MIOItemwCreateUpdateSerializer
    filterset_fields = ['mioitem']
    ordering = ["number"]
    ordering_fields = ["number", "create_time"]

    def filter_queryset(self, queryset):
        if not self.detail and not self.request.query_params.get('mioitem', None):
            raise ParseError('请指定所属出入库记录明细')
        return super().filter_queryset(queryset)

    def cal_mioitem_count(self, mioitem:MIOItem):
        count = MIOItemw.objects.filter(mioitem=mioitem).count()
        mioitem.count = count
        mioitem.count_notok = MIOItemw.objects.filter(mioitem=mioitem, ftest__is_ok=False).count()
        mioitem.save()

    @transaction.atomic
    def perform_create(self, serializer):
        ins: MIOItemw = serializer.save()
        mioitem: MIOItem = ins.mioitem
        self.cal_mioitem_count(mioitem)

    @transaction.atomic
    def perform_update(self, serializer):
        mioitemw = serializer.save()
        self.cal_mioitem_count(mioitemw.mioitem)

    @transaction.atomic
    def perform_destroy(self, instance: MIOItemw):
        mioitem = instance.mioitem
        ftest = instance.ftest
        instance.delete()
        if ftest:
            ftest.delete()
        self.cal_mioitem_count(mioitem)
    