package cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.step;

import cn.estsh.i3plus.ext.mes.pcn.pojo.context.MesProductionProcessContext;
import cn.estsh.i3plus.ext.mes.pcn.pojo.context.MesProductionPsInContext;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtConstWords;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
import cn.estsh.i3plus.pojo.mes.bean.MesCraftRouteDetail;
import cn.estsh.i3plus.pojo.mes.bean.MesProductionRecord;
import cn.estsh.i3plus.pojo.mes.bean.MesWorkCenter;
import cn.estsh.i3plus.pojo.mes.model.StationRequestBean;
import cn.estsh.i3plus.pojo.mes.model.StationResultBean;
import cn.estsh.i3plus.pojo.mes.model.StepResult;
import cn.estsh.i3plus.pojo.mes.util.MesExtEnumUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description : 前道工艺防错工步 【排序】
 * @Author : wangjie
 **/
@Slf4j
@Service("mesProdCraftRouteCheckSortStepService")
public class MesProdCraftRouteCheckSortStepService extends MesProdCraftRouteCheckStepService {

    @Override
    public StepResult execute(StationRequestBean reqBean) {

        StationResultBean resultBean = new StationResultBean();

        StepResult stepResult = StepResult.getSuccessComplete();

        //获取上下文信息
        MesProductionProcessContext productionProcessContext = productionProcessContextStepService.dispatchCurCellEquipment(reqBean);

        //获取生产过程上下文对象有异常信息 抛出异常
        if (!productionProcessContext.getSuccess()) stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), productionProcessContext.getMessage());

        //存储生产过程上下文对象
        productionProcessContextStepService.dispatchProductionProcessContext(reqBean, productionProcessContext);

        //获取产品条码数据信息
        List<MesProductionPsInContext> productionPsInContextList = productionDispatchContextStepService.getProductionPsInContext(reqBean);

        if (CollectionUtils.isEmpty(productionPsInContextList)) stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), "当前无产品条码信息,前道工艺防错验证异常,请重置工序!");

        //验证是否零件为空的产品条码
        if (isExistProductSnNoPart(productionPsInContextList)) stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), "当前存在零件号[空]的产品条码信息,前道工艺防错验证异常,请重置工序!");

        //验证工位是否设置需要前道防错
        if (!isNeedCheckCraft(productionPsInContextList))
            return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, String.format("生产线[%s]工位[%s]未设置前道工艺防错,无需进行前道工艺防错验证!", reqBean.getWorkCenterCode(), reqBean.getWorkCellCode()));

        //验证是否存在工艺强过码, 存在则保存 并返回强过的产品条码
        List<String> productSnList2Jump = doHandleCraftJumpCode(reqBean, productionPsInContextList);
        if (!CollectionUtils.isEmpty(productSnList2Jump))
            return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog().scanInfo(productSnList2Jump.toString()), stepResult, String.format("产品条码%s跳过前道工艺防错验证成功!", productSnList2Jump.toString()));

        List<String> partNoList = (productionPsInContextList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getPartNo()) &&
                o.getCheckCraftResult().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) != 0)).map(MesProductionPsInContext::getPartNo).collect(Collectors.toList())).stream().filter(o -> !StringUtils.isEmpty(o)).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(partNoList)) return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean, stepResult, "产品条码均已通过前道工艺防错验证!");

        //从上下文中取出生产线对象
        MesWorkCenter workCenter = productionProcessContext.getWorkCenter();

        //查询工艺路线数据
        Map<String, List<MesCraftRouteDetail>> craftRouteDataMap = new HashMap<>();
        partNoList.forEach(o -> handleProdCraftData(reqBean, workCenter, o, craftRouteDataMap, true));

        if (CollectionUtils.isEmpty(craftRouteDataMap) || craftRouteDataMap.size() != partNoList.size()) {
            //剔除未验证的进料产品条码后保存数据
            productionDispatchContextStepService.dispatchProductionPsInContext(reqBean, productionPsInContextList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getForeignKey()))).collect(Collectors.toList()));
            return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, String.format("零件编码%s未匹配到产品工艺路线!",
                    CollectionUtils.isEmpty(craftRouteDataMap) ? partNoList.toString() : partNoList.stream().filter(o -> (!StringUtils.isEmpty(o) && !craftRouteDataMap.containsKey(o))).collect(Collectors.toList()).toString()));
        }

        //前道工艺防错验证处理
        doHandleProdCraftRouteCheck(reqBean, resultBean, stepResult, productionProcessContext, workCenter, productionPsInContextList, craftRouteDataMap);

        return stepResult;

    }

    //验证是否存在零件为空的产品条码
    private Boolean isExistProductSnNoPart(List<MesProductionPsInContext> productionPsInContextList) {
        Optional<MesProductionPsInContext> optional = productionPsInContextList.stream().filter(o -> (null != o && o.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0 && StringUtils.isEmpty(o.getPartNo()))).findFirst();
        return (null != optional && optional.isPresent()) ? true : false;
    }

    //前道工艺防错验证处理
    private void doHandleProdCraftRouteCheck(StationRequestBean reqBean, StationResultBean resultBean, StepResult stepResult, MesProductionProcessContext productionProcessContext,
                                             MesWorkCenter workCenter, List<MesProductionPsInContext> productionPsInContextList, Map<String, List<MesCraftRouteDetail>> craftRouteDataMap) {

        List<String> productSnList = new ArrayList<>();

        for (MesProductionPsInContext productionPsInContext : productionPsInContextList) {

            if (null == productionPsInContext) continue;
            if (StringUtils.isEmpty(productionPsInContext.getPartNo())) continue;
            if (productionPsInContext.getCheckCraftResult().compareTo(MesPcnExtConstWords.ZERO) != 0) continue;

            //前道工艺防错验证
            Boolean result = doProdCraftRouteCheckSort(reqBean, stepResult, productionProcessContext, productionPsInContext, craftRouteDataMap.get(productionPsInContext.getPartNo()));

            productionPsInContext.checkCraftResult(!result ? CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue() : CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue());

            productSnList.add(productionPsInContext.getProductSn());

        }

        //保存进料产品条码数据
        productionDispatchContextStepService.dispatchProductionPsInContext(reqBean, productionPsInContextList);

        if (stepResult.isCompleted()) this.sendMessage(reqBean, resultBean.writeDbLog().scanInfo(productSnList.toString()), String.format("产品条码%s前道工艺防错验证成功!", productSnList), MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.TEXT);
        else this.sendMessage(reqBean, resultBean.writeDbLog().scanInfo(productSnList.toString()), stepResult.getMsg(), MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);

    }

    //前道工艺防错验证【排序】
    private Boolean doProdCraftRouteCheckSort(StationRequestBean reqBean, StepResult stepResult, MesProductionProcessContext productionProcessContext, MesProductionPsInContext productionPsInContext, List<MesCraftRouteDetail> craftRouteDetailList) {

        String message = StringUtils.isEmpty(stepResult.getMsg()) ? MesPcnExtConstWords.EMPTY : stepResult.getMsg() + MesPcnExtConstWords.SEMICOLON;

        //判断产品条码的当前工艺是否包含在产品工艺路线中
        Optional<MesCraftRouteDetail> optionalPs = StringUtils.isEmpty(productionPsInContext.getCraftCode()) ? null : craftRouteDetailList.stream().filter(o -> (null != o && o.getCraftCode().equals(productionPsInContext.getCraftCode()))).findFirst();
        if (!StringUtils.isEmpty(productionPsInContext.getCraftCode()) && (null == optionalPs || !optionalPs.isPresent()))
            return stepResult.isCompleted(false).msg(String.format("%s产品条码[%s]对应的工艺代码[%s]不匹配零件[%s]对应的产品工艺路线[%s]",
                    message, productionPsInContext.getProductSn(), productionPsInContext.getCraftCode(), productionPsInContext.getPartNo(), craftRouteDetailList.get(0).getCraftRouteCode())).isCompleted();

        //判断当前工位的工序对应的工艺是否包含在产品工艺路线中
        Optional<MesCraftRouteDetail> optionalCell = craftRouteDetailList.stream().filter(o -> (null != o && o.getCraftCode().equals(productionProcessContext.getCraftCode()))).findFirst();
        if (null == optionalCell || !optionalCell.isPresent())
            return stepResult.isCompleted(false).msg(String.format("%s产品条码[%s]零件[%s]对应的产品工艺路线[%s]不包含当前工位[%s]对应的工艺[%s]工序[%s]",
                    message, productionPsInContext.getProductSn(), productionPsInContext.getPartNo(), craftRouteDetailList.get(0).getCraftRouteCode(), reqBean.getWorkCellCode(), reqBean.getProcessCode(), productionProcessContext.getCraftCode())).isCompleted();

        //验证工艺对应工序最多经过1个: 当前产品条码的工艺字段有值, 对应的工艺路线明细信息设置了【true】, 当前工位的工艺与产品条码的当前工艺相同, 当前工位的工序与产品条码的当前工序不相同
        if (null != optionalPs && optionalPs.isPresent() && optionalPs.get().getAtMostProcess().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) == 0 && productionProcessContext.getCraftCode().equals(productionPsInContext.getCraftCode()) && !reqBean.getProcessCode().equals(productionPsInContext.getProcessCode()))
            return stepResult.isCompleted(false).msg(String.format("%s产品条码[%s]产品工艺路线[%s]相同工艺对应工序最多经过1个,上道工艺[%s]当前工位工艺[%s]",
                    message, productionPsInContext.getProductSn(), craftRouteDetailList.get(0).getCraftRouteCode(), productionPsInContext.getCraftCode(), productionProcessContext.getCraftCode())).isCompleted();

        //验证是否捆绑后道工艺: 如果当前工位的工艺与产品条码的当前工艺不一致的情况下, 根据产品条码的当前工艺获取在工艺路线明细的下一个工艺, 判断当前工位的工艺与下个工艺是否一致
        if (null != optionalPs && optionalPs.isPresent() && optionalPs.get().getIsBindNextCraft().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) == 0 && !productionProcessContext.getCraftCode().equals(productionPsInContext.getCraftCode())) {
            Optional<MesCraftRouteDetail> optional = craftRouteDetailList.stream().filter(o -> (null != o && o.getSeq().compareTo(optionalPs.get().getSeq()) > 0)).findFirst();
            if (null != optionalCell && optionalCell.isPresent() && !productionProcessContext.getCraftCode().equals(optional.get().getCraftCode()))
                return stepResult.isCompleted(false).msg(String.format("%s产品条码[%s]产品工艺路线[%s]捆绑后道工艺,上道工艺[%s]下道工艺[%s]当前工位工艺[%s]",
                        message, productionPsInContext.getProductSn(), craftRouteDetailList.get(0).getCraftRouteCode(), productionPsInContext.getCraftCode(), optional.get().getCraftCode(), productionProcessContext.getCraftCode())).isCompleted();
        }

        //验证已完成工序最大重复次数: 如果当前工位的工序与产品条码的当前工序一致的情况下, 根据条码+物料+工序+工艺查询加工记录, 判断加工记录条数
        if (null != optionalPs && optionalPs.isPresent() && reqBean.getProcessCode().equals(productionPsInContext.getProcessCode())) {
            List<MesProductionRecord> productionRecordList = productionRecordService.findProductionRecordList(reqBean.getOrganizeCode(), productionPsInContext.getProductSn(), productionPsInContext.getPartNo(), reqBean.getProcessCode(), productionProcessContext.getCraftCode());
            if (!CollectionUtils.isEmpty(productionRecordList) && productionRecordList.size() >= ((!StringUtils.isEmpty(optionalPs.get().getRepeatTimes()) && optionalPs.get().getRepeatTimes().compareTo(MesPcnExtConstWords.ZERO) > 0) ? optionalPs.get().getRepeatTimes() : MesPcnExtConstWords.ONE))
                return stepResult.isCompleted(false).msg(String.format("%s产品条码[%s]当前工序[%s]产品工艺路线[%s]已完成工序最大重复次数[%s]",
                        message, productionPsInContext.getProductSn(), reqBean.getProcessCode(), craftRouteDetailList.get(0).getCraftRouteCode(), optionalPs.get().getRepeatTimes())).isCompleted();
        }

        //根据当前工位对应的工艺 获取 所有前道工艺
        List<MesCraftRouteDetail> beforeCellCraftList = craftRouteDetailList.stream().filter(o -> (null != o && o.getSeq().compareTo(optionalCell.get().getSeq()) < 0)).collect(Collectors.toList());

        //验证首工艺: 当前产品条码的工艺字段有值,并且当前工位的工艺不等于产品条码的当前工艺。   如果等于已经在【验证工艺对应工序最多经过1个】与【 验证已完成工序最大重复次数】 中验证通过,所以这边只考虑不相等
        if (CollectionUtils.isEmpty(beforeCellCraftList) && !StringUtils.isEmpty(productionPsInContext.getCraftCode()) && !productionProcessContext.getCraftCode().equals(productionPsInContext.getCraftCode()))
            return stepResult.isCompleted(false).msg(String.format("%s产品条码[%s]已经过首道工艺[%s]", message, productionPsInContext.getProductSn(), productionProcessContext.getCraftCode())).isCompleted();

        //执行首道工艺    当前产品条码验证通过
        if (CollectionUtils.isEmpty(beforeCellCraftList)) return true;

        //当前产品条码的工艺字段为空  验证首工艺是否存在工单装配件信息
        if (StringUtils.isEmpty(productionPsInContext.getCraftCode()) && craftRouteDetailList.get(0).getIsChoose().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0 && checkIsExistWorkOrderAssembly(productionPsInContext, craftRouteDetailList.get(0).getCraftCode()))
            return stepResult.isCompleted(false).msg(String.format("%s产品条码[%s]未经过首道工艺[%s]", message, productionPsInContext.getProductSn(), craftRouteDetailList.get(0).getCraftCode())).isCompleted();

        //搜集产品条码当前工艺于当前工位对应工艺之间的未完成的工艺集合
        List<MesCraftRouteDetail> unCompleteCraftList;
        if (StringUtils.isEmpty(productionPsInContext.getCraftCode())) unCompleteCraftList = beforeCellCraftList;
        else {
            Optional<MesCraftRouteDetail> optional = beforeCellCraftList.stream().filter(o -> (null != o && o.getCraftCode().equals(productionPsInContext.getCraftCode()))).findFirst();
            if (null == optional || !optional.isPresent()) //在工位对应的工艺之前的所有前道工艺中未找到产品条码的当前工艺则表示产品条码已经过当前工艺
                return stepResult.isCompleted(false).msg(String.format("%s产品条码[%s]已经过当前工艺[%s]", message, productionPsInContext.getProductSn(), productionProcessContext.getCraftCode())).isCompleted();
            unCompleteCraftList = beforeCellCraftList.stream().filter(o -> (null != o && o.getSeq().compareTo(optional.get().getSeq()) > 0)).collect(Collectors.toList());
        }

        //如果没有未完成的工艺, 则代表当前工位可操作
        if (CollectionUtils.isEmpty(unCompleteCraftList)) return true;

        //未完成工艺倒序
        unCompleteCraftList = unCompleteCraftList.stream().filter(o -> null != o).sorted(Comparator.comparing(MesCraftRouteDetail::getSeq).reversed()).collect(Collectors.toList());

        //验证未完成工艺是否存在工单装配件信息
        Optional<MesCraftRouteDetail> optional = unCompleteCraftList.stream().filter(o -> (null != o && o.getIsChoose() == CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue())).findFirst();
        if (unCompleteCraftList.get(0).getIsChoose().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0 && checkIsExistWorkOrderAssembly(productionPsInContext, unCompleteCraftList.get(0).getCraftCode()))
            return stepResult.isCompleted(false).msg(String.format("%s产品条码[%s]未经过必过工艺[%s]", message, productionPsInContext.getProductSn(), unCompleteCraftList.get(0).getCraftCode())).isCompleted();

        return true;

    }

    //验证工艺是否存在工单装配件信息
    private Boolean checkIsExistWorkOrderAssembly(MesProductionPsInContext productionPsInContext, String craftCode) {
        return !CollectionUtils.isEmpty(assemblyExtService.getWorkOrderAssemblyList(productionPsInContext.getOrganizeCode(), productionPsInContext.getWorkOrderNo(), productionPsInContext.getProductSn(), craftCode)) ? true : false;
    }

}
