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

import cn.estsh.i3plus.ext.mes.pcn.api.busi.*;
import cn.estsh.i3plus.ext.mes.pcn.pojo.context.MesCellEquipContext;
import cn.estsh.i3plus.ext.mes.pcn.pojo.context.MesEquipLogDispatchContext;
import cn.estsh.i3plus.ext.mes.pcn.pojo.context.MesEquipVariableCfgCollectContext;
import cn.estsh.i3plus.ext.mes.pcn.pojo.context.MesProductionProcessContext;
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.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.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Description : 读取加工结果工步
 * @Author : wangjie
 **/
@Slf4j
@Service("mesProductResultReadStepService")
public class MesProductResultReadStepService extends BaseStepService {

    @Autowired
    private IMesProductionProcessContextStepService productionProcessContextStepService;

    @Autowired
    private IMesProductionDispatchContextStepService productionDispatchContextStepService;

    @Autowired
    private IMesProductionCustomContextStepService productionCustomContextStepService;

    @Autowired
    private IMesEquipmentLogExtService equipmentLogExtService;

    @Autowired
    private IMesEquipVariableCfgRuleMatchDispatchService equipVariableCfgRuleMatchService;

    @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) {

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

        return super.init(reqBean);

    }

    @Override
    public StepResult execute(StationRequestBean reqBean) {

        StepResult stepResult = StepResult.getSuccessComplete();

        StationResultBean resultBean = new StationResultBean();

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

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

        //当前工序已存在加工结果
        if (!StringUtils.isEmpty(productionDispatchContextStepService.getProductResultContext(reqBean))) return stepResult;

        //获取生产过程上下文对象有异常信息 抛出异常
        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_OK.getValue(), MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_NOK.getValue(),
                MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_SCRAP.getValue(), MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_SUSPICIOUS.getValue());

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

        //配置错误 抛出异常
        if (!productionProcessContextStepService.checkNecessaryEquipmentVariableCfgAndValue(productionProcessContext, cellEquipContext, equipmentVariableCfgList, MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_OK.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);
        }

        //匹配加工结果
        matchProductResult(reqBean, resultBean, stepParamMap, stepResult, productionProcessContext, cellEquipContext, equipLogDispatchContext, shotCounter(equipmentVariableList));

        //根据设备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()),
                String.format("读取到设备[%s]加工结果的详细JSON内容: %s", cellEquipContext.getEquipmentName(), JSONObject.toJSONString(equipLogDispatchContext)), MesPcnEnumUtil.STATION_BUSI_TYPE.RUNNING_INFO, MesPcnEnumUtil.STATION_DATA_TYPE.TEXT);

        return stepResult;

    }

    //匹配加工结果
    private StepResult matchProductResult(StationRequestBean reqBean, StationResultBean resultBean, Optional<Map<String, MesProdRouteOptParam>> stepParamMap, StepResult stepResult,
                                    MesProductionProcessContext productionProcessContext, MesCellEquipContext cellEquipContext, MesEquipLogDispatchContext equipLogDispatchContext, Boolean shotCounter) {

        //未采集到数据
        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);

        //根据变量类别分别匹配加工结果
        matchVariableCategoryMap(reqBean, resultBean, stepParamMap, stepResult, productionProcessContext, cellEquipContext, equipLogDispatchContext, shotCounter);

        //保存设备当前一轮工序的加工结果
        if (!StringUtils.isEmpty(stepResult.getObj())) productionDispatchContextStepService.dispatchProductResultContext(reqBean, MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.valueOfCodeStr((String) stepResult.getObj()));

        return stepResult;

    }

    //加工结果规则匹配:根据变量类别分组, 只要匹配一组即可;  在一组中根据规则类别分组, 相同规则类别的一组必须全部匹配, 任意一组规则类别匹配成功即可
    private StepResult matchVariableCategoryMap(StationRequestBean reqBean, StationResultBean resultBean, Optional<Map<String, MesProdRouteOptParam>> stepParamMap, StepResult stepResult,
                                                MesProductionProcessContext productionProcessContext, MesCellEquipContext cellEquipContext, MesEquipLogDispatchContext equipLogDispatchContext, Boolean shotCounter) {

        //根据变量类别分组数据
        Map<String, List<MesEquipVariableCfgCollectContext>> variableCategoryMap = equipLogDispatchContext.getEquipVariableCfgCollectContextList().stream().filter(o -> null != o).collect(Collectors.groupingBy(MesEquipVariableCfgCollectContext::getVariableCategory));

        Integer shotCounterCached = !shotCounter ? MesPcnExtConstWords.ZERO : productionCustomContextStepService.getShortCounter(reqBean);
        Boolean checkShotCounter = true;

        List<MesEquipVariableCfgCollectContext> shotCounterList = null;

        for (Map.Entry<String, List<MesEquipVariableCfgCollectContext>> entry : variableCategoryMap.entrySet()) {

            if (null == entry) continue;

            List<MesEquipVariableCfgCollectContext> equipVariableCfgCollectContextList = !shotCounter ? entry.getValue() : entry.getValue().stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getCategoryLevelTwo()) && !o.getCategoryLevelTwo().equals(MesPcnExtConstWords.SHOT_COUNTER))).collect(Collectors.toList());

            //存在设备计数则直接判断,且只需要判断一次 [设备计数]
            if (CollectionUtils.isEmpty(shotCounterList) && shotCounter) {
                shotCounterList = entry.getValue().stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getCategoryLevelTwo()) && o.getCategoryLevelTwo().equals(MesPcnExtConstWords.SHOT_COUNTER))).collect(Collectors.toList());
                if (!CollectionUtils.isEmpty(shotCounterList)) checkShotCounter = checkShotCounter(reqBean, shotCounterList, shotCounterCached);
            }

            //当存在采集数据或者 不存在采集数据但非加工成功类型
            if (!CollectionUtils.isEmpty(equipVariableCfgCollectContextList) || !MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_OK.getValue().equals(entry.getKey())) {
                //设备数据变量接口逻辑根据变量类别规则匹配
                if (!(Boolean) equipVariableCfgRuleMatchService.matchEquipVariableCfgCollectContext(reqBean, productionProcessContext, entry.getKey(), equipVariableCfgCollectContextList)) continue;
            }

            //非设备加工失败情况下: 判断设备计数
            if (!MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_NOK.getValue().equals(entry.getKey()) && !checkShotCounter) continue;

            //发送工步内容
            productionCustomContextStepService.sendStepContextMessage(reqBean, MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.valueOfDescription(entry.getKey()), MesExtEnumUtil.CELL_MESSAGE_SOURCE.READ);

            //设备加工失败
            if (MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_NOK.getValue().equals(entry.getKey())) {
                this.sendMessage(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.READ.getValue()), String.format("%s!", MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_NOK.getDescription()), MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                threadSleep(stepParamMap, MesPcnExtConstWords.READ_FAILURE_SLEEP, MesPcnExtConstWords.READ_FAILURE_SLEEP_DEFAULT_TIME);
                return stepResult.isCompleted(false).obj(entry.getKey());
            }

            //设备加工成功/零件报废/设备加工可疑 均返回true;   零件报废/设备加工可疑标记错误信息
            return stepDynamicsCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.READ.getValue()), stepResult.obj(entry.getKey()), true, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE,
                    MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.PRODUCT_OK.getValue().equals(entry.getKey()) ? MesPcnEnumUtil.STATION_DATA_TYPE.TEXT : MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT,
                    String.format("%s!", MesExtEnumUtil.EQUIP_VARIABLE_CFG_CATEGORY.valueOfDescription(entry.getKey())));
        }

        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);

        return stepResult.isCompleted(false);

    }

    //判断是否存在设备计数
    private Boolean shotCounter(List<MesEquipmentVariable> equipmentVariableList) {
        Optional<MesEquipmentVariable> optional = CollectionUtils.isEmpty(equipmentVariableList) ? null :
                equipmentVariableList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getCategoryLevelTwo()) && o.getCategoryLevelTwo().equals(MesPcnExtConstWords.SHOT_COUNTER))).findFirst();
        return (null != optional && optional.isPresent()) ? true : false;
    }

    //验证shotCounter, 大于缓存的计数则默认加工成功, 与缓存的计数不一致则更新缓存
    private Boolean checkShotCounter(StationRequestBean reqBean, List<MesEquipVariableCfgCollectContext> shotCounterList, Integer shotCounterCached) {
        if (CollectionUtils.isEmpty(shotCounterList)) return false;
        if (CollectionUtils.isEmpty(shotCounterList.get(0).getEquipVariableCollectContextList())) return false;
        Integer equipVariableValue = StringUtils.isEmpty(shotCounterList.get(0).getEquipVariableCollectContextList().get(0).getEquipVariableValue()) ? MesPcnExtConstWords.ZERO : Integer.valueOf(shotCounterList.get(0).getEquipVariableCollectContextList().get(0).getEquipVariableValue());
        if (shotCounterCached.compareTo(equipVariableValue) != 0) {
            productionCustomContextStepService.dispatchShortCounter(reqBean, equipVariableValue);
            log.info("工厂{}生产线{}工位{}:FSM STATE DISPATCHER --- DO STEP --- {} EXEC --- shotCounterCached:{} --- UPDATE:{}", reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(), StringUtil.toLowerCaseFirst(this.getClass().getSimpleName()), shotCounterCached, equipVariableValue);
        }
        if (shotCounterCached.compareTo(equipVariableValue) >= 0) return false;
        return true;
    }

}
