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

import cn.estsh.i3plus.ext.mes.pcn.api.busi.IMesProductionCustomContextStepService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.IMesProductionDispatchContextStepService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.IMesProductionProcessContextStepService;
import cn.estsh.i3plus.ext.mes.pcn.pojo.context.*;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtConstWords;
import cn.estsh.i3plus.mes.pcn.actor.shipping.dispatch.IFsmCommonService;
import cn.estsh.i3plus.mes.pcn.serviceimpl.fsm.BaseStepService;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
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 com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Description : 扫描主条码工步 【非排序】
 * @Author : wangjie
 **/
@Slf4j
@Service("mesProductSnScanStepService")
public class MesProductSnScanStepService extends BaseStepService {

    @Autowired
    private IMesProductionProcessContextStepService productionProcessContextStepService;

    @Autowired
    private IMesProductionDispatchContextStepService productionDispatchContextStepService;

    @Autowired
    private IMesProductionCustomContextStepService productionCustomContextStepService;

    @Autowired
    private IFsmCommonService fsmCommonService;

    @Override
    public StepResult guide(StationRequestBean reqBean) {

        //发送工步内容
        productionCustomContextStepService.sendStepContextMessage(reqBean);

        return stepSuccessCompleteAndSendGuideReturn(reqBean, new StationResultBean().writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.PROCESS.getValue()), "请扫描主条码!");

    }

    @Override
    public StepResult execute(StationRequestBean reqBean) {

        StationResultBean resultBean = new StationResultBean();

        StepResult stepResult = StepResult.getSuccessComplete();

        //扫描信息置空
        String scanInfo = reqBean.resetScanInfo(reqBean.getScanInfo());

        //发送工步内容
        if (!StringUtils.isEmpty(scanInfo)) productionCustomContextStepService.sendStepContextMessage(reqBean, scanInfo, MesExtEnumUtil.CELL_MESSAGE_SOURCE.SCAN);

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

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

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

        //当前工位使用的设备
        MesCellEquipContext cellEquipContext = productionProcessContext.getCurCellEquip();

        //获取上下文产出零件信息
        List<MesProductionPartContext> productionPartContextList = productionDispatchContextStepService.getProductionPartContext(reqBean);

        //获取上下文扫/读信息:主条码
        List<MesEquipVariableCollectContext> equipVariableCollectContextList = productionDispatchContextStepService.getScanProductSnContext(reqBean);

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

        //验证扫描信息是否属于工艺强过码
        Boolean isCraftJumpCode = (!StringUtils.isEmpty(scanInfo) && scanInfo.equals(productionProcessContext.getCraftJumpCode())) ? true : false;

        //不属于工艺强过码,并且存在扫描信息,剔除此前已标记工艺防错结果错误的数据
        if (!isCraftJumpCode && !StringUtils.isEmpty(scanInfo) && !CollectionUtils.isEmpty(productionPsInContextList)) productionPsInContextList = updateProductionPsInContextList(reqBean, productionPsInContextList);

        //先判断前期扫描数量
        Integer scanedQty = calcScanedQty(equipVariableCollectContextList, productionPsInContextList);

        //根据产出零件或者腔数拿到需要的扫描数量, 优先使用产出零件数量
        Integer needQty = !CollectionUtils.isEmpty(productionPartContextList) ? productionPartContextList.size() : cellEquipContext.getCavity();

        //验证前期扫描数量是否已超过匹配腔数
        checkIsAboveNeedQty(reqBean, resultBean, stepResult, scanedQty, needQty, equipVariableCollectContextList, productionPsInContextList, productionPartContextList, cellEquipContext);

        //验证是否支持跳过
        Boolean isAllowJump = checkIsAllowJump(reqBean, productionPsInContextList);

        //当前不允许跳过
        if (isCraftJumpCode && (StringUtils.isEmpty(isAllowJump) || !isAllowJump))
            return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.SCAN.getValue()).scanInfo(scanInfo), stepResult, String.format("当前扫描信息工艺强过码[%s],当前不支持强过!", scanInfo));

        //允许跳过,先更新数据
        if (isCraftJumpCode && !StringUtils.isEmpty(isAllowJump) && isAllowJump) {
            updateProductionPsInContextList(reqBean, productionPsInContextList, scanInfo);
            return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.SCAN.getValue()).scanInfo(scanInfo), stepResult, String.format("当前扫描信息工艺强过码[%s]!", scanInfo));
        }

        //扫描信息为空 并且 判断当前是否必须要扫描
        if (StringUtils.isEmpty(scanInfo) && (scanedQty.compareTo(needQty) < 0 || StringUtils.isEmpty(isAllowJump) || isAllowJump)) stepSendGuideAndThrowEx(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.PROCESS.getValue()), "请扫描主条码!");

        //是否支持混腔扫描[工位参数]
        Boolean cavityNosortCfg = fsmCommonService.checkWcpcMapIsMatchTrue(reqBean, MesPcnExtConstWords.CAVITY_NOSORT_CFG);

        //封装待验证的主条码信息
        if (!scanInfo.equals(productionProcessContext.getFinishCode())) equipVariableCollectContextList = doHandleScanProductSnContext(reqBean, scanInfo, equipVariableCollectContextList, cavityNosortCfg);

        //验证是否存在重复主条码
        if (checkIsExistRepeatScan(productionProcessContext, equipVariableCollectContextList, productionPsInContextList))
            return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.SCAN.getValue()).scanInfo(scanInfo), stepResult, String.format("当前扫描信息主条码[%s],当前存在重复扫描的主条码!", scanInfo));

        Boolean scanInfoIsFinishCode = !scanInfo.equals(productionProcessContext.getFinishCode()) ? false : true;

        //比对产出零件的空腔数量与主条码的空腔数量, 产出零件的空腔数必须小于 剩余待扫描主条码数量加上已扫描的空腔数量
        if (!checkFinishCodeSize(stepResult, productionPartContextList, productionPsInContextList, scanInfoIsFinishCode).isCompleted()) return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, stepResult.getMsg());

        //保存上下文扫/读信息:主条码  或者 添加了finishCode后的主条码数据
        if (!scanInfoIsFinishCode) productionDispatchContextStepService.dispatchScanProductSnContext(reqBean, equipVariableCollectContextList);
        else productionPsInContextList = saveProductionPsInContextList(reqBean, productionPsInContextList);

        //前期扫描数量已等于腔数
        scanedQty = calcScanedQty(equipVariableCollectContextList, productionPsInContextList);

        String scanInfoName = !scanInfo.equals(productionProcessContext.getFinishCode()) ? "主条码" : "空腔码";

        if (scanedQty.compareTo(needQty) < 0 && cavityNosortCfg)
            return stepDynamicsCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.SCAN.getValue()).scanInfo(scanInfo), stepResult, false,
                    MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.TEXT, String.format("当前扫描信息%s[%s],已知腔数[%s],还需要再连续扫描[%s]次主条码!", scanInfoName, scanInfo, needQty, needQty - equipVariableCollectContextList.size()));

        //扫描场景 验证是否全部为空腔
        if (!checkIsAllFinishCode(productionPsInContextList, equipVariableCollectContextList))
            return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.SCAN.getValue()).scanInfo(scanInfo), stepResult, String.format("当前扫描信息%s[%s]%s!", scanInfoName, scanInfo, scanedQty.compareTo(needQty) == 0 ? String.format(",当前主条码个数[%s]已满足腔数", scanedQty) : MesPcnExtConstWords.EMPTY));

        //删除上下文扫/读信息:主条码
        productionDispatchContextStepService.removeScanProductSnContext(reqBean);
        //删除进料主条码数据
        productionDispatchContextStepService.removeProductionPsInContext(reqBean);

        return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, "当前扫描的主条码信息全部为空腔,已清除当前的空腔数据!");

    }

    private Integer calcScanedQty(List<MesEquipVariableCollectContext> equipVariableCollectContextList, List<MesProductionPsInContext> productionPsInContextList) {
        return (CollectionUtils.isEmpty(equipVariableCollectContextList) ? MesPcnExtConstWords.ZERO : equipVariableCollectContextList.size()) + (CollectionUtils.isEmpty(productionPsInContextList) ? MesPcnExtConstWords.ZERO : productionPsInContextList.size());
    }

    //不属于工艺强过码,并且存在扫描信息,剔除此前已标记工艺防错结果错误的数据
    private List<MesProductionPsInContext> updateProductionPsInContextList(StationRequestBean reqBean, List<MesProductionPsInContext> productionPsInContextList) {
        Integer sourceSize = productionPsInContextList.size();
        productionPsInContextList = productionPsInContextList.stream().filter(o -> (null != o && o.getCheckCraftResult().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) != 0)).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(productionPsInContextList) && productionPsInContextList.size() == sourceSize) return productionPsInContextList;
        //保存进料主条码数据
        productionDispatchContextStepService.dispatchProductionPsInContext(reqBean, productionPsInContextList);
        return productionPsInContextList;
    }

    //封装无需验证的finishCode空腔数据
    private List<MesProductionPsInContext> saveProductionPsInContextList(StationRequestBean reqBean, List<MesProductionPsInContext> productionPsInContextList) {
        if (CollectionUtils.isEmpty(productionPsInContextList)) productionPsInContextList = new ArrayList<>();
        productionPsInContextList.add(new MesProductionPsInContext().isFinishCode());
        //保存进料主条码数据
        productionDispatchContextStepService.dispatchProductionPsInContext(reqBean, productionPsInContextList);
        return productionPsInContextList;
    }

    //验证是否支持跳过
    private Boolean checkIsAllowJump(StationRequestBean reqBean, List<MesProductionPsInContext> productionPsInContextList) {
        Optional<MesProductionPsInContext> optional = CollectionUtils.isEmpty(productionPsInContextList) ? null :
                productionPsInContextList.stream().filter(o -> (null != o && o.getCheckCraftResult().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0)).findFirst();
        if (null == optional || !optional.isPresent()) return false;
        //非排序是否允许强过【工位参数】, 默认不支持, 不支持返回null, 存在可以跳过的数据且支持跳过返回true
        String isAllowJump = fsmCommonService.checkWcpcMapIsMatchValue(reqBean, MesPcnExtConstWords.IS_ALLOW_JUMP, CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValueStr());
        return StringUtils.isEmpty(isAllowJump) ? null : true;
    }

    //验证扫描信息属于工艺强过码 并且支持跳过，进行数据变更
    private void updateProductionPsInContextList(StationRequestBean reqBean, List<MesProductionPsInContext> productionPsInContextList, String craftJumpCode) {
        productionPsInContextList.forEach(o -> {
            if (o.getCheckCraftResult().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0) o.setCraftJumpCode(craftJumpCode);
        });
        //保存进料主条码数据
        productionDispatchContextStepService.dispatchProductionPsInContext(reqBean, productionPsInContextList);
    }

    //验证前期扫描数量是否已超过匹配腔数
    private void checkIsAboveNeedQty(StationRequestBean reqBean, StationResultBean resultBean, StepResult stepResult, Integer scanedQty, Integer needQty,
                                     List<MesEquipVariableCollectContext> equipVariableCollectContextList, List<MesProductionPsInContext> productionPsInContextList, List<MesProductionPartContext> productionPartContextList, MesCellEquipContext cellEquipContext) {
        if (scanedQty.compareTo(needQty) <= 0) return;
        String message = String.format("请检查相关数据,当前主条码个数[%s]已满足腔数[%s],可重置工序解决!", scanedQty, needQty);
        this.sendMessage(reqBean, resultBean, message +
                " --- List<MesEquipVariableCollectContext> --- [" + getJsonMessage(equipVariableCollectContextList) + "]" +
                " --- List<MesProductionPsInContext> --- [" + getJsonMessage(productionPsInContextList) + "]" +
                " --- List<MesProductionPartContext> --- [" + getJsonMessage(productionPartContextList) + "]" +
                " --- MesCellEquipContext --- [" + JSONObject.toJSONString(cellEquipContext) + "] ---", MesPcnEnumUtil.STATION_BUSI_TYPE.RUNNING_INFO, MesPcnEnumUtil.STATION_DATA_TYPE.TEXT);
        stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), message);
    }

    //集合判空转JSON
    private String getJsonMessage(List list) {
        return CollectionUtils.isEmpty(list) ? MesPcnExtConstWords.EMPTY : JSONObject.toJSONString(list);
    }

    //封装扫/读信息:主条码信息
    private List<MesEquipVariableCollectContext> doHandleScanProductSnContext(StationRequestBean reqBean, String scanInfo, List<MesEquipVariableCollectContext> equipVariableCollectContextList, Boolean cavityNosortCfg) {

        if (CollectionUtils.isEmpty(equipVariableCollectContextList)) equipVariableCollectContextList = new ArrayList<>();

        equipVariableCollectContextList.add(new MesEquipVariableCollectContext(reqBean.getOrganizeCode(), scanInfo, !cavityNosortCfg ? MesExtEnumUtil.CELL_MESSAGE_SOURCE.SCAN.getValue() : MesExtEnumUtil.CELL_MESSAGE_SOURCE.MANY.getValue()));

        return equipVariableCollectContextList;

    }

    //验证是否存在重复主条码
    private Boolean checkIsExistRepeatScan(MesProductionProcessContext productionProcessContext, List<MesEquipVariableCollectContext> equipVariableCollectContextList, List<MesProductionPsInContext> productionPsInContextList) {

        //扫描条码
        List<String> filterList1 = CollectionUtils.isEmpty(equipVariableCollectContextList) ? null :
                equipVariableCollectContextList.stream().filter(o -> null != o).map(MesEquipVariableCollectContext::getEquipVariableValue).collect(Collectors.toList());
        //已验证条码 剔除finishCode信息
        List<String> filterList2 = CollectionUtils.isEmpty(productionPsInContextList) ? null :
                productionPsInContextList.stream().filter(o -> (null != o && o.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0)).map(MesProductionPsInContext::getProductSn).collect(Collectors.toList());

        List<String> filterList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(filterList1)) filterList.addAll(filterList1);
        if (!CollectionUtils.isEmpty(filterList2)) filterList.addAll(filterList2);

        //验证是否存在重复条码
        if (!CollectionUtils.isEmpty(filterList) && filterList.size() != filterList.stream().filter(o -> !StringUtils.isEmpty(o)).distinct().collect(Collectors.toList()).size()) return true;

        return false;

    }

    //比对产出零件的空腔数量与主条码的空腔数量, 产出零件的空腔数必须小于等于 剩余待扫描主条码数量加上已扫描的空腔数量
    private StepResult checkFinishCodeSize(StepResult stepResult, List<MesProductionPartContext> productionPartContextList, List<MesProductionPsInContext> productionPsInContextList, Boolean scanInfoIsFinishCode) {

        //没有产出零件 不需要对比空腔数量
        if (CollectionUtils.isEmpty(productionPartContextList)) return stepResult;

        List<MesProductionPartContext> finishCodePpList = productionPartContextList.stream().filter(o -> (null != o && o.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) == 0)).collect(Collectors.toList());
        List<MesProductionPsInContext> finishCodePsList = CollectionUtils.isEmpty(productionPsInContextList) ? null : productionPsInContextList.stream().filter(o -> (null != o && o.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) == 0)).collect(Collectors.toList());

        //产出零件的空腔数
        Integer ppSize = CollectionUtils.isEmpty(finishCodePpList) ? MesPcnExtConstWords.ZERO : finishCodePpList.size();
        //已扫描的主条码空腔数量
        Integer psSize = (CollectionUtils.isEmpty(finishCodePsList) ? MesPcnExtConstWords.ZERO : finishCodePsList.size()) + (!scanInfoIsFinishCode ? MesPcnExtConstWords.ZERO : MesPcnExtConstWords.ONE);
        //剩余待扫描主条码数量
        Integer remainSize = productionPartContextList.size() - (CollectionUtils.isEmpty(productionPsInContextList) ? MesPcnExtConstWords.ZERO : productionPsInContextList.size()) - MesPcnExtConstWords.ONE;

        return ppSize.compareTo(remainSize + psSize) <= 0 ? stepResult : stepResult.isCompleted(false).msg(String.format("产出零件存在空腔数[%s],扫描的主条码空腔数[%s],当前主条码必须扫描空腔码!", ppSize, psSize));

    }

    private Boolean checkIsAllFinishCode(List<MesProductionPsInContext> productionPsInContextList, List<MesEquipVariableCollectContext> equipVariableCollectContextList) {
        //扫描场景 验证是否全部为空腔
        Optional<MesProductionPsInContext> optional = CollectionUtils.isEmpty(productionPsInContextList) ? null : productionPsInContextList.stream().filter(o -> (null != o && o.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue()) == 0)).findFirst();
        //已验证的主条码全部空腔 并且 当前待验证主条码不存在数据 则全部空腔
        return ((null == optional || !optional.isPresent()) && CollectionUtils.isEmpty(equipVariableCollectContextList)) ? true : false;
    }

}
