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

import cn.estsh.i3plus.ext.mes.pcn.api.busi.IMesProdRuleCfgExtService;
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.api.busi.IMesWorkOrderCutService;
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.serviceimpl.fsm.BaseStepService;
import cn.estsh.i3plus.mes.pcn.util.StringUtil;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
import cn.estsh.i3plus.pojo.mes.bean.MesCutSchemeEquipment;
import cn.estsh.i3plus.pojo.mes.bean.MesProdRuleIgnoreCfg;
import cn.estsh.i3plus.pojo.mes.bean.MesProdRuleNosortCfg;
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 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.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description : 裁片工单匹配加工规则工步【非排序】
 * @Author : xinwang.yi
 **/
@Slf4j
@Service("mesWorkOrderCutMatchingProdRuleStepService")
public class MesWorkOrderCutMatchingProdRuleStepService extends BaseStepService {

    @Autowired
    private IMesProductionProcessContextStepService productionProcessContextStepService;

    @Autowired
    private IMesProductionDispatchContextStepService productionDispatchContextStepService;

    @Autowired
    private IMesProdRuleCfgExtService prodRuleCfgExtService;

    @Autowired
    private IMesWorkOrderCutService workOrderCutService;

    @Override
    public StepResult execute(StationRequestBean reqBean) {

        StationResultBean resultBean = new StationResultBean();

        StepResult stepResult = StepResult.getSuccessComplete();

        //获取上下文信息: 处理加工不可用规则
        MesProductionProcessContext productionProcessContext = productionProcessContextStepService.dispatchProdRuleIgnoreCfgContext(reqBean);

        //配置错误 抛出异常
        if (!productionProcessContext.getSuccess()) stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), productionProcessContext.getMessage());

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

        //从上下文中取出设备对象
        MesCellEquipContext cellEquipContext = productionProcessContext.getCurCellEquip();

        //获取上下文加工规则数据信息集合
        List<MesProdRuleContext> prodRuleContextList = productionDispatchContextStepService.getProdRuleDataContext(reqBean);

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

        //获取上下文进料零件条码信息集合
        List<MesProductionPsInContext> productionPsInContextList = productionDispatchContextStepService.getProductionPsInContext(reqBean);

        //根据现有数据【产出零件数据】【进料零件条码信息】比对上下文中已经存在的加工规则数据信息集合, 没有加工规则的数据进行查询
        if (CollectionUtils.isEmpty(prodRuleContextList)) prodRuleContextList = new ArrayList<>();
        Integer initSize = prodRuleContextList.size();

        //封装非排序加工规则
        if (!doHandleProdRuleDataByProductionPart(reqBean, resultBean, stepResult, productionProcessContext, cellEquipContext,
                prodRuleContextList, productionPartContextList, productionPsInContextList).isCompleted()) return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, stepResult.getMsg());

        if (prodRuleContextList.size() != initSize) {
            //保存上下文产品加工规则信息集合
            productionDispatchContextStepService.dispatchProdRuleDataContext(reqBean, prodRuleContextList);
            //保存上下文产出零件信息
            if (!CollectionUtils.isEmpty(productionPartContextList)) productionDispatchContextStepService.dispatchProductionPartContext(reqBean, productionPartContextList);
            //保存进料主条码数据
            if (!CollectionUtils.isEmpty(productionPsInContextList)) productionDispatchContextStepService.dispatchProductionPsInContext(reqBean, productionPsInContextList);
        }

        return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.PROCESS.getValue()), stepResult, "匹配完成!");
    }

    //根据是否存在【产出零件数据】获取剩余待匹配的加工规则数据 (只能查询到一条,否则报错)
    private StepResult doHandleProdRuleDataByProductionPart(StationRequestBean reqBean, StationResultBean resultBean,
                                                            StepResult stepResult,
                                                            MesProductionProcessContext productionProcessContext,
                                                            MesCellEquipContext cellEquipContext,
                                                            List<MesProdRuleContext> prodRuleContextList,
                                                            List<MesProductionPartContext> productionPartContextList,
                                                            List<MesProductionPsInContext> productionPsInContextList) {

        log.info("doHandleProdRuleDataByProductionPart --- productionPartContextList --- outPartNoList:{}", productionPartContextList.stream().map(MesProductionPartContext::getPartNo).collect(Collectors.toList()));

        log.info("doHandleProdRuleDataByProductionPart --- productionPartContextList:{}", productionPartContextList);

        //拿到当前最大的foreignKey
        Optional<MesProductionPartContext> maxForeignKeyOptional = productionPartContextList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getForeignKey()))).max(Comparator.comparing(MesProductionPartContext::getForeignKey));
        Integer foreignKey = (null != maxForeignKeyOptional && maxForeignKeyOptional.isPresent()) ? maxForeignKeyOptional.get().getForeignKey() : MesPcnExtConstWords.ZERO;

        log.info("doHandleProdRuleDataByProductionPart --- foreignKey:{}", foreignKey);

        //搜集未匹配规则的产出零件
        List<String> outPartNoList = productionPartContextList.stream()
                .filter(o -> (null != o && StringUtils.isEmpty(o.getForeignKey()) && !StringUtils.isEmpty(o.getPartNo())))
                .map(MesProductionPartContext::getPartNo).collect(Collectors.toList());

        log.info("doHandleProdRuleDataByProductionPart --- outPartNoList:{}", foreignKey);

        //根据产出零件条件获取匹配当前产出零件的加工规则数据(只能查询到一条,否则报错)
        return doHandleProdRuleDataByProductionPartAndEmptyProductSn(reqBean, resultBean, stepResult, productionProcessContext,
                cellEquipContext, prodRuleContextList, productionPartContextList, productionPsInContextList, outPartNoList, foreignKey);

    }

    //没有【进料零件条码信息】信息, 根据进料[NULL]条件获取匹配当前产出零件的加工规则数据(只能查询到一条,否则报错)
    private StepResult doHandleProdRuleDataByProductionPartAndEmptyProductSn(StationRequestBean reqBean, StationResultBean resultBean, StepResult stepResult, MesProductionProcessContext productionProcessContext,
                                                                             MesCellEquipContext cellEquipContext, List<MesProdRuleContext> prodRuleContextList, List<MesProductionPartContext> productionPartContextList,
                                                                             List<MesProductionPsInContext> productionPsInContextList, List<String> outPartNoList, Integer foreignKey) {

        //获取加工不可用规则
        Map<String, List<MesProdRuleIgnoreCfg>> prodRuleIgnoreCfgMap = productionProcessContextStepService.getProdRuleIgnoreCfgContextMap(reqBean, productionProcessContext);

        //【非排序线】获取产品加工规则 ; 条件进料[NULL]
        List<MesProdRuleNosortCfg> prodRuleNosortCfgList = prodRuleCfgExtService.getProdRuleNosortCfgList(new MesProdRuleContext(reqBean.getOrganizeCode()).equipmentCode(cellEquipContext.getEquipmentCode()).outPartNos(outPartNoList).inPartIsEmpty(true));

        //根据产出零件分组数据
        Map<String, List<MesProdRuleNosortCfg>> prodRuleNosortCfgMap = groupProdRuleNosortCfgList(prodRuleNosortCfgList);

        String cutCode = productionPartContextList.get(0).getCutCode();

        MesCutSchemeEquipment cutSchemeEquipment = workOrderCutService.getCutSchemeEquipment(cutCode, cellEquipContext.getEquipmentCode(), reqBean.getOrganizeCode());

        if (cutSchemeEquipment == null)
            return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, String.format(
                    "未维护裁片方案代码[%s]设备[%s]的裁片设备配置信息!", cutCode, cellEquipContext.getEquipmentName()));

        // 根据工单分组进料条码
        Map<String, MesProductionPsInContext> productionPsInContextMapByWorkOrder = CollectionUtils.isEmpty(productionPsInContextList) ? null : productionPsInContextList.stream().collect(Collectors.toMap(MesProductionPsInContext::getWorkOrderNo, Function.identity(), (x, y) -> y));

        for (MesProductionPartContext productionPartContext : productionPartContextList) {

            log.info("doHandleProdRuleDataByProductionPartAndEmptyProductSn --- 是否找加工规则:{}", null == productionPartContext || productionPartContext.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) == 0 || !StringUtils.isEmpty(productionPartContext.getForeignKey()));

            //foreignKey有值代表已经匹配过产品加工规则
            if (null == productionPartContext || productionPartContext.getIsFinishCode().compareTo(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue()) == 0 || !StringUtils.isEmpty(productionPartContext.getForeignKey())) continue;

            List<MesProdRuleNosortCfg> filterList = !CollectionUtils.isEmpty(prodRuleNosortCfgMap) ? prodRuleNosortCfgMap.get(productionPartContext.getPartNo().toUpperCase()) : null;

            if (CollectionUtils.isEmpty(filterList) || filterList.size() > 1) {
                productionPartContextList.forEach(o -> o.busiCheckToDelete());
                return stepNonCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, String.format(
                        "设备[%s]产出零件[%s]进料零件[空]%s加工规则配置信息!", cellEquipContext.getEquipmentName(), productionPartContext.getPartNo(), CollectionUtils.isEmpty(filterList) ? "未匹配到" : String.format("匹配到[%s]条", filterList.size())));
            }
            //【非排序线】获取产品加工规则对应的装配件信息 -同时- 关联不可用规则
            MesProdRuleContext prodRuleContext = prodRuleCfgExtService.getProdRuleNosortContext(
                    new MesProdRuleContext(reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), reqBean.getProcessCode(), productionProcessContext.getCraftCode())
                            .copy(filterList.get(0)).workOrderNo(productionPartContext.getWorkOrderNo()).foreignKey(productionPartContext.foreignKey(foreignKey += 1).getForeignKey()),
                    prodRuleIgnoreCfgMap);
            //prodRuleContext.setIsPrint(cutSchemeEquipment.getIsPrint());
            prodRuleContextList.add(prodRuleContext);

            if (!CollectionUtils.isEmpty(productionPsInContextMapByWorkOrder) && productionPsInContextMapByWorkOrder.containsKey(productionPartContext.getWorkOrderNo()))
                productionPsInContextMapByWorkOrder.get(productionPartContext.getWorkOrderNo()).foreignKey(productionPartContext.getForeignKey());

            log.info("工厂{}生产线{}工位{}:FSM STATE DISPATCHER --- DO STEP --- {} EXEC --- 设备:{} 产出零件信息:{} 产品加工规则:{} ---", reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(),
                    StringUtil.toLowerCaseFirst(this.getClass().getSimpleName()), cellEquipContext.getEquipmentCode(), JSONObject.toJSONString(productionPartContext), JSONObject.toJSONString(prodRuleContext));

        }

        return stepResult;
    }

    //根据产出零件分组数据
    private Map<String, List<MesProdRuleNosortCfg>> groupProdRuleNosortCfgList(List<MesProdRuleNosortCfg> prodRuleNosortCfgList) {
        return CollectionUtils.isEmpty(prodRuleNosortCfgList) ? null :
                prodRuleNosortCfgList.stream().filter(o -> null != o).collect(Collectors.groupingBy(o -> o.getOutPartNo().toUpperCase()));
    }

}
