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

import cn.estsh.i3plus.ext.mes.pcn.api.base.IMesProdShiftRecordService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.IMesProduceSnExtService;
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.apiservice.serviceimpl.step.method.MesPartDataMapSaveStepService;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.step.method.MesWorkOrderCheckCompleteQtyStepService;
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.IFsmRouteDataService;
import cn.estsh.i3plus.mes.pcn.api.iservice.busi.ISyncFuncService;
import cn.estsh.i3plus.mes.pcn.serviceimpl.fsm.BaseStepService;
import cn.estsh.i3plus.mes.pcn.util.StringUtil;
import cn.estsh.i3plus.platform.common.convert.ConvertBean;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
import cn.estsh.i3plus.pojo.mes.bean.MesPart;
import cn.estsh.i3plus.pojo.mes.bean.MesProduceSn;
import cn.estsh.i3plus.pojo.mes.bean.MesWorkCell;
import cn.estsh.i3plus.pojo.mes.bean.MesWorkCenter;
import cn.estsh.i3plus.pojo.mes.model.GenSerialNoModel;
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.repository.MesNumberRuleRepository;
import cn.estsh.i3plus.pojo.mes.repository.MesProduceSnRepository;
import cn.estsh.i3plus.pojo.mes.util.MesExtEnumUtil;
import cn.estsh.impp.framework.boot.exception.ImppBusiException;
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.UUID;
import java.util.stream.Collectors;

/**
 * @Description : 生成零件条码工步
 * @Author : wangjie
 **/
@Slf4j
@Service("mesProductSnGenerateStepService")
public class MesProductSnGenerateStepService extends BaseStepService {

    @Autowired
    private IMesProductionProcessContextStepService productionProcessContextStepService;

    @Autowired
    private IMesProductionDispatchContextStepService productionDispatchContextStepService;

    @Autowired
    private MesPartDataMapSaveStepService partDataMapSaveStepService;

    @Autowired
    private ISyncFuncService syncFuncService;

    @Autowired
    private IMesProduceSnExtService produceSnExtService;

    @Autowired
    private MesProduceSnRepository produceSnRepository;

    @Autowired
    private MesNumberRuleRepository mesNumberRuleRao;

    @Autowired
    private IFsmRouteDataService fsmRouteDataService;

    @Autowired
    private IMesProdShiftRecordService prodShiftRecordService;

    @Autowired
    private MesWorkOrderCheckCompleteQtyStepService workOrderCheckCompleteQtyStepService;

    @Override
    public StepResult execute(StationRequestBean reqBean) {

        StationResultBean resultBean = new StationResultBean();

        StepResult stepResult = StepResult.getSuccessComplete();

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

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

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

        //从上下文中取出生产线对象
        MesWorkCenter workCenter = productionProcessContext.getWorkCenter();

        //排序线 直接跳过
        if (MesExtEnumUtil.WORK_CENTER_TYPE.SORT.getValue() == workCenter.getCenterType()) return stepResult;

        //已经生成过零件条码
        if (productionDispatchContextStepService.checkProductionPsOutIsExistContext(reqBean)) return stepResult;

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

        if (CollectionUtils.isEmpty(prodRuleContextList)) stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), "当前不存在加工规则信息,请重置工序解决!");

        //获取生产线的当前班组班次信息
        MesProdShiftContext prodShiftContext = prodShiftRecordService.getMesProdShiftKvBean(reqBean.getOrganizeCode(), reqBean.getWorkCenterCode());

        if (null == prodShiftContext) stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), "当前不存在生产开班记录,请重新开班!");

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

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

        //获取上下文零件数据信息
        List<String> partNoList = (prodRuleContextList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getOutPartNo()))).map(MesProdRuleContext::getOutPartNo).collect(Collectors.toList())).stream().filter(o -> !StringUtils.isEmpty(o)).distinct().collect(Collectors.toList());
        partDataMapSaveStepService.savePartDataMap(reqBean, resultBean, stepResult, partNoList, false, false);

        //从上下文中取出工位对象
        MesWorkCell workCell = productionProcessContext.getWorkCell();
        //获取上下文加工结果
        String productResult = productionDispatchContextStepService.getProductResultContext(reqBean);

        Map<Integer, MesProductionPartContext> ppMap = CollectionUtils.isEmpty(productionPartContextList) ? null : productionPartContextList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getForeignKey()))).collect(Collectors.toMap(MesProductionPartContext::getForeignKey, o -> o));
        Map<Integer, MesProductionPsInContext> psMap = CollectionUtils.isEmpty(productionPsInContextList) ? null : productionPsInContextList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getForeignKey()))).collect(Collectors.toMap(MesProductionPsInContext::getForeignKey, o -> o));
        Map<String, MesPart> partDataMap = (Map<String, MesPart>) stepResult.getObj();

        //循环生成零件条码，并写入产出零件
        List<MesProductionPsOutContext> productionPsOutContextList = new ArrayList<>();
        prodRuleContextList.stream().filter(o -> (null != o && !StringUtils.isEmpty(o.getForeignKey()) && !StringUtils.isEmpty(o.getOutPartNo()))).forEach(o -> {
            MesProductionPsOutContext productionPsOutContext = insertProduceSnData(reqBean, resultBean, stepResult, productionProcessContext, workCell, o, ppMap, psMap, partDataMap, prodShiftContext, productResult);
            if (null != productionPsOutContext) productionPsOutContextList.add(productionPsOutContext);
        });

        //保存上下文产出条码数据信息集合
        productionDispatchContextStepService.dispatchProductionPsOutContext(reqBean, productionPsOutContextList);

        return stepSuccessCompleteAndSendMsgReturn(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.PROCESS.getValue()), stepResult, "生成零件条码成功!");

    }

    //生成零件条码
    private MesProductionPsOutContext insertProduceSnData(StationRequestBean reqBean, StationResultBean resultBean, StepResult stepResult, MesProductionProcessContext productionProcessContext, MesWorkCell workCell, MesProdRuleContext prodRuleContext,
                                                          Map<Integer, MesProductionPartContext> ppMap, Map<Integer, MesProductionPsInContext> psMap, Map<String, MesPart> partDataMap, MesProdShiftContext prodShiftContext, String productResult) {

        //产出零件数据信息
        MesProductionPartContext productionPartContext = !CollectionUtils.isEmpty(ppMap) ? ppMap.get(prodRuleContext.getForeignKey()) : null;
        //进料零件条码
        MesProductionPsInContext productionPsInContext = !CollectionUtils.isEmpty(psMap) ? psMap.get(prodRuleContext.getForeignKey()) : null;
        //零件信息
        MesPart part = partDataMap.get(prodRuleContext.getOutPartNo());

        //验证进料零件与产出零件是否一致
        Boolean isSamePart = workOrderCheckCompleteQtyStepService.isSamePart(productionPsInContext, productionPartContext, prodRuleContext);
        //验证是否计数
        Boolean isCalcCompleteQty = workOrderCheckCompleteQtyStepService.isCalcCompleteQty(isSamePart, productionPsInContext, productionPartContext);

        MesProduceSn produceSn;
        //进出一致, 不累计工单完成数量 在显示装配件工步中已经验证了 进出一致  进出存在工单则必须一致
        if (isSamePart) produceSn = produceSnExtService.getProduceSn(reqBean.getOrganizeCode(), productionPsInContext.getId());
            //未知条码
        else if (null != productionPsInContext && !StringUtils.isEmpty(productionPsInContext.getRelateId())) produceSn = produceSnExtService.getProduceSn(reqBean.getOrganizeCode(), productionPsInContext.getRelateId());
            //生成新条码
        else produceSn = new MesProduceSn();

        if (StringUtils.isEmpty(produceSn.getProductSn())) {
            if (null != productionPsInContext && !StringUtils.isEmpty(productionPsInContext.getProductSn())) produceSn.setProductSn(productionPsInContext.getProductSn());
            else produceSn.setProductSn(doGererateSerialNo(reqBean, resultBean,
                    generateGenSerialNoModel(reqBean, resultBean, getProduceSnNumberRule(reqBean, resultBean, part), part).shiftCode(prodShiftContext.getShiftCode()).foreignKey(prodRuleContext.getForeignKey())));
            produceSn.setCustSn(produceSn.getProductSn());
        }

        if (StringUtils.isEmpty(produceSn.getSerialNumber())) {
            produceSn.setSerialNumber(doGererateSerialNo(reqBean, resultBean, generateGenSerialNoModel(reqBean, resultBean, MesPcnExtConstWords.DEFAULT_SERIAL_NUMBER_RULE, part)));
        }

        produceSn.setQty(new Double(1));

        if (!StringUtils.isEmpty(prodRuleContext.getWorkOrderNo())) produceSn.setWorkOrderNo(prodRuleContext.getWorkOrderNo());

        if (null != productionPartContext && !StringUtils.isEmpty(productionPartContext.getWorkOrderNo())) {
            produceSn.setCustCode(productionPartContext.getCustCode());
            produceSn.setCustPartNo(productionPartContext.getCustPartNo());
        }

        produceSn.setAreaCode(reqBean.getAreaCode());
        produceSn.setWorkCenterCode(reqBean.getWorkCenterCode());
        produceSn.setWorkCellCode(reqBean.getWorkCellCode());

        produceSn.setPartNo(part.getPartNo());
        produceSn.setPartName(part.getPartName());
        produceSn.setProcessLabelTemplate(part.getProcessLabelTemplate());
        produceSn.setProdLabelTemplate(part.getProductLabelTemplate());
        produceSn.setCustLabelTemplate(part.getCustLabelTemplate());

        produceSn.setSnType(MesExtEnumUtil.PRODUCE_SN_TYPE.STANDARD.getValue());
        produceSn.setSnStatus(MesExtEnumUtil.PRODUCE_SN_STATUS.UNKNOW.getValue());
        produceSn.setQcStatus(!StringUtils.isEmpty(productResult) ? Integer.valueOf(productResult) : MesExtEnumUtil.PRODUCE_QC_STATUS.QUALIFIED.getValue());

        produceSn.setShiftCode(prodShiftContext.getShiftCode());
        produceSn.setShiftGroup(prodShiftContext.getShiftGroup());

        if (StringUtils.isEmpty(produceSn.getPrintCount())) produceSn.setPrintCount(MesPcnExtConstWords.ZERO);
        if (StringUtils.isEmpty(produceSn.getPrintStatus())) produceSn.setPrintStatus(MesExtEnumUtil.PRINT_STATUS.UNPRINT.getValue());

        produceSn.setOrganizeCode(reqBean.getOrganizeCode());

        if (StringUtils.isEmpty(produceSn.getId())) ConvertBean.serviceModelInitialize(produceSn, reqBean.getUserInfo());
        else ConvertBean.serviceModelUpdate(produceSn, reqBean.getUserInfo());

        if (StringUtils.isEmpty(produceSn.getInWorkCenterTime())) produceSn.setInWorkCenterTime(produceSn.getModifyDatetime());
        produceSn.setLotNo(produceSn.getModifyDatetime().substring(0, 10));

        //未知不同步
        produceSn.setSystemSyncStatus(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue());

        if (StringUtils.isEmpty(produceSn.getId())) {
            produceSn.setFid(UUID.randomUUID().toString());
            produceSn = produceSnExtService.insert(produceSn);
        } else produceSnExtService.updateNoSync(produceSn);

        //isCalcCompleteQty=true的情况下【工位是否不累计工单完成数noCalcOrderQty=true最终不会实际累加到工单上】【noCalcOrderQty=false代表生产线维度的统计,才会实际累加到工单上】
        MesProductionPsOutContext productionPsOutContext = new MesProductionPsOutContext()
                .copy(produceSn, prodRuleContext.getForeignKey()).isCalcCompleteQty(isCalcCompleteQty, isSamePart, workCell.getNoCalcOrderQty());

        log.info("工厂{}生产线{}工位{}:FSM STATE DISPATCHER --- DO STEP --- {} EXEC --- MesProduceSn:{} --- MesProductionPsOutContext:{}",
                reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode(),
                StringUtil.toLowerCaseFirst(this.getClass().getSimpleName()), JSONObject.toJSONString(produceSn), JSONObject.toJSONString(productionPsOutContext));

        return productionPsOutContext;

    }

    private GenSerialNoModel generateGenSerialNoModel(StationRequestBean reqBean, StationResultBean resultBean, String ruleCode, MesPart part) {
        return new GenSerialNoModel(ruleCode).prodLocation(reqBean.getWorkCenterCode()).partNo(part.getPartNo()).partSnParam(part.getPartSnParam())
                .putDataMap(MesPart.class.getSimpleName(), part).basicInfo(reqBean.getOrganizeCode(), reqBean.getWorkCenterCode(), reqBean.getWorkCellCode());
    }

    private String doGererateSerialNo(StationRequestBean reqBean, StationResultBean resultBean, GenSerialNoModel serialNoModel) {
        try {
            return syncFuncService.syncSerialNo(serialNoModel, reqBean.getUserInfo(), reqBean.getOrganizeCode(), 1).getResultList().get(0).toString();
        } catch (ImppBusiException e) {
            stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), e.getErrorDetail());
        } catch (Exception e) {
            String webMsg = String.format("零件编码[%s]编码规则代码[%s]生成零件条码%s", serialNoModel.getPartNo(), serialNoModel.getRuleCode(), fsmRouteDataService.handleFsmCfgOrDefault(reqBean, MesPcnEnumUtil.FSM_CFG.FSM_EXCEPTION_MSG.getCode()));
            this.sendMessage(reqBean, resultBean.writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.ERROR.getValue()), webMsg, MesPcnEnumUtil.STATION_BUSI_TYPE.MESSAGE, MesPcnEnumUtil.STATION_DATA_TYPE.EXP_TEXT);
            this.cacheException(reqBean, reqBean.getStepName(), webMsg, e, true);
            foundExThrowNoShowMsg();
        }
        return null;
    }

    private String getProduceSnNumberRule(StationRequestBean reqBean, StationResultBean resultBean, MesPart part) {
        if (StringUtils.isEmpty(part.getProductMatchRule())) stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), String.format("请检查零件信息,零件[%s]信息未维护零件条码编码规则!", part.getPartNo()));
        return part.getProductMatchRule();
    }

}
