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

import cn.estsh.i3plus.ext.mes.pcn.api.base.IMesEquipmentExtService;
import cn.estsh.i3plus.ext.mes.pcn.api.base.IMesEquipmentProdParamCfgService;
import cn.estsh.i3plus.ext.mes.pcn.api.base.IMesProdShiftRecordService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.IMesEquipVariableRwExtService;
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.serviceimpl.fsm.BaseStepService;
import cn.estsh.i3plus.platform.common.tool.TimeTool;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
import cn.estsh.i3plus.pojo.mes.bean.MesEquipmentProdParamCfg;
import cn.estsh.i3plus.pojo.mes.bean.MesEquipmentVariable;
import cn.estsh.i3plus.pojo.mes.bean.MesWorkCenter;
import cn.estsh.i3plus.pojo.mes.bean.shipping.MesShippingQueue;
import cn.estsh.i3plus.pojo.mes.model.MesEquipVariableRwResult;
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.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.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description : 发送加工参数
 * @Author : wangjie
 **/
@Slf4j
@Service("mesSendEquipParamsCmdStepService")
public class MesSendEquipParamsCmdStepService extends BaseStepService {

    @Autowired
    private IMesProductionProcessContextStepService productionProcessContextStepService;

    @Autowired
    private IMesProductionDispatchContextStepService productionDispatchContextStepService;

    @Autowired
    private IMesEquipmentProdParamCfgService equipmentProdParamCfgService;

    @Autowired
    private IMesEquipVariableRwExtService equipVariableRwExtService;

    @Autowired
    private IMesEquipmentExtService equipmentExtService;

    @Autowired
    private IMesProdShiftRecordService prodShiftRecordService;

    @Override
    public StepResult execute(StationRequestBean reqBean) {

        StepResult stepResult = StepResult.getSuccessComplete();

        StationResultBean resultBean = new StationResultBean();

        //获取上下文加工规则数据信息集合
        List<MesProdRuleContext> prodRuleContextList = productionDispatchContextStepService.getProdRuleDataContext(reqBean);
        if (CollectionUtils.isEmpty(prodRuleContextList)) return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, "发送设备加工参数:当前没有加工规则数据,跳过发送!");

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

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

        //获取生产线信息
        MesWorkCenter workCenter = productionProcessContext.getWorkCenter();

        //搜集数据:根据数据关联键分组加工参数关联ID集合
        Map<Integer, List<Long>> sourceIdMap2ForeignKey = new HashMap<>();

        //搜集加工参数关联ID集合 根据生产线类型区分
        List<Long> sourceIdList = getEquipmentProdParamCfgSourceIdList(productionProcessContext, prodRuleContextList, workCenter, sourceIdMap2ForeignKey);

        //根据来源ID查询设备加工参数信息
        List<MesEquipmentProdParamCfg> equipmentProdParamCfgList = equipmentProdParamCfgService.getEquipmentProdParamCfgList(reqBean.getOrganizeCode(), sourceIdList);

        if (CollectionUtils.isEmpty(equipmentProdParamCfgList)) return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, "发送设备加工参数:当前加工规则未匹配到设备加工参数相关配置,跳过发送!");

        //设备加工参数信息集合根据来源ID分组
        Map<Long, List<MesEquipmentProdParamCfg>> eppcMap2SourceId = equipmentProdParamCfgList.stream().filter(o -> null != o).collect(Collectors.groupingBy(MesEquipmentProdParamCfg::getSourceId));

        //设备加工参数信息集合根据数据变量ID分组
        Map<Long, List<MesEquipmentProdParamCfg>> eppcMap2EquipVariableId = equipmentProdParamCfgList.stream().filter(o -> null != o).collect(Collectors.groupingBy(MesEquipmentProdParamCfg::getEquipVariableId));

        //根据设备数据变量ID集合查询设备数据变量信息
        List<MesEquipmentVariable> equipmentVariableList = equipmentExtService.getEquipmentVariableList(reqBean.getOrganizeCode(), new ArrayList<>(eppcMap2EquipVariableId.keySet()));

        //设备加工参数信息集合根据数据变量ID分组
        Map<Long, MesEquipmentVariable> evMap = CollectionUtils.isEmpty(equipmentVariableList) ? null : equipmentVariableList.stream().filter(o -> null != o).collect(Collectors.toMap(MesEquipmentVariable::getId, o -> o));

        if (CollectionUtils.isEmpty(evMap)) return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(), stepResult, "发送设备加工参数:当前加工规则未匹配到设备变量数据信息,跳过发送!");

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

        //获取上下文中的业务数据
        String productResult = productionDispatchContextStepService.getProductResultContext(reqBean);
        List<MesProductionPartContext> productionPartContextList = productionDispatchContextStepService.getProductionPartContext(reqBean);
        List<MesProductionPsInContext> productionPsInContexts = productionDispatchContextStepService.getProductionPsInContext(reqBean);
        List<MesProductionPsOutContext> productionPsOutContexts = productionDispatchContextStepService.getProductionPsOutContext(reqBean);
        MesProdShiftContext mesProdShiftKvBean = prodShiftRecordService.getMesProdShiftKvBean(reqBean.getOrganizeCode(), reqBean.getWorkCenterCode());

        //发送设备加工参数
        prodRuleContextList.stream().filter(o -> null != o).forEach(o -> {

            Optional<MesProductionPartContext> productionPartContextOp = CollectionUtils.isEmpty(productionPartContextList) ? null :
                    productionPartContextList.stream().filter(i -> (null != i && !StringUtils.isEmpty(i.getForeignKey()) && i.getForeignKey().compareTo(o.getForeignKey()) == 0)).findFirst();

            Optional<MesProductionPsInContext> productionPsInContextOp = CollectionUtils.isEmpty(productionPsInContexts) ? null :
                    productionPsInContexts.stream().filter(i -> (null != i && !StringUtils.isEmpty(i.getForeignKey()) && i.getForeignKey().compareTo(o.getForeignKey()) == 0)).findFirst();

            Optional<MesProductionPsOutContext> productionPsOutContextOp = CollectionUtils.isEmpty(productionPsOutContexts) ? null :
                    productionPsOutContexts.stream().filter(i -> (null != i && !StringUtils.isEmpty(i.getForeignKey()) && i.getForeignKey().compareTo(o.getForeignKey()) == 0)).findFirst();


            doSendEquipParamsCmd(reqBean, resultBean, o, sourceIdMap2ForeignKey.get(o.getForeignKey()), cellEquipContext, eppcMap2SourceId, evMap, productResult, productionPartContextOp, productionPsInContextOp, productionPsOutContextOp);

        });

        return stepResult;
    }

    //搜集加工参数关联ID集合
    private List<Long> getEquipmentProdParamCfgSourceIdList(MesProductionProcessContext productionProcessContext, List<MesProdRuleContext> prodRuleContextList, MesWorkCenter workCenter, Map<Integer, List<Long>> sourceIdMap2ForeignKey) {
        if (MesExtEnumUtil.WORK_CENTER_TYPE.NOSORT.getValue() == workCenter.getCenterType()) {
            //非排序
            List<Long> sourceIdList = new ArrayList<>();
            prodRuleContextList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getPid()))).forEach(o -> {
                sourceIdMap2ForeignKey.put(o.getForeignKey(), Stream.of(o.getPid()).collect(Collectors.toList()));
                sourceIdList.add(o.getPid());
            });
            return sourceIdList;
        } else {
            //排序
            List<Long> sourceIdList = null;
            for (MesProdRuleContext prodRuleContext : prodRuleContextList) {
                if (StringUtils.isEmpty(prodRuleContext.getAssemblyDataJson())) continue;
                List<Long> itemIdList = prodRuleContext.getSortAssemblyDataContext().stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getPid()))).map(MesProductionAssemblySortContext::getPid).collect(Collectors.toList());
                if (CollectionUtils.isEmpty(itemIdList)) continue;
                if (CollectionUtils.isEmpty(sourceIdList)) sourceIdList = new ArrayList<>();
                sourceIdMap2ForeignKey.put(prodRuleContext.getForeignKey(), itemIdList);
                sourceIdList.addAll(itemIdList);
            }
            return sourceIdList;
        }
    }

    //发送设备加工参数
    private void doSendEquipParamsCmd(StationRequestBean reqBean, StationResultBean resultBean, MesProdRuleContext prodRuleContext, List<Long> sourceIdList, MesCellEquipContext cellEquipContext,
                                      Map<Long, List<MesEquipmentProdParamCfg>> eppcMap2SourceId, Map<Long, MesEquipmentVariable> evMap, String productResult,
                                      Optional<MesProductionPartContext> productionPartContextOp, Optional<MesProductionPsInContext> productionPsInContextOp, Optional<MesProductionPsOutContext> productionPsOutContextOp) {

        if (CollectionUtils.isEmpty(sourceIdList)) return;

        for (Long sourceId : sourceIdList) {

            if (StringUtils.isEmpty(sourceId)) continue;

            //当前加工规则的PID对应的设备加工参数
            List<MesEquipmentProdParamCfg> equipmentProdParamCfgList = eppcMap2SourceId.get(sourceId);

            if (CollectionUtils.isEmpty(equipmentProdParamCfgList)) continue;

            MesProductionPartContext productionPartContext = (null != productionPartContextOp && productionPartContextOp.isPresent()) ? productionPartContextOp.get() : null;

            MesProductionPsInContext productionPsInContext = (null != productionPsInContextOp && productionPsInContextOp.isPresent()) ? productionPsInContextOp.get() : null;

            MesProductionPsOutContext productionPsOutContext = (null != productionPsOutContextOp && productionPsOutContextOp.isPresent()) ? productionPsOutContextOp.get() : null;

            //搜集发送规则并去重
            List<String> matchRuleList = (equipmentProdParamCfgList.stream().filter(o -> null != o).map(o -> checkTransformUpperCase(o.getMatchRule())).collect(Collectors.toList())).stream().filter(o -> !StringUtils.isEmpty(o)).distinct().collect(Collectors.toList());

            for (String matchRule : matchRuleList) {

                if (StringUtils.isEmpty(matchRule)) continue;

                Optional<MesEquipmentProdParamCfg> equipmentProdParamCfgOp = equipmentProdParamCfgList.stream().filter(o -> (null != o && checkTransformUpperCase(o.getMatchRule()).equals(matchRule) && evMap.containsKey(o.getEquipVariableId()))).findFirst();

                if (null == equipmentProdParamCfgOp || !equipmentProdParamCfgOp.isPresent()) continue;

                evMap.remove(equipmentProdParamCfgOp.get().getId());

                //数据转换
                String matchValue = transferValue(matchRule, reqBean, prodRuleContext, productResult, productionPartContext, productionPsInContext, productionPsOutContext);

                if (StringUtils.isEmpty(matchValue)) {
                    this.sendMessage(reqBean, resultBean.writeDbLog(), String.format("发送设备加工参数:[%s]未匹配到业务数据", matchRule), MesPcnEnumUtil.STATION_BUSI_TYPE.RUNNING_INFO, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }

                MesEquipmentVariable equipmentVariable = evMap.get(equipmentProdParamCfgOp.get().getEquipVariableId());
                if (null == equipmentVariable) {
                    this.sendMessage(reqBean, resultBean.writeDbLog(), String.format("发送设备加工参数:设备加工参数信息ID[%s]关联的设备数据变量ID[%s]未维护有效的设备数据变量信息!", equipmentProdParamCfgOp.get().getId(), equipmentProdParamCfgOp.get().getEquipVariableId()), MesPcnEnumUtil.STATION_BUSI_TYPE.RUNNING_INFO, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }

                //写值
                MesEquipVariableRwResult equipVariableRwResult = equipVariableRwExtService.writeVariable(matchValue, equipmentVariable, cellEquipContext.getKepwareFlag(equipmentVariable.getChannel()));

                if (!equipVariableRwResult.getIsSuccessed()) {
                    this.sendMessage(reqBean, new StationResultBean().writeDbLog().checkRepeat(), String.format("设备[%s]:发送设备加工参数失败!原因:%s", cellEquipContext.getEquipmentName(), equipVariableRwResult.getMessage()), MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
                    continue;
                }

                this.sendMessage(reqBean, new StationResultBean().writeDbLog(), String.format("设备[%s]:发送设备加工参数成功!%s", cellEquipContext.getEquipmentName(), equipVariableRwResult.getMessage()), MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.TEXT);

            }

        }

    }

    private String checkTransformUpperCase(String matchRule) {
        matchRule = matchRule.trim();
        return matchRule.contains(MesPcnExtConstWords.PERCENT) ? matchRule.toUpperCase() : matchRule;
    }

    //数据转换
    private String transferValue(String matchRule, StationRequestBean reqBean, MesProdRuleContext prodRuleContext, String productResult,
                                 MesProductionPartContext productionPartContext, MesProductionPsInContext productionPsInContext, MesProductionPsOutContext productionPsOutContext) {
        switch (matchRule) {
            case "%RESULT%" :
                return MesExtEnumUtil.PRODUCT_RESULT_EQUIP_PARAM.codeOfValue(productResult);
            case "%PARAM%":
            case "%ORDERCODE%":
                return prodRuleContext.getWorkOrderNo();
            case "%CUSTPARTNO%":
                return null != productionPartContext ? productionPartContext.getCustPartNo() : null;
            case "%EMPLOYEENO%":
            case "%CREATEUSER%":
                return reqBean.getUserInfo();
            case "%BARCODE%":
                return null != productionPsOutContext ? productionPsOutContext.getProductSn() : null;
            case "%CREATEDATE%":
            case "%GETDATE%":
                return TimeTool.getNowTime(true);
            case "%SHIFT%":
                return "shift";
            case "%STATUS%":
                return null != productionPsInContext ? productionPsInContext.getSnStatus().toString() : null;
            case "%CUSTORDERCOD%":
                return null != productionPartContext ? productionPartContext.getCustOrderNo() : null;
            case "%PARTNO%":
                return null != productionPsOutContext ? productionPsOutContext.getPartNo() : (null != productionPartContext ? productionPartContext.getPartNo() : null);
            case "%VINCODE%":
                return null != productionPartContext ? productionPartContext.getVinCode() : null;
            case "%SEQUENCE%":
                return null != productionPartContext ? productionPartContext.getWorkOrderSeq() : null;
            case "%CUSTBARCODE%":
                return null != productionPsOutContext ? productionPsOutContext.getCustSn() : null;
            default:
                return matchRule;
        }

    }



}
