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.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.platform.common.tool.TimeTool;
import cn.estsh.i3plus.pojo.base.bean.DdlPackBean;
import cn.estsh.i3plus.pojo.base.codemaker.SnowflakeIdMaker;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.enumutil.MesPcnEnumUtil;
import cn.estsh.i3plus.pojo.base.tool.DdlPreparedPack;
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.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.MesWorkOrderCutDetailRepository;
import cn.estsh.i3plus.pojo.mes.repository.MesWorkOrderCutRepository;
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.function.Function;
import java.util.stream.Collectors;

/**
 * @Description : 保存裁片工单条码工步
 * @Author : wangjie
 **/
@Slf4j
@Service("mesWorkOrderCutProductSnSaveStepService")
public class MesWorkOrderCutProductSnSaveStepService extends BaseStepService {

    @Autowired
    private IMesProductionProcessContextStepService productionProcessContextStepService;

    @Autowired
    private IMesProductionDispatchContextStepService productionDispatchContextStepService;

    @Autowired
    private MesWorkOrderCutRepository workOrderCutRepository;

    @Autowired
    private IMesProduceSnExtService produceSnExtService;

    @Autowired
    private ISyncFuncService syncFuncService;

    @Autowired
    private IFsmRouteDataService fsmRouteDataService;

    @Autowired
    private MesWorkOrderCutDetailRepository workOrderCutDetailRepository;

    @Autowired
    private IMesProdShiftRecordService prodShiftRecordService;

    @Autowired
    private SnowflakeIdMaker snowflakeIdMaker;

    @Override
    public StepResult execute(StationRequestBean reqBean) {

        StationResultBean resultBean = new StationResultBean();

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

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

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

        //看是否修改裁片工单状态
        //获取上下文进料零件条码信息集合
        List<MesProductionPsInContext> productionPsInContextList = productionDispatchContextStepService.getProductionPsInContext(reqBean);

        //获取上下文产成零件条码信息集合
        List<MesProductionPsOutContext> productionPsOutContextList = productionDispatchContextStepService.getProductionPsOutContext(reqBean);

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

        // 从产出零件上下文中获取裁片工单号
        String cutWorkOrderNo = productionPartContextList.stream().findFirst().get().getCutWorkOrderNo();

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

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

        // 根据产出零件号分组加工规则
        Map<String, MesProdRuleContext> prodRuleContextMapByOutPartNo = prodRuleContextList.stream().collect(Collectors.toMap(MesProdRuleContext::getOutPartNo, Function.identity(), (x, y) -> y));

        // 获取产成零件信息
        Map<String, MesPart> partDataContext = productionDispatchContextStepService.getPartDataContext(reqBean);

        //从上下文中取出工位对象
        MesWorkCell workCell = productionProcessContext.getWorkCell();

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

        // 无进料条码则生成条码
        if (CollectionUtils.isEmpty(productionPsInContextList)) {

            productionPsOutContextList = new ArrayList<>();

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

            for (MesProductionPartContext mesProductionPartContext : productionPartContextList) {

                MesPart part = partDataContext.get(mesProductionPartContext.getPartNo());

                MesProdRuleContext mesProdRuleContext = prodRuleContextMapByOutPartNo.get(mesProductionPartContext.getPartNo());

                MesProduceSn produceSn = new MesProduceSn();
                ConvertBean.serviceModelInitialize(produceSn, reqBean.getUserInfo());
                produceSn.setOrganizeCode(reqBean.getOrganizeCode());
                produceSn.setProductSn(cutWorkOrderNo);
                produceSn.setRouteCode(reqBean.getRouteCode());
                produceSn.setProcessCode(reqBean.getProcessCode());
                produceSn.setCraftCode(productionProcessContext.getCraftCode());

                produceSn.setCustSn(produceSn.getCustSn());
                produceSn.setSerialNumber(snowflakeIdMaker.nextId() + MesPcnExtConstWords.EMPTY);
                produceSn.setQty(mesProductionPartContext.getCutQty());

                produceSn.setWorkOrderNo(mesProductionPartContext.getWorkOrderNo());
                produceSn.setCustCode(mesProductionPartContext.getCustCode());
                produceSn.setCustPartNo(mesProductionPartContext.getCustPartNo());

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

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

                produceSn.setSnType(MesExtEnumUtil.PRODUCE_SN_TYPE.STANDARD.getValue());
                produceSn.setSnStatus(workCell.getIsEndWorkCell() != null && workCell.getIsEndWorkCell() == CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue() ? MesExtEnumUtil.PRODUCE_SN_STATUS.OFFLINE.getValue() : MesExtEnumUtil.PRODUCE_SN_STATUS.PROCESSING.getValue());
                produceSn.setQcStatus(MesExtEnumUtil.PRODUCE_QC_STATUS.QUALIFIED.getValue());

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

                produceSn.setPrintCount(MesPcnExtConstWords.ZERO);
                produceSn.setPrintStatus(MesExtEnumUtil.PRINT_STATUS.UNPRINT.getValue());

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

                produceSn.setFid(UUID.randomUUID().toString());
                produceSn = produceSnExtService.insert(produceSn);

                MesProductionPsOutContext productionPsOutContext = new MesProductionPsOutContext().copy(produceSn, mesProdRuleContext.getForeignKey());

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

                productionPsOutContextList.add(productionPsOutContext);

                // 裁片工单明细 条码赋值 作用：多工位情况下 判定productSn是否有值，无值则无需查条码表
                workOrderCutDetailRepository.updateByProperties(
                        new String[]{MesPcnExtConstWords.CUT_WORK_ORDER_NO, MesPcnExtConstWords.ORGANIZE_CODE, MesPcnExtConstWords.IS_VALID, MesPcnExtConstWords.IS_DELETED},
                        new Object[]{cutWorkOrderNo, reqBean.getOrganizeCode(), CommonEnumUtil.VALID, CommonEnumUtil.FALSE},
                        new String[]{MesPcnExtConstWords.PRODUCT_SN, MesPcnExtConstWords.MODIFY_USER, MesPcnExtConstWords.MODIFY_DATE_TIME},
                        new Object[]{cutWorkOrderNo, reqBean.getUserInfo(), TimeTool.getNowTime(true)});

            }

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

        } else {

            // 保存零件条码
            productionPsOutContextList.stream().filter(o -> null != o).forEach(o -> o.copy(saveProduceSnData(reqBean, resultBean, productionProcessContext, o, prodShiftContext, workCell)));

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

            for (MesProductionPartContext mesProductionPartContext : productionPartContextList) {

                Map<String, MesProductionPsOutContext> productionPsOutContextMap = productionPsOutContextList.stream().collect(Collectors.toMap(MesProductionPsOutContext::getPartNo, Function.identity(), (x, y) -> y));

                MesProductionPsOutContext productionPsOutContext = productionPsOutContextMap.get(mesProductionPartContext.getPartNo());

                if (productionPsOutContext == null)
                    stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), String.format("产出零件号[%s]不存在,请重置工序解决!", mesProductionPartContext.getPartNo()));

            }
        }

        return stepSuccessCompleteAndSendMsgReturn(reqBean, new StationResultBean().writeDbLog(MesPcnEnumUtil.WORK_CELL_SCAN_MONITOR_LOG_TYPE.PROCESS.getValue()), "保存裁片工单条码成功!");

    }

    private MesProduceSn saveProduceSnData(StationRequestBean reqBean, StationResultBean resultBean, MesProductionProcessContext productionProcessContext, MesProductionPsOutContext productionPsOutContext,
                                           MesProdShiftContext prodShiftContext, MesWorkCell workCell) {

        //根据零件条码ID查询零件条码信息
        MesProduceSn produceSn = produceSnExtService.getProduceSn(reqBean.getOrganizeCode(), productionPsOutContext.getId());

        if (null == produceSn) stepExpSendMsgAndThrowEx(reqBean, resultBean.writeDbLog(), String.format("请检查零件条码信息,产出零件条码[%s]ID[%s]信息不存在!", productionPsOutContext.getProductSn(), productionPsOutContext.getId()));

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

        produceSn.setRouteCode(reqBean.getRouteCode());
        produceSn.setProcessCode(reqBean.getProcessCode());
        produceSn.setCraftCode(productionProcessContext.getCraftCode());

        produceSn.setQcStatus(MesExtEnumUtil.PRODUCE_QC_STATUS.QUALIFIED.getValue());

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

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

        produceSn.setSnStatus(MesExtEnumUtil.PRODUCE_SN_STATUS.PROCESSING.getValue());

        produceSnExtService.update(produceSn);

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

        return produceSn;

    }

    private void updateWorkOrderCutStatus(StationRequestBean reqBean, String cutWorkOrderNo, Integer status) {
        DdlPackBean packBean = DdlPackBean.getDdlPackBean(reqBean.getOrganizeCode());
        DdlPreparedPack.getStringEqualPack(cutWorkOrderNo, MesPcnExtConstWords.CUT_WORK_ORDER_NO, packBean);
        workOrderCutRepository.updateByProperties(new String[]{MesPcnExtConstWords.MODIFY_USER, MesPcnExtConstWords.MODIFY_DATE_TIME, MesPcnExtConstWords.WORK_ORDER_STATUS},
                new Object[]{reqBean.getUserInfo(), TimeTool.getNowTime(true), status},
                packBean);
    }

    private String doGenerateSerialNo(StationRequestBean reqBean, StationResultBean resultBean, String ruleCode, MesPart part) {
        try {
            return syncFuncService.syncSerialNo(
                    new GenSerialNoModel(ruleCode).prodLocation(reqBean.getWorkCenterCode()).partNo(part.getPartNo()).putDataMap(MesPart.class.getSimpleName(), part).organizeCode(reqBean.getOrganizeCode()),
                    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", part.getPartNo(), ruleCode, 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;
    }

}
