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

import cn.estsh.i3plus.ext.mes.pcn.api.base.IMesMouldMultiCavityService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.*;
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.bean.MesEquipmentVariable;
import cn.estsh.i3plus.pojo.mes.bean.MesEquipmentVariableCfg;
import cn.estsh.i3plus.pojo.mes.bean.MesProdRouteOptParam;
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.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Description : 读主条码工步
 * @Author : wangjie
 **/
@Slf4j
@Service("mesProductSnReadStepService")
public class MesProductSnReadStepService extends BaseStepService {

    @Autowired
    private IFsmCommonService fsmCommonService;

    @Autowired
    private IMesProductionProcessContextStepService productionProcessContextStepService;

    @Autowired
    private IMesProductionDispatchContextStepService productionDispatchContextStepService;

    @Autowired
    private IMesProductionCustomContextStepService productionCustomContextStepService;

    @Autowired
    private IMesEquipmentLogExtService equipmentLogExtService;

    @Autowired
    private IMesEquipVariableCfgRuleMatchDispatchService equipVariableCfgRuleMatchService;

    @Autowired
    private IMesMouldMultiCavityService mouldMultiCavityService;

    @Override
    public void title(StationRequestBean reqBean) {
        this.sendMessage(reqBean, new StationResultBean().resultObj(MesPcnExtConstWords.STEP_DISABLE_SCAN), String.format("工步: %s", reqBean.getStepName()), MesPcnEnumUtil.STATION_BUSI_TYPE.STEP_TITLE, MesPcnEnumUtil.STATION_DATA_TYPE.TITLE);
    }

    @Override
    public StepResult init(StationRequestBean reqBean) {

        StepResult stepResult = StepResult.getSuccessComplete();

        String endlessLoopReadTimes = fsmCommonService.handleFsmWcpcMapDataForDoScanThenBackValue(reqBean, MesPcnExtConstWords.ENDLESS_LOOP_READ_TIMES);
        if (StringUtils.isEmpty(endlessLoopReadTimes)) endlessLoopReadTimes = MesPcnExtConstWords.ENDLESS_LOOP_READ_TIMES_DEFAULT;
        if (productionDispatchContextStepService.dispatchOverEndlessLoopReadTimes(reqBean, endlessLoopReadTimes)) {
            stepThreadSleepAndSendTaskCompleteAndThrowEx(reqBean, new StationResultBean().isWs(false).writeDbLog().checkRepeat(),
                    stepResult.isCompleted(false).msg(String.format("当前读取到设备的主条码匹配失败超过[%s]次!", endlessLoopReadTimes)),
                    MesPcnEnumUtil.STATION_BUSI_TYPE.RUNNING_INFO, MesPcnEnumUtil.STATION_DATA_TYPE.TEXT, getStepParams(reqBean), MesPcnExtConstWords.READ_FAILURE_SLEEP, MesPcnExtConstWords.READ_FAILURE_SLEEP_DEFAULT_TIME);
        }

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

        return stepResult;

    }

    @Override
    public StepResult execute(StationRequestBean reqBean) {

        StationResultBean resultBean = new StationResultBean();

        StepResult stepResult = StepResult.getSuccessComplete();

        //获取工步参数
        Optional<Map<String, MesProdRouteOptParam>> stepParamMap = getStepParams(reqBean);

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

        //当前工序已存在读取待验证的主条码信息
        if (productionDispatchContextStepService.checkScanProductSnIsExistContext(reqBean)) return stepResult;

        //TODO 后面增加工艺强过码 通过按钮触发 判断是否存在已验证主条码 且 存在工艺验证错误的数据 进行标记后放行

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

        //从上下文的设备数据变量接口逻辑对象集合中取出当前设备信息的逻辑类型对应的接口逻辑对象集合
        List<MesEquipmentVariableCfg> equipmentVariableCfgList = productionProcessContext.getEquipVariableCfgListByVct();

        //根据变量类别[主条码]搜集设备数据变量接口逻辑信息
        equipmentVariableCfgList = productionProcessContextStepService.collectEquipmentVariableCfgList(equipmentVariableCfgList, MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_SN.getValue());

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

        //配置错误 抛出异常
        if (!productionProcessContextStepService.checkNecessaryEquipmentVariableCfg(productionProcessContext, cellEquipContext, equipmentVariableCfgList, MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_SN.getValue()).getSuccess())
            stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), productionProcessContext.getMessage());

        //搜集设备数据变量接口逻辑信息中的二级变量
        List<String> categoryLevelTwoList = productionProcessContextStepService.collectCategoryLevelTwoList(equipmentVariableCfgList);

        //根据变量类型与二级变量获取设备数据变量信息
        List<MesEquipmentVariable> equipmentVariableList = productionProcessContextStepService.findEquipmentVariableList(productionProcessContext, MesExtEnumUtil.EQUIP_VARIABLE_TYPE.PRODUCTION.getValue(), categoryLevelTwoList);

        //配置错误 抛出异常
        if (!productionProcessContextStepService.checkIsEmptyEquipmentVariableList(productionProcessContext, cellEquipContext, equipmentVariableList, MesExtEnumUtil.EQUIP_VARIABLE_TYPE.PRODUCTION, categoryLevelTwoList).getSuccess())
            stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), productionProcessContext.getMessage());

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

        //获取设备LOG采集数据
        MesEquipLogDispatchContext equipLogDispatchContext = equipmentLogExtService.getEquipLogDispatchContext(cellEquipContext, equipmentVariableList, equipmentVariableCfgList);

        //验证设备通信质量
        productionCustomContextStepService.sendEquipQualityMessage(reqBean, cellEquipContext);
        if (!equipmentLogExtService.checkEquipQuality(cellEquipContext.getQuality())) {
            this.sendMessage(reqBean, new StationResultBean().writeDbLog().checkRepeat(), String.format("设备网络异常!请检查设备的质量状态,读取到设备[%s]的数据变量质量值为[%s],持续监听中...",
                    cellEquipContext.getEquipmentName(), cellEquipContext.getQuality()), MesPcnEnumUtil.STATION_BUSI_TYPE.RUNNING_INFO, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
            stepThreadSleepAndSendTaskCompleteAndThrowEx(reqBean, resultBean,
                    stepResult.isCompleted(false).msg(String.format("设备[%s]网络异常! 持续监听中...", cellEquipContext.getEquipmentName())),
                    MesPcnEnumUtil.STATION_BUSI_TYPE.GUIDE, MesPcnEnumUtil.STATION_DATA_TYPE.TEXT, stepParamMap, MesPcnExtConstWords.READ_FAILURE_SLEEP, MesPcnExtConstWords.READ_FAILURE_SLEEP_DEFAULT_TIME);
        }

        //匹配读取的主条码的有效性
        return doMatchProductSnValid(reqBean, resultBean, stepParamMap, stepResult, productionProcessContext, cellEquipContext, equipLogDispatchContext);

    }

    //匹配读取的主条码的有效性
    private StepResult doMatchProductSnValid(StationRequestBean reqBean, StationResultBean resultBean, Optional<Map<String, MesProdRouteOptParam>> stepParamMap, StepResult stepResult, MesProductionProcessContext productionProcessContext, MesCellEquipContext cellEquipContext, MesEquipLogDispatchContext equipLogDispatchContext) {

        //未采集到数据
        if (!equipLogDispatchContext.getIsCollectValue())
            stepThreadSleepAndSendTaskCompleteAndThrowEx(reqBean, resultBean.writeDbLog().checkRepeat(), stepResult.isCompleted(false).msg(String.format("当前未读取到设备[%s]主条码,持续监听中...", cellEquipContext.getEquipmentName())),
                    MesPcnEnumUtil.STATION_BUSI_TYPE.GUIDE, MesPcnEnumUtil.STATION_DATA_TYPE.TEXT, stepParamMap, MesPcnExtConstWords.READ_FAILURE_SLEEP, MesPcnExtConstWords.READ_FAILURE_SLEEP_DEFAULT_TIME);

        //读取的主条码进行规则匹配
        List<MesEquipVariableCollectContext> equipVariableCollectContextList = (List<MesEquipVariableCollectContext>) equipVariableCfgRuleMatchService.matchEquipVariableCfgCollectContext(reqBean, productionProcessContext, MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_SN.getValue(), equipLogDispatchContext.getEquipVariableCfgCollectContextList());

        //没有有效的数据
        if (CollectionUtils.isEmpty(equipVariableCollectContextList)) {
            this.sendMessage(reqBean, new StationResultBean().writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.READ.getValue()).checkRepeat(),
                    String.format("当前未读取到设备[%s]有效的主条码,持续监听中...%s", cellEquipContext.getEquipmentName(), JSONObject.toJSONString(equipLogDispatchContext)), MesPcnEnumUtil.STATION_BUSI_TYPE.RUNNING_INFO, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
            stepThreadSleepAndSendTaskCompleteAndThrowEx(reqBean, resultBean,
                    stepResult.isCompleted(false).msg(String.format("当前未读取到设备[%s]有效的主条码,持续监听中...", cellEquipContext.getEquipmentName())),
                    MesPcnEnumUtil.STATION_BUSI_TYPE.GUIDE, MesPcnEnumUtil.STATION_DATA_TYPE.TEXT, stepParamMap, MesPcnExtConstWords.READ_FAILURE_SLEEP, MesPcnExtConstWords.READ_FAILURE_SLEEP_DEFAULT_TIME);
        }

        //根据设备ID,设备数据变量ID集合 修改设备ID分表采集数据的状态
        equipmentLogExtService.updateEquipmentLogList(reqBean.getOrganizeCode(), cellEquipContext.getEquipId(), equipLogDispatchContext.getResetEquipmentLogIdMap());

        this.sendMessage(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.READ.getValue()).checkRepeat(),
                String.format("读取到设备[%s]主条码的详细JSON内容: %s", cellEquipContext.getEquipmentName(), JSONObject.toJSONString(equipLogDispatchContext)), MesPcnEnumUtil.STATION_BUSI_TYPE.RUNNING_INFO, MesPcnEnumUtil.STATION_DATA_TYPE.TEXT);

        //读取主条码前置验证
        checkReadProductSnValid(reqBean, resultBean, stepParamMap, stepResult, productionProcessContext, cellEquipContext, equipVariableCollectContextList);

        //发送工步内容
        String scanInfo = equipVariableCollectContextList.stream().filter(o -> null != o).map(MesEquipVariableCollectContext::getEquipVariableValue).collect(Collectors.joining(MesPcnExtConstWords.SEMICOLON));
        productionCustomContextStepService.sendStepContextMessage(reqBean, scanInfo, MesExtEnumUtil.CELL_MESSAGE_SOURCE.READ);

        return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.READ.getValue()).checkRepeat().scanInfo(scanInfo), stepResult, String.format("当前已读取到设备[%s]主条码信息[%s]!", cellEquipContext.getEquipmentName(), scanInfo));

    }

    //读取主条码前置验证
    private void checkReadProductSnValid(StationRequestBean reqBean, StationResultBean resultBean, Optional<Map<String, MesProdRouteOptParam>> stepParamMap, StepResult stepResult, MesProductionProcessContext productionProcessContext, MesCellEquipContext cellEquipContext, List<MesEquipVariableCollectContext> equipVariableCollectContextList) {

        //搜集主条码值
        List<MesEquipVariableCollectContext> productSnList = filterEquipVariableCollectContextList(productionProcessContext, equipVariableCollectContextList, true);
        //搜集空腔条码
        List<MesEquipVariableCollectContext> finishCodeList = filterEquipVariableCollectContextList(productionProcessContext, equipVariableCollectContextList, false);

        //全部匹配为空腔
        if (CollectionUtils.isEmpty(productSnList))
            stepThreadSleepAndSendTaskCompleteAndThrowEx(reqBean, resultBean.writeDbLog(), stepResult.isCompleted(false).msg(String.format("当前读取到设备[%s]主条码全部为空腔,持续监听中...", cellEquipContext.getEquipmentName())),
                    MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT, stepParamMap, MesPcnExtConstWords.READ_FAILURE_SLEEP, MesPcnExtConstWords.READ_FAILURE_SLEEP_DEFAULT_TIME);

        //根据读取条码分组数据
        Map<String, List<MesEquipVariableCollectContext>> produceSnCollectMap = productSnList.stream().filter(o -> null != o).collect(Collectors.groupingBy(MesEquipVariableCollectContext::getEquipVariableValue));

        //验证是否存在重复条码
        if (!CollectionUtils.isEmpty(productSnList) && productSnList.size() != produceSnCollectMap.size())
            stepThreadSleepAndSendTaskCompleteAndThrowEx(reqBean, resultBean.writeDbLog(), stepResult.isCompleted(false).msg(String.format("当前读取到设备[%s]主条码存在重复条码,持续监听中...", cellEquipContext.getEquipmentName())),
                    MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT, stepParamMap, MesPcnExtConstWords.READ_FAILURE_SLEEP, MesPcnExtConstWords.READ_FAILURE_SLEEP_DEFAULT_TIME);

        //验证条码数量是否满足腔数
        List<MesProductionPartContext> productionPartContextList = productionDispatchContextStepService.getProductionPartContext(reqBean);
        if (!CollectionUtils.isEmpty(productionPartContextList) && equipVariableCollectContextList.size() != productionPartContextList.size())
            stepThreadSleepAndSendTaskCompleteAndThrowEx(reqBean, resultBean.writeDbLog(), stepResult.isCompleted(false).msg(String.format("当前读取到设备[%s]主条码数量[%s]与产出零件对应的腔数[%s]不一致,持续监听中...", cellEquipContext.getEquipmentName(), equipVariableCollectContextList.size(), productionPartContextList.size())),
                    MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT, stepParamMap, MesPcnExtConstWords.READ_FAILURE_SLEEP, MesPcnExtConstWords.READ_FAILURE_SLEEP_DEFAULT_TIME);

        if (CollectionUtils.isEmpty(productionPartContextList) && equipVariableCollectContextList.size() != cellEquipContext.getCavity())
            stepThreadSleepAndSendTaskCompleteAndThrowEx(reqBean, resultBean.writeDbLog(), stepResult.isCompleted(false).msg(String.format("当前读取到设备[%s]主条码数量[%s]与工位设备维护的腔数[%s]不一致,持续监听中...", cellEquipContext.getEquipmentName(), equipVariableCollectContextList.size(), cellEquipContext.getCavity())),
                    MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT, stepParamMap, MesPcnExtConstWords.READ_FAILURE_SLEEP, MesPcnExtConstWords.READ_FAILURE_SLEEP_DEFAULT_TIME);

        //比对产出零件的空腔数量与主条码的空腔数量,  产出零件的空腔数必须小于主条码的空腔数
        if (!checkFinishCodeSizeValid(stepResult, cellEquipContext, productionPartContextList, finishCodeList).isCompleted())
            stepThreadSleepAndSendTaskCompleteAndThrowEx(reqBean, resultBean.writeDbLog(), stepResult.isCompleted(false).msg(stepResult.getMsg()), MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT, stepParamMap, MesPcnExtConstWords.READ_FAILURE_SLEEP, MesPcnExtConstWords.READ_FAILURE_SLEEP_DEFAULT_TIME);

        //保存设备当前一轮工序的待验证的主条码信息
        productionDispatchContextStepService.dispatchScanProductSnContext(reqBean, productSnList);

        //保存空腔数据, 没有空腔默认清除历史主条码
        if (CollectionUtils.isEmpty(finishCodeList)) productionDispatchContextStepService.removeProductionPsInContext(reqBean);
        else productionDispatchContextStepService.dispatchProductionPsInContext(reqBean, getProductionPsInContextList(finishCodeList));

    }

    //比对产出零件的空腔数量与主条码的空腔数量,  产出零件的空腔数必须小于主条码的空腔数
    private StepResult checkFinishCodeSizeValid(StepResult stepResult, MesCellEquipContext cellEquipContext, List<MesProductionPartContext> productionPartContextList, List<MesEquipVariableCollectContext> finishCodeList) {

        //没有产出零件 不需要对比空腔数量
        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());

        Integer ppSize = CollectionUtils.isEmpty(finishCodePpList) ? MesPcnExtConstWords.ZERO : finishCodePpList.size();
        Integer psSize = CollectionUtils.isEmpty(finishCodeList) ? MesPcnExtConstWords.ZERO : finishCodeList.size();

        return ppSize.compareTo(psSize) <= 0 ? stepResult : stepResult.isCompleted(false).msg(String.format("当前读取到设备[%s]主条码空腔数量[%s]小于产出零件对应的空腔数量[%s],持续监听中...", cellEquipContext.getEquipmentName(), psSize, ppSize));

    }

    //封装无需验证的finishCode空腔数据
    private List<MesProductionPsInContext> getProductionPsInContextList(List<MesEquipVariableCollectContext> finishCodeList) {
        List<MesProductionPsInContext> productionPsInContextList = new ArrayList<>();
        finishCodeList.stream().filter(o -> null != o).forEach(o -> productionPsInContextList.add(new MesProductionPsInContext().isFinishCode()));
        return productionPsInContextList;
    }

    //搜集主条码值与空腔值
    private List<MesEquipVariableCollectContext> filterEquipVariableCollectContextList(MesProductionProcessContext productionProcessContext, List<MesEquipVariableCollectContext> equipVariableCollectContextList, Boolean flag) {
        return flag ? equipVariableCollectContextList.stream().filter(o -> (null != o && !o.getEquipVariableValue().equals(productionProcessContext.getFinishCode()))).collect(Collectors.toList()) :
                equipVariableCollectContextList.stream().filter(o -> (null != o && o.getEquipVariableValue().equals(productionProcessContext.getFinishCode()))).collect(Collectors.toList());
    }


}
