from apps.qm.models import FtestWork, FtestworkDefect
from apps.wpm.models import WMaterial
from apps.system.models import User
from rest_framework.exceptions import ParseError
from django.utils import timezone
from apps.wf.models import Ticket
from apps.qm.models import NotOkOption, Defect
from apps.wpm.services_2 import ana_batch_thread
from apps.inm.models import MaterialBatch
from django.db import transaction

def ftestwork_submit_validate(ins: FtestWork):
    wm:WMaterial = ins.wm
    if ins.need_update_wm:
        if wm.state == WMaterial.WM_TEST:
            xcount = wm.count - ins.count
            if xcount < 0:
                raise ParseError("超过待检数量")
        else:
            xcount = wm.count - ins.count_notok
            if xcount < 0:
                raise ParseError("不合格数不可大于批次数量")


@transaction.atomic
def ftestwork_submit(ins:FtestWork, user: User):
    ins = FtestWork.objects.select_for_update().get(pk=ins.pk)
    if ins.submit_time is not None:
        raise ParseError('该检验工作已提交')
    wm = (
        WMaterial.objects.select_for_update().get(pk=ins.wm_id)
        if ins.wm_id else None
    )
    fwd_qs = FtestworkDefect.objects.filter(ftestwork=ins)
    if wm and ins.need_update_wm:
        if ins.qct is None and not fwd_qs.exists():
            if wm.state == WMaterial.WM_TEST:
                # 更新对应的车间库存
                wm.count = wm.count - ins.count
                if wm.count >= 0:
                    # 已检测的数量
                    wm.count_xtest = wm.count_xtest + ins.count
                    wm.save()
                else:
                    raise ParseError("超过待检数量")

                # 寻找count_notok_json中的b类不合格不进行处理
                count_notok_json = ins.count_notok_json
                need_move_count = 0
                for k, v in count_notok_json.items():
                    if v > 0:
                        notok_sign = k.replace('count_n_', '')
                        if NotOkOption.get_extra_info(notok_sign)['cate'] == 'ok_b':  # b类不合格在后续处理
                            need_move_count = need_move_count + v
                count_ok = ins.count_ok - need_move_count
                if count_ok > 0:
                    wm, new_create = WMaterial.locked_get_or_create_inventory(
                        material=wm.material,
                        batch=wm.batch,
                        mgroup=wm.mgroup,
                        belong_dept=wm.belong_dept,
                        state=WMaterial.WM_OK,
                        notok_sign=None,
                        defaults={
                            'count': count_ok,
                        }
                    )
                    if not new_create:
                        wm.count = wm.count + count_ok
                        wm.save()
            else: # 如果直接对工段库存检验 , 则直接扣减不合格数
                wm.count = wm.count - ins.count_notok
                if wm.count >= 0:
                    wm.save()
                else:
                    raise ParseError("不合格数不可大于批次数量")
        
            # 生成不合格的
            process = wm.material.process
            if process.store_notok:
                count_notok_json = ins.count_notok_json
                for k, v in count_notok_json.items():
                    if v > 0:
                        notok_sign = k.replace('count_n_', '')
                        astate = WMaterial.WM_NOTOK
                        if NotOkOption.get_extra_info(notok_sign)['cate'] == 'ok_b':
                            astate = WMaterial.WM_OK
                        wm_n, new_create = WMaterial.locked_get_or_create_inventory(
                            material=wm.material,
                            batch=wm.batch,
                            mgroup=wm.mgroup,
                            belong_dept=wm.belong_dept,
                            notok_sign=notok_sign,
                            state=astate,
                            defaults={
                                'count': v,
                                'material': wm.material,
                                'batch': wm.batch,
                                'mgroup': wm.mgroup,
                                'belong_dept': wm.belong_dept,
                                'notok_sign': notok_sign,
                                'state': astate,
                            }
                        )
                        if not new_create:
                            wm_n.count = wm_n.count + v
                            wm_n.save()
        else:
            wm:WMaterial = ins.wm
            # 此时调用了qct表
            for item in fwd_qs:
                item:FtestworkDefect = item
                if item.count > 0:
                    wm.count = wm.count - item.count
                    if wm.count < 0:
                        raise ParseError("数量不足,扣减失败")
                    wm.save()
                    wmstate = WMaterial.WM_OK
                    if item.defect.okcate == Defect.DEFECT_NOTOK:
                        wmstate = WMaterial.WM_NOTOK
                    wmx, new_create = WMaterial.locked_get_or_create_inventory(
                        material=wm.material,
                        batch=wm.batch,
                        mgroup=wm.mgroup,
                        belong_dept=wm.belong_dept,
                        state=wmstate,
                        notok_sign=None,
                        defect=item.defect,
                        defaults={
                            'count': item.count,
                        }
                    )
                    if not new_create:
                        wmx.count = wmx.count + item.count
                        wmx.save()
    
    if ins.mb:
        mb = MaterialBatch.objects.select_for_update().get(pk=ins.mb_id)
        for item in fwd_qs:
            item:FtestworkDefect = item
            if item.count > 0:
                mb.count = mb.count - item.count
                if mb.count < 0:
                    raise ParseError("数量不足,扣减失败")
                mb.save()
                mbstate = WMaterial.WM_OK
                if item.defect.okcate == Defect.DEFECT_NOTOK:
                    mbstate = WMaterial.WM_NOTOK
                mbx, new_create = MaterialBatch.objects.get_or_create(
                    material=mb.material, 
                    warehouse=mb.warehouse, 
                    batch=mb.batch, 
                    defect=item.defect,
                    state=mbstate,
                    defaults={
                        'count': item.count,
                    }
                )
                if not new_create:
                    mbx.count = mbx.count + item.count
                    mbx.save()
    ins.submit_user = user
    ins.submit_time = timezone.now()
    ins.save()
    # 触发批次统计分析
    ana_batch_thread(xbatchs=[ins.batch])


@transaction.atomic
def ftestwork_revert(ins: FtestWork):
    ins = FtestWork.objects.select_for_update().get(pk=ins.pk)
    if ins.submit_time is None:
        raise ParseError('该检验工作未提交')
    wm = (
        WMaterial.objects.select_for_update().get(pk=ins.wm_id)
        if ins.wm_id else None
    )
    if wm and ins.need_update_wm:
        fwd_qs = FtestworkDefect.objects.filter(ftestwork=ins)
        for item in fwd_qs:
            item:FtestworkDefect = item
            if item.count > 0:
                wm.count = wm.count + item.count
                wm.save()
                wmstate = WMaterial.WM_OK
                if item.defect.okcate == Defect.DEFECT_NOTOK:
                    wmstate = WMaterial.WM_NOTOK
                try:
                    wmx = WMaterial.objects.get(
                        material=wm.material,
                        batch=wm.batch,
                        mgroup=wm.mgroup,
                        belong_dept=wm.belong_dept,
                        state=wmstate,
                        notok_sign=None,
                        defect=item.defect,
                    )
                except Exception as e:
                    raise ParseError(f'找不到{item.defect.name}的车间库存: {str(e)}')
                wmx.count = wmx.count - item.count
                if wmx.count < 0:
                    raise ParseError("数量不足,撤回失败")
                wmx.save()
    else:
        raise ParseError("该检验工作不支持撤回")
    ins.submit_user = None
    ins.submit_time = None
    ins.save()
    # 触发批次统计分析
    ana_batch_thread(xbatchs=[ins.batch])

def bind_ftestwork(ticket: Ticket, transition, new_ticket_data: dict):
    ins = FtestWork.objects.get(id=new_ticket_data['t_id'])
    if ins.submit_time is not None:
        raise ParseError('该检验工作不可提交审批')
    ftestwork_submit_validate(ins)
    ticket_data = ticket.ticket_data
    ticket_data.update({
        't_model': 'ftestwork',
        't_id': ins.id,
    })
    ticket.ticket_data = ticket_data
    ticket.create_by = ins.create_by
    ticket.save()
    if ins.ticket is None:
        ins.ticket = ticket
        ins.save()

def ftestwork_audit_end(ticket: Ticket):
    ins = FtestWork.objects.get(id=ticket.ticket_data['t_id'])
    ftestwork_submit(ins, ticket.create_by)
