package com.xisc.pm.app.service.strategy.pdmattype;

import com.google.common.collect.Lists;
import com.xisc.pm.api.dto.PmCompositeBlockDTO;
import com.xisc.pm.api.dto.PmCompositeSlabCalcDTO;
import com.xisc.pm.api.dto.PmProContFilterRequestDTO;
import com.xisc.pm.api.dto.PmProContFilterResposeDTO;
import com.xisc.pm.api.dto.PmProContYieldDTO;
import com.xisc.pm.api.dto.PmProContYieldRequestDTO;
import com.xisc.pm.api.dto.PmProContYieldResposeDTO;
import com.xisc.pm.api.dto.PmSmallPlateDTO;
import com.xisc.pm.api.dto.RuleParamDTO;
import com.xisc.pm.api.dto.ctevent.ProContBoardEndDTO;
import com.xisc.pm.app.service.PmPdCcSlabService;
import com.xisc.pm.app.service.strategy.PdMatTypeStrategyFactory;
import com.xisc.pm.app.service.strategy.formplatemode.FormPlateModeStrategy;
import com.xisc.pm.domain.entity.PmContMscTech;
import com.xisc.pm.domain.entity.PmIngotCutLenBt;
import com.xisc.pm.domain.entity.PmIngotSpecTrimBt;
import com.xisc.pm.domain.entity.PmIngotThickAddvBt;
import com.xisc.pm.domain.entity.PmIngotTypeBt;
import com.xisc.pm.domain.entity.PmIngotYieldBt;
import com.xisc.pm.domain.entity.PmPdBlockPos;
import com.xisc.pm.domain.entity.PmPdCcSlab;
import com.xisc.pm.domain.entity.PmPdConstBt;
import com.xisc.pm.domain.entity.PmPdContSum;
import com.xisc.pm.domain.entity.PmPdSlab;
import com.xisc.pm.domain.entity.PmProContProdTtr;
import com.xisc.pm.domain.entity.PmSlabSpecBt;
import com.xisc.pm.domain.entity.PmSmallPltPos;
import com.xisc.pm.domain.repository.PmIngotCutLenBtRepository;
import com.xisc.pm.domain.repository.PmIngotSpecTrimBtRepository;
import com.xisc.pm.domain.repository.PmIngotThickAddvBtRepository;
import com.xisc.pm.domain.repository.PmIngotTypeBtRepository;
import com.xisc.pm.domain.repository.PmIngotYieldBtRepository;
import com.xisc.pm.domain.repository.PmPdCcSlabRepository;
import com.xisc.pm.domain.repository.PmPdConstBtRepository;
import com.xisc.pm.domain.repository.PmPdSlabRepository;
import com.xisc.pm.domain.repository.PmQdToPoOrderRepository;
import com.xisc.pm.domain.vo.CtMainVO;
import com.xisc.pm.infra.constant.Constants;
import com.xisc.pm.infra.constant.Constants.PdStatusHp;
import com.xisc.pm.infra.constant.CtEventConstants;
import com.xisc.pm.infra.constant.PmPlateConstants;
import com.xisc.pm.infra.enums.PdEventType;
import com.xisc.pm.infra.enums.PdMatType;
import com.xisc.pm.infra.enums.PdProdFlag;
import com.xisc.pm.infra.enums.PdRuleSetType;
import com.xisc.pm.infra.util.BeanUtils;
import com.xisc.pm.infra.util.Utils;
import io.choerodon.core.exception.CommonException;
import org.apache.commons.lang3.StringUtils;
import org.hzero.core.base.BaseConstants;
import org.hzero.core.message.Message;
import org.hzero.core.message.MessageAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

/**
 * 二次开坯（钢锭）
 * 
 * @author jun.zeng
 *
 */
@Component
public class IgSlabPdMatTypeStratrgy extends DefaultPdMatTypeStrategy<PmPdSlab> {

    @Autowired
    private PmPdSlabRepository pmPdSlabRepository;

    @Autowired
    private PmPdCcSlabRepository pmPdCcSlabRepository;
    
    @Autowired
    private PmPdCcSlabService pmPdCcSlabService;

    @Autowired
    private PmPdConstBtRepository pmPdConstBtRepository;

    @Autowired
    private PmIngotSpecTrimBtRepository pmIngotSpecTrimBtRepository;

    @Autowired
    private PmQdToPoOrderRepository pmQdToPoOrderRepository;

    @Autowired
    private PmIngotYieldBtRepository pmIngotYieldBtRepository;

    @Autowired
    private PmIngotThickAddvBtRepository pmIngotThickAddvBtRepository;

    @Autowired
    private PmIngotCutLenBtRepository pmIngotCutLenBtRepository;
    
    @Autowired
    private PmIngotTypeBtRepository pmIngotTypeBtRepository;

    @Override
    public String pdProgram() {
        return Constants.PdProgram.IG_SLAB_PROGRAM;

    }
    
    @Override
    public void ctEventRec(PmPdSlab t, List<PmSmallPltPos> pmSmallPltPoss,
            Map<String, PmProContFilterResposeDTO> filterOrderMap, PmProContYieldRequestDTO pmProContYield,
            PmCompositeSlabCalcDTO pmCompositeSlabCalc, PmProContYieldResposeDTO pmProContYieldRespose) {
        List<ProContBoardEndDTO> boardEndEventList = new ArrayList<>();
        String midVirtualSlabNo = getSlabNo(pmProContYield);
        PmProContFilterResposeDTO axisPmProCont = filterOrderMap.get(pmProContYield.getAxisOrderNo());
        // 虚拟板坯维度
        boardEndEventList.add(new ProContBoardEndDTO(axisPmProCont.getOrderNo()).setMatNo(midVirtualSlabNo)
                            .setLen(pmProContYieldRespose.getPreCleanSlabLen()).setMoMatNo(getSlabMoMatNo(pmProContYield))
                            .setMscLineNo(pmProContYield.getPmContFilterRequest().getMscLineNo())
                            .setPdMatStatus(PdStatusHp.PD_STATUS_HP_12).setPpFlag(PdProdFlag.VIRTUAL_SLAB.getCode())
                            .setThk(pmProContYield.getPmContFilterRequest().getMoldThick())
                            .setWid(pmProContYield.getPmContFilterRequest().getMoldWidth())
                            .setWgt(pmProContYieldRespose.getPreCleanSlabWt())
                            .setWholeBacklogCode(getWholeBacklogCode(axisPmProCont, pmProContYield))
                            .setStNo(axisPmProCont.getStNo())
                            .setSgSign(axisPmProCont.getSgSign())
                            .setProcCd(getProcCd()));
        // 大板维度
        boardEndEventList.add(new ProContBoardEndDTO(axisPmProCont.getOrderNo()).setMatNo(midVirtualSlabNo)
                .setLen(pmProContYieldRespose.getLaim()).setMoMatNo(getSlabMoMatNo(pmProContYield))
                .setMscLineNo(pmProContYield.getPmContFilterRequest().getMscLineNo())
                .setPdMatStatus(PdStatusHp.PD_STATUS_HP_12).setPpFlag(PdProdFlag.VIRTUAL_COMPOSITE_SLAB.getCode())
                .setThk(pmProContYieldRespose.getTaim())
                .setWid(pmProContYieldRespose.getWaim())
                .setWgt(pmProContYieldRespose.getRollWtHp())
                .setWholeBacklogCode(getWholeBacklogCode(axisPmProCont, pmProContYield))
                .setStNo(axisPmProCont.getStNo())
                .setSgSign(axisPmProCont.getSgSign())
                .setProcCd(getProcCd()));
        
        for (PmSmallPltPos pmSmallPltPos : pmSmallPltPoss) {
            PmProContFilterResposeDTO pmProContFilterResposeDTO = filterOrderMap.get(pmSmallPltPos.getOrderNo());
            // 小板维度
            boardEndEventList
                    .add(new ProContBoardEndDTO(pmSmallPltPos.getOrderNo()).setMatNo(midVirtualSlabNo + pmSmallPltPos.getSmallPlateDivNo())
                            .setLen(pmSmallPltPos.getOrderLen()).setMoMatNo(midVirtualSlabNo)
                            .setMscLineNo(pmProContYield.getPmContFilterRequest().getMscLineNo())
                            .setPdMatStatus(PdStatusHp.PD_STATUS_HP_12).setPpFlag(getSmallPosPpFlag())
                            .setThk(pmSmallPltPos.getOrderThick()).setWid(pmSmallPltPos.getOrderWidth())
                            .setWgt(pmProContFilterResposeDTO.getOrderUnitAimWt())
                            .setWholeBacklogCode(getWholeBacklogCode(axisPmProCont, pmProContYield))
                            .setStNo(pmProContFilterResposeDTO.getStNo())
                            .setSgSign(pmProContFilterResposeDTO.getSgSign())
                            .setProcCd(getProcCd()));
        }
        eventClient.addEvent(getCtEventCode(), boardEndEventList);
    }
    
    @Override
    public void buildOtherInfo(PmProContYieldRequestDTO pmProContYield, List<PmPdBlockPos> pmPdBlockPoss,
            List<PmPdContSum> pmPdContSums, List<PmSmallPltPos> pmSmallPltPoss, PmProContProdTtr axisPmProContProdTtr,
            Map<String, PmProContFilterResposeDTO> filterOrderMap) {
        String maxDivNo = pmSmallPltPosRepository.selectMaxSmallDivNoBySlabNo(pmProContYield.getTenantId() , getSlabNo(pmProContYield));
        String pdMatNo = pmProContYield.getVirtualSlabNo();
        Long tenantId = pmProContYield.getTenantId();
        Map<String , PmPdContSum> contMap = new HashMap<>();
        int i = maxDivNo == null ? 1 : (Integer.parseInt(maxDivNo) + 1);
        if (!"0".equals(axisPmProContProdTtr.getTrimCode())
                && pmProContYield.getCompositeBoard().getSpCount() >= PmPlateConstants.SMALL_PLATE_SPLIT_NO.length) {
            throw new CommonException("pm.pm_pro_cont_044");
        }
        String slabNo = getSlabNo(pmProContYield);
        String plateNo = getPlateNo(pmProContYield);
        for (PmCompositeBlockDTO block : pmProContYield.getCompositeBoard().getPmCompositeBlockDTOs()) {
            Set<String> contAddFlag = new HashSet<>(pmProContYield.getCompositeBoard().getOrderCount());
            for (PmSmallPlateDTO smallPlate : block.getSmallPlateDTOList()) {
                Long proContId = filterOrderMap.get(smallPlate.getOrderNo()).getProContId();
                if(!contMap.containsKey(smallPlate.getOrderNo())) {
                    PmPdContSum pmPdContSum = BeanUtils.copy(smallPlate, PmPdContSum.class);
                    pmPdContSum.setTenantId(tenantId);
                    pmPdContSum.setOrderNo(smallPlate.getOrderNo());
                    pmPdContSum.setPdMatNo(pdMatNo);
                    PdMatType pdMatType = PdMatType.getByCode(pmProContYield.getPmContFilterRequest().getMatDesignKind());
                    pmPdContSum.setPdMatType(pdMatType.getActualPdMatType());
                    pmPdContSum.setProContId(proContId);
                    pmPdContSum.setSlabNo(slabNo);
                    pmPdContSum.setPlateNo(plateNo);
                    if (axisPmProContProdTtr.getOrderNo().equals(smallPlate.getOrderNo())) {
                        pmPdContSum.setAxisFlag(BaseConstants.Flag.YES.longValue());
                    } else {
                        pmPdContSum.setAxisFlag(BaseConstants.Flag.NO.longValue());
                    }
    
                    pmPdContSum.setPdStatus(getOrderSumStatus());
                    pmPdContSums.add(pmPdContSum);
                    contMap.put(smallPlate.getOrderNo(), pmPdContSum);
                } else {
                    if(!contAddFlag.contains(smallPlate.getOrderNo())) {
                        // 同一个板块类一个小板合同只计算一次
                        PmPdContSum pmPdContSum = contMap.get(smallPlate.getOrderNo());
                    }
                }
                if (!contAddFlag.contains(smallPlate.getOrderNo())) {
                    // 如果不在标记中，则需要添加到组板子板位置表 和合同总量表中
                    contAddFlag.add(smallPlate.getOrderNo());
                    PmPdBlockPos pmPdBlockPos = new PmPdBlockPos();
                    pmPdBlockPos.setPdMatNo(pdMatNo);
                    pmPdBlockPos.setOrderBlockNo(block.getBlockNo().longValue());
                    pmPdBlockPos.setOrderNo(smallPlate.getOrderNo());
                    pmPdBlockPos.setOrderSeqNo(smallPlate.getOrderSeqNo());
                    pmPdBlockPos.setBlockPlateNum(BigDecimal.valueOf(smallPlate.getOrderBlockNum()));
                    pmPdBlockPos.setSlabNo(slabNo);
                    pmPdBlockPos.setPlateNo(plateNo);
                    pmPdBlockPos.setProContId(proContId);
                    pmPdBlockPos.setTenantId(tenantId);
                    pmPdBlockPoss.add(pmPdBlockPos);
                }
    
                PmSmallPltPos pmSmallPltPos = BeanUtils.copy(smallPlate, PmSmallPltPos.class);
                pmSmallPltPos.setTenantId(tenantId);
                pmSmallPltPos.setPdMatNo(pdMatNo);
                pmSmallPltPos.setSlabNo(slabNo);
                pmSmallPltPos.setPlateNo(plateNo);
                pmSmallPltPos.setProContId(proContId);
                pmSmallPltPos.setPickModeLenDir(smallPlate.getPickModeLenDirPos().longValue());
                pmSmallPltPos.setPickModeWidDir(smallPlate.getPickModeWidDirPos().longValue());
                pmSmallPltPos.setSmallPlateDivNo(getSmallPlateDivNo(plateNo, axisPmProContProdTtr.getTrimCode() , i++));
                pmSmallPltPoss.add(pmSmallPltPos);
            }
        }
    }
    
    @Override
    public String getSmallPlateDivNo(String plateNo, String trimCode, int index) {
        return String.format("%03d", index);
    }

    @Override
    public void contFilterInitParam(PmProContFilterRequestDTO pmProContFilterRequest) {
        //pmProContFilterRequest.setSgSign(null);
        pmProContFilterRequest.setStReplFlag(BaseConstants.Flag.NO.longValue());
        pmProContFilterRequest.check();
    }

    @Override
    public boolean contFilterRuleSetCheck(PmProContFilterRequestDTO pmProContFilterRequest,
            PmProContFilterResposeDTO pmProContFilterResposeDTO, List<PmSlabSpecBt> pmSlabSpecBts,
            Map<String, CtMainVO> cmCtMainMap) {
        RuleParamDTO ruleParam = initContFilterRuleParam(PdRuleSetType.A01, pmProContFilterResposeDTO,
                pmProContFilterRequest, pmSlabSpecBts);
        ruleParam.buildExcludeRuleNo(pmProContFilterRequest);
        try {
            pmPdRuleSetService.runRuleSet(ruleParam);
            return true;
        } catch (CommonException e) {
            Message message = MessageAccessor.getMessage(e.getCode(), e.getParameters());
            pmProContFilterResposeDTO.setUnSelectedReason(message.getDesc());
            return false;
        }
    }

    @Override
    public RuleParamDTO contSelectionRuleSet() {
        return new RuleParamDTO(PdRuleSetType.A03);
    }

    @Override
    public RuleParamDTO contSelectionCheckRuleSet() {
        return new RuleParamDTO(PdRuleSetType.A04);
    }

    @Override
    public RuleParamDTO pdResultCheckRuleSet() {
        return new RuleParamDTO(PdRuleSetType.A05);
    }

    @Override
    public void yiledCalcBeforeCheck(PmProContYieldRequestDTO pmProContYield, boolean isExec) {
        if (StringUtils.isBlank(pmProContYield.getPmContFilterRequest().getIngotType())) {
            throw new CommonException("pm.pm_pro_cont_057");
        }
        if (StringUtils.isBlank(pmProContYield.getVirtualSlabNo())) {
            throw new CommonException("pm.pm_pro_cont_039");
        }
        pmProContYield.getPmContFilterRequest().setFixSlabLenFlag(BaseConstants.Flag.NO.longValue());
        // 二次开坯不抛合同跟踪事件
        if (isExec) {
            if(pmProContYield.getPmContFilterRequest().getOneRollFlag() != null && pmProContYield.getPmContFilterRequest().getOneRollFlag() == 1L) {
                return ;
            }
            PmPdSlab pmPdSlab = pmPdSlabRepository.selectByVirtualSlabNo(pmProContYield.getVirtualSlabNo(),
                    pmProContYield.getTenantId());
            if (pmPdSlab == null || !Constants.PdStatusHp.PD_STATUS_HP_31.equals(pmPdSlab.getPdStatusHp())) {
                // 分割信息【virtual_slab_no】未在中间坯材料处于组板中状态，不能组板；
                throw new CommonException("pm.pm_pd_slab_34", pmProContYield.getVirtualSlabNo());
            }
            pmProContYield.setPmPdSlab(pmPdSlab);
        }
    }

    @Override
    public void calcRsCodeAndBroadFlag(PmProContYieldRequestDTO pmProContYield, PmProContProdTtr axisPmProContProdTtr,
            PmCompositeSlabCalcDTO pmCompositeSlabCalc) {
        // 获取切边量
        List<PmIngotSpecTrimBt> pmIngotSpecTrimBts = pmIngotSpecTrimBtRepository.selectBy(pmProContYield.getTenantId(),
                pmProContYield.getPmContFilterRequest().getRollDirectCode(), axisPmProContProdTtr.getProdSideMark(),
                axisPmProContProdTtr.getOrderThick(), pmProContYield.getCompositeBoard().getSzWidth(),
                pmProContYield.getPmContFilterRequest().getMscLineNo());

        if (pmIngotSpecTrimBts == null || pmIngotSpecTrimBts.isEmpty()) {
            // 钢锭轧制规格及切边量基准表,切边标记【{0}】,轴合同厚度【{1}】,组合板宽【{2}】,轧制方向【{3}】，设计产线号【{4}】
            throw new CommonException("pm.pm_ingot_spec_trim_bt_001", axisPmProContProdTtr.getProdSideMark(),
                    axisPmProContProdTtr.getOrderThick().toPlainString(),
                    pmProContYield.getCompositeBoard().getSzWidth().toPlainString(),
                    pmProContYield.getPmContFilterRequest().getRollDirectCode(),
                    pmProContYield.getPmContFilterRequest().getMscLineNo());
        }
        BigDecimal trimWidth = BigDecimal.ZERO;
        // 取小板宽度切边量的最大值
        for (PmIngotSpecTrimBt pmPlateTrimBt : pmIngotSpecTrimBts) {
            for (BigDecimal mLen : pmCompositeSlabCalc.getMLen()) {
                if (mLen.compareTo(pmPlateTrimBt.getRollLenFrom()) >= 0
                        && mLen.compareTo(pmPlateTrimBt.getRollLenTo()) <= 0) {
                    if (pmPlateTrimBt.getTrimWidth().compareTo(trimWidth) > 0) {
                        trimWidth = pmPlateTrimBt.getTrimWidth();
                    }
                }
            }
        }
        pmCompositeSlabCalc.setTrimWidth(trimWidth);
        if (pmProContYield.getCompositeBoard().getSzWidth().add(trimWidth)
                .compareTo(pmProContYield.getPmContFilterRequest().getMoldWidth()) <= 0) {
            pmCompositeSlabCalc.setBroadFlag(BaseConstants.Flag.YES.toString());
            pmCompositeSlabCalc.setRsCode(3);
        } else {
            pmCompositeSlabCalc.setRsCode(0);
            pmCompositeSlabCalc.setBroadFlag(BaseConstants.Flag.NO.toString());
        }
    }

    @Override
    public PmProContYieldResposeDTO calcYield(Map<String, PmProContFilterResposeDTO> filterOrderMap,
            PmProContYieldRequestDTO pmProContYield, PmProContProdTtr axisPmProContProdTtr,
            PmContMscTech axisPmContMscTech, PmCompositeSlabCalcDTO pmCompositeSlabCalc) {
        PmProContYieldResposeDTO yieldRespose = new PmProContYieldResposeDTO();
        yieldRespose.setTenantId(pmProContYield.getTenantId());
        yieldRespose.setCombinePlateWidth(pmProContYield.getCompositeBoard().getSzWidth());
        yieldRespose.setCombinePlateLen(pmProContYield.getCompositeBoard().getSzLength());
        yieldRespose.setBroadFlag(pmCompositeSlabCalc.getBroadFlag());
        yieldRespose.setRsCode(pmCompositeSlabCalc.getRsCode());
        // 只要存在任意合同存在试样数据，则保性能
        Set<String> orderSet = pmQdToPoOrderRepository.qdToLineupResultSampleAllOrderList(
                Lists.newArrayList(filterOrderMap.keySet()), pmProContYield.getTenantId());
        if (orderSet != null && !orderSet.isEmpty()) {
            yieldRespose.setGuaranteeQuality(BaseConstants.Flag.YES.toString());
        } else {
            yieldRespose.setGuaranteeQuality(BaseConstants.Flag.NO.toString());
        }

        PmIngotYieldBt pmIngotYieldBt = pmIngotYieldBtRepository.selectByIngotType(
                pmProContYield.getPmContFilterRequest().getIngotType(), pmProContYield.getTenantId());

        BeanCopier copier = BeanCopier.create(PmProContFilterResposeDTO.class, PmProContProdTtr.class, false);
        BeanCopier copierContMscTech = BeanCopier.create(PmProContFilterResposeDTO.class, PmContMscTech.class, false);
        Map<String, PmProContProdTtr> pmProContProdTtrs = new HashMap<String, PmProContProdTtr>();
        Map<String, PmContMscTech> pmContMscTechs = new HashMap<String, PmContMscTech>();

        // 通过参数匹配厚度余量
        PmIngotThickAddvBt pmIngotThickAddvBt = pmIngotThickAddvBtRepository.selectTaimAddv(
                pmProContYield.getTenantId(), pmProContYield.getPmContFilterRequest().getIngotType(),
                axisPmContMscTech.getRollMode(), axisPmContMscTech.getWholeHeatTreatmentType(),
                pmProContYield.getPmContFilterRequest().getRollDirectCode(), axisPmProContProdTtr.getOrderThick(),
                pmProContYield.getCompositeBoard().getSzWidth());

        // 轧制目标厚度taim
        BigDecimal maxTaim = null;
        BigDecimal minTaim = null;
        // 合同最大头部或者尾部切舍量
        BigDecimal maxOrderheadCut = null;
        BigDecimal maxOrderTailCut = null;
        // 合同厚度是否是否一致
        boolean thkEqualsFlag = true;
        // 公差交集下限是否一致
        boolean minTaimFlag = true;

        for (Entry<String, PmProContFilterResposeDTO> entry : filterOrderMap.entrySet()) {
            PmProContProdTtr pmProContProdTtr = new PmProContProdTtr();
            PmContMscTech pmContMscTech = new PmContMscTech();
            copier.copy(entry.getValue(), pmProContProdTtr, null);
            pmProContProdTtrs.put(entry.getKey(), pmProContProdTtr);
            copierContMscTech.copy(entry.getValue(), pmContMscTech, null);
            pmContMscTechs.put(entry.getKey(), pmContMscTech);
            if (thkEqualsFlag
                    && axisPmProContProdTtr.getOrderThick().compareTo(pmProContProdTtr.getOrderThick()) != 0) {
                // 如果合同厚度不等于轴合同订货厚度需要取交集的中间值
                thkEqualsFlag = false;
            }
            if (minTaimFlag
                    && axisPmProContProdTtr.getThickTolMin().compareTo(pmProContProdTtr.getThickTolMin()) != 0) {
                minTaimFlag = false;
            }
            // 合同订货区间
            BigDecimal thickMin = pmProContProdTtr.getOrderThick().add(pmProContProdTtr.getThickTolMin());
            BigDecimal thickMax = pmProContProdTtr.getOrderThick().add(pmProContProdTtr.getThickTolMax());
            // 记录交集
            if (maxTaim == null || maxTaim.compareTo(thickMax) > 0) {
                maxTaim = thickMax;
            }
            if (minTaim == null || minTaim.compareTo(thickMin) < 0) {
                minTaim = thickMin;
            }
            if (maxOrderheadCut == null || maxOrderheadCut.compareTo(pmContMscTech.getHeadCut()) < 0) {
                maxOrderheadCut = pmContMscTech.getHeadCut();
            }
            if (maxOrderTailCut == null || maxOrderTailCut.compareTo(pmContMscTech.getTailCut()) < 0) {
                maxOrderTailCut = pmContMscTech.getTailCut();
            }
        }

        if (filterOrderMap.size() == 1) {
            // 当为一个合同时，轧制目标厚度=订货厚度+厚度公差下限+厚度余量
            yieldRespose.setTaim(minTaim.add(pmIngotThickAddvBt.getThickAddv()));
        } else if (filterOrderMap.size() > 1 && thkEqualsFlag && minTaimFlag) {
            // 当为多个合同且下公差相同时，轧制目标厚度=订货厚度+厚度公差下限+厚度余量
            yieldRespose.setTaim(minTaim.add(pmIngotThickAddvBt.getThickAddv()));
        } else if (filterOrderMap.size() > 1 && thkEqualsFlag && !minTaimFlag) {
            // 当为多个合同且下公差不同时，计算每个合同的区间[订货厚度+厚度公差下限，订货厚度+合同厚度公差上限]，取各区间交集，轧制目标厚度=交集的下限+厚度余量，
            // 当轧制目标厚度＜交集的上限时允许通过，当轧制目标厚度≥交集的上限时不允许通过。
            yieldRespose.setTaim(minTaim.add(pmIngotThickAddvBt.getThickAddv()));
            if (yieldRespose.getTaim().compareTo(maxTaim) >= 0) {
                throw new CommonException("pm.pm_pro_cont_022");
            }
        } else {
            // 当为多个合同且多个厚度时，取出每个合同的区间[订货厚度+厚度公差下限，订货厚度+合同厚度公差上限],
            // 取各区间交集。若交集的上限-交集的下限>厚度余量，则通过，
            // 轧制目标厚度=两个交集的中间值。否则，不通过，报错：多个合同厚度不同且超过厚度余量范围，不能组在一起。
            BigDecimal diff = maxTaim.subtract(minTaim);
            if (diff.compareTo(pmIngotThickAddvBt.getThickAddv()) <= 0) {
                throw new CommonException("pm.pm_pro_cont_055");
            }
            yieldRespose.setTaim(
                    minTaim.add(diff.divide(BigDecimal.valueOf(2L), PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP)));
        }

        yieldRespose.setRollThickTolMax(maxTaim.subtract(axisPmProContProdTtr.getOrderThick()));
        yieldRespose.setRollThickTolMin(minTaim.subtract(axisPmProContProdTtr.getOrderThick()));

        // 钢板切缝切头尾范围基准表数据
        PmIngotCutLenBt pmIngotCutLenBt = pmIngotCutLenBtRepository
                .selectByOrderThick(axisPmProContProdTtr.getOrderThick(), pmProContYield.getTenantId());

        // 轧制目标宽度
        if (pmCompositeSlabCalc.getRsCode() == 3) {
            yieldRespose.setWaim(pmProContYield.getPmContFilterRequest().getMoldWidth());
        } else {
            // 轧制目标宽度=组合板宽度 + (轴合同)宽度余量 + 切边量 +（取板方式_宽度方向-1） × 钢板切缝宽度
            BigDecimal waim = yieldRespose.getCombinePlateWidth().add(axisPmProContProdTtr.getWidthAddv())
                    .add(pmCompositeSlabCalc.getTrimWidth());
            BigDecimal pickModeWidDir = BigDecimal.valueOf(pmProContYield.getCompositeBoard().getPickModeWidDir())
                    .subtract(BigDecimal.ONE);
            yieldRespose.setWaim(waim.add(pickModeWidDir.multiply(pmIngotCutLenBt.getPlateCutWidth())));
        }
        // 轧制目标长度laim
        // 组合板长度 + 试样长度 + 组板基表头部切舍量 + 组板基表尾部切舍量 +（取板方式_长度方向-1）* 长方向切缝量 +
        // MAX（大板上所有合同的头部切舍量）+ MAX（大板上所有合同的尾部切舍量）
        // 长方向切缝量 == 钢板切缝宽度
        // plateCutLenBt.getHeadCut().add(maxOrderheadCut);
        BigDecimal headCut = BigDecimal.valueOf(75);
        // plateCutLenBt.getTailCut().add(maxOrderTailCut);
        BigDecimal tailCut = BigDecimal.valueOf(75);
        BigDecimal pickModeLenDir = BigDecimal.valueOf(pmProContYield.getCompositeBoard().getPickModeLenDir())
                .subtract(BigDecimal.ONE);
        BigDecimal laim = yieldRespose.getCombinePlateLen().add(pmCompositeSlabCalc.getSampleLength()).add(headCut)
                .add(tailCut).add(pickModeLenDir.multiply(pmIngotCutLenBt.getPlateCutWidth()));
        yieldRespose.setLaim(laim);
        yieldRespose.setNormYield(pmIngotYieldBt.getYield());
        yieldRespose.setHeadCut(headCut);
        yieldRespose.setTailCut(tailCut);

        calc(pmProContYield, axisPmProContProdTtr, pmCompositeSlabCalc, pmProContProdTtrs, yieldRespose);
        return yieldRespose;
    }

    @Override
    public void calc(PmProContYieldRequestDTO pmProContYield, PmProContProdTtr axisPmProContProdTtr,
            PmCompositeSlabCalcDTO pmCompositeSlabCalc, Map<String, PmProContProdTtr> pmProContProdTtrs,
            PmProContYieldResposeDTO yieldRespose) {
        // 板坯厚,板坯宽
        BigDecimal slabTaim = pmProContYield.getPmContFilterRequest().getMoldThick();
        BigDecimal slabWaim = pmProContYield.getPmContFilterRequest().getMoldWidth();
        // 密度（克/cm^3）
        BigDecimal prodDensity = axisPmProContProdTtr.getProdDensity();

        // 轧制有效重量(克) = 轴合同订货厚度 × 组合宽 × 组合长 × 密度
        BigDecimal effectWt = axisPmProContProdTtr.getOrderThick().multiply(yieldRespose.getCombinePlateLen())
                .multiply(yieldRespose.getCombinePlateWidth()).multiply(prodDensity)
                .divide(BigDecimal.valueOf(1000), PmPlateConstants.TEM_SCALE, RoundingMode.HALF_UP);

        // 必要板坯重量(克) = 轧制有效重量÷基准收得率 = (轧制目标厚 * 轧制目标宽 * 轧制目标长 * 密度) / (1-s1);
        BigDecimal needSlabWtGram = effectWt.divide(yieldRespose.getNormYield(), PmPlateConstants.TEM_SCALE,
                RoundingMode.HALF_UP);

        // 宽，厚转化为cm - 密度单位转化为 克/cm
        BigDecimal waimAndTaimAndDensity = slabTaim.multiply(slabWaim).multiply(prodDensity)
                .divide(BigDecimal.valueOf(100), PmPlateConstants.TEM_SCALE, RoundingMode.HALF_UP);

        // 必要板坯长度（cm) = 必要板坯重量(克) ÷（板坯厚度 × 板坯宽度 × 密度）;
        // 转化为毫米
        BigDecimal needSlabLen = needSlabWtGram.multiply(BigDecimal.TEN).divide(waimAndTaimAndDensity,
                PmPlateConstants.LEN_SCALE, RoundingMode.HALF_UP);
        
        
        yieldRespose.setNeedSlabLen(needSlabLen);
        yieldRespose.setNeedSlabWt(needSlabWtGram.divide(PmPlateConstants.GRAM_TO_TON , PmPlateConstants.WT_SCALE , RoundingMode.HALF_UP));
        
        yieldRespose.setInfurSlabWt(yieldRespose.getNeedSlabWt());
        yieldRespose.setInfurSlabLen(needSlabLen);
        yieldRespose.setPreCleanSlabWt(yieldRespose.getNeedSlabWt());
        yieldRespose.setPreCleanSlabLen(needSlabLen);
        
        yieldRespose.setPreCleanSlabMinLen(yieldRespose.getPreCleanSlabLen());
        yieldRespose.setPreCleanSlabMinWt(yieldRespose.getPreCleanSlabWt());
        yieldRespose.setInfurSlabMinLen(yieldRespose.getInfurSlabLen());
        yieldRespose.setInfurSlabMinWt(yieldRespose.getInfurSlabWt());
        yieldRespose.setPreCleanSlabMaxLen(yieldRespose.getPreCleanSlabLen());
        yieldRespose.setPreCleanSlabMaxWt(yieldRespose.getPreCleanSlabWt());
        yieldRespose.setInfurSlabMaxLen(yieldRespose.getInfurSlabLen());
        yieldRespose.setInfurSlabMaxWt(yieldRespose.getInfurSlabWt());
        
        FormPlateModeStrategy formPlateModeStrategy = getFormPlateModeStrategy(pmProContYield);
        // 轧制收得率 ＝ 轧制有效重量 ÷ 钢锭重量（ingot_wt）
        PmIngotTypeBt pmIngotTypeBt = pmIngotTypeBtRepository.selectByIngotType(pmProContYield.getPmContFilterRequest().getIngotType() , pmProContYield.getTenantId());
        yieldRespose.setRollYield(effectWt.divide(PmPlateConstants.GRAM_TO_TON , PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP)
            .divide(pmIngotTypeBt.getIngotWt(), PmPlateConstants.YIELD_SCALE, RoundingMode.HALF_UP));
        // 命令收得率 ＝Σ合同单重 ÷ 进加热炉板坯重量
        BigDecimal sumPlateWt = formPlateModeStrategy.getSumPlateWt(pmProContProdTtrs, pmProContYield);
        yieldRespose.setSumPlateWt(sumPlateWt);
        yieldRespose.setCommandYield(
                sumPlateWt.divide(yieldRespose.getInfurSlabWt(), PmPlateConstants.YIELD_SCALE, RoundingMode.HALF_UP));
        
        // 厚板轧制重量roll_wt_hp = 轧制目标厚度(mm) * 轧制目标宽度(mm) * 轧制目标长度(mm) * 密度
        // 转化为吨
        yieldRespose.setRollWtHp(yieldRespose.getTaim().multiply(yieldRespose.getWaim())
                .multiply(yieldRespose.getLaim()).multiply(prodDensity)
                .divide(BigDecimal.valueOf(1000L * 1000L * 1000L), PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
    }

    @Override
    public boolean needPlateCut() {
        return false;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        PdMatTypeStrategyFactory.register(PdMatType.A, this);
    }

    @Override
    protected Long ctEventBoardPpFlag(PmPdSlab pmPdSlab, PmProContYieldRequestDTO pmProContYield) {
        return null;
    }

    @Override
    public void generateCmd(PmPdSlab pmPdSlab, List<PmPdContSum> pmPdContSums, List<PmSmallPltPos> pmSmallPltPoss,
            String mscLineNo) {
        // 二次开坯确认时组板执行时不生成命令数据
    }

    @Override
    public PmPdSlab generateResult(PmProContYieldRequestDTO pmProContYield,
            Map<String, PmProContFilterResposeDTO> filterOrderMap, PmCompositeSlabCalcDTO pmCompositeSlabCalc,
            PmProContYieldResposeDTO pmProContYieldRespose, PmContMscTech axisPmContMscTech,
            PmProContProdTtr axisPmProContProdTtr) {
        if(pmProContYield.getPmContFilterRequest().getOneRollFlag() != null && pmProContYield.getPmContFilterRequest().getOneRollFlag() == 1L) {
            pmPdCcSlabService.createPmPdCcSlab(pmProContYield, filterOrderMap, pmCompositeSlabCalc, pmProContYieldRespose, axisPmContMscTech, axisPmProContProdTtr);
            return null;
        }
        PmPdSlab pmPdSlab = pmProContYield.getPmPdSlab();
        PmPdSlab update = new PmPdSlab();
        Set<String> ultCode = new TreeSet<>();
        for (PmProContYieldDTO order : pmProContYield.getPmProContYieldList()) {
            PmProContFilterResposeDTO resposeDTO = filterOrderMap.get(order.getOrderNo());
            if (StringUtils.isNotBlank(resposeDTO.getErpUtSpec1())) {
                ultCode.add(resposeDTO.getErpUtSpec1());
             }
             if (StringUtils.isNotBlank(resposeDTO.getErpUtSpec2())) {
                 ultCode.add(resposeDTO.getErpUtSpec2());
             }
             if (StringUtils.isNotBlank(resposeDTO.getErpUtSpec3())) {
                 ultCode.add(resposeDTO.getErpUtSpec3());
             }
        }
        if(ultCode != null && !ultCode.isEmpty()) {
            update.setUltCode(String.join("@", ultCode));
        }
        update.setMatDesignKind(PdMatType.A.getActualPdMatType());
        update.setIgMatDesignKind(PdMatType.A.getActualPdMatType());
        update.setObjectVersionNumber(pmPdSlab.getObjectVersionNumber());
        update.setPmPdSlabId(pmPdSlab.getPmPdSlabId());
        update.setPdStatusHp(PdStatusHp.PD_STATUS_HP_32);
        update.setFormPlateMode(pmProContYield.getPmContFilterRequest().getFormPlateMode());
        update.setOrderNoAxis(pmProContYield.getAxisOrderNo());
        update.setSgSign(axisPmProContProdTtr.getSgSign());
        update.setStNo(axisPmContMscTech.getStNo());
        update.setIngotType(pmProContYield.getPmContFilterRequest().getIngotType());
        update.setNeedSlabLen(pmProContYieldRespose.getNeedSlabLen());
        update.setNeedSlabWt(pmProContYieldRespose.getNeedSlabWt());
        update.setInfurSlabLen(pmProContYieldRespose.getNeedSlabLen());
        update.setInfurSlabWt(pmProContYieldRespose.getNeedSlabWt());
        update.setPreCleanSlabLen(pmProContYieldRespose.getNeedSlabLen());
        update.setPreCleanSlabWt(pmProContYieldRespose.getNeedSlabWt());
        update.setPreCleanSlabThick(pmProContYield.getPmContFilterRequest().getMoldThick());
        update.setPreCleanSlabWidth(pmProContYield.getPmContFilterRequest().getMoldWidth());
        update.setInfurSlabThick(pmProContYield.getPmContFilterRequest().getMoldThick());
        update.setInfurSlabWid(pmProContYield.getPmContFilterRequest().getMoldWidth());
        update.setRollAimLen(pmProContYieldRespose.getLaim());
        update.setRollAimThick(pmProContYieldRespose.getTaim());
        update.setRollAimWidth(pmProContYieldRespose.getWaim());
        update.setOrderThickAxis(axisPmProContProdTtr.getOrderThick());
        update.setCombinePlateLen(pmProContYieldRespose.getCombinePlateLen());
        update.setCombinePlateWidth(pmProContYieldRespose.getCombinePlateWidth());
        update.setNormYield(pmProContYieldRespose.getNormYield());
        update.setMscLineNo(pmProContYield.getPmContFilterRequest().getMscLineNo());
        update.setRsCode(String.valueOf(pmCompositeSlabCalc.getRsCode()));
        update.setBroadFlag(pmCompositeSlabCalc.getBroadFlag());
        update.setRollDirectCode(Constants.RollDirectCode.L);

        PmPdCcSlab pmPdCcSlab = new PmPdCcSlab();
        PmPdCcSlab pmPdCcSlabDb = pmPdCcSlabRepository.selectByPdMatNo(pmProContYield.getTenantId(),
                pmPdSlab.getSlabNo());
        pmPdCcSlab.setRollAimThick(pmProContYield.getPmContFilterRequest().getMoldThick());
        pmPdCcSlab.setRollAimWidth(pmProContYield.getPmContFilterRequest().getMoldWidth());
        pmPdCcSlab.setVirtualSlabId(pmPdCcSlabDb.getVirtualSlabId());
        pmPdCcSlab.setObjectVersionNumber(pmPdCcSlabDb.getObjectVersionNumber());
        if (StringUtils.isBlank(pmPdCcSlabDb.getUrgOrderFlag())) {
            // 取第一个分割坯的第一个合同
            pmPdCcSlab.setUrgOrderFlag(axisPmProContProdTtr.getUrgOrderFlag() != null ? null
                    : axisPmProContProdTtr.getUrgOrderFlag().toString());
        }

        if (pmPdCcSlabDb.getRollAimLen() == null) {
            pmPdCcSlab.setRollAimLen(pmProContYieldRespose.getNeedSlabLen());
        } else {
            PmPdConstBt pmPdConstBt = new PmPdConstBt();
            pmPdConstBt.setTenantId(pmProContYield.getTenantId());
            pmPdConstBt
                    .setRollLineNo(Utils.mscLineNoToRoolLineNo(pmProContYield.getPmContFilterRequest().getMscLineNo()));
            pmPdConstBt = pmPdConstBtRepository.selectOne(pmPdConstBt);
            if (pmPdConstBt == null || pmPdConstBt.getSlabCutGap() == null) {
                // 未查询到组板常数基准表，或者长板坯充当短板坯之间切缝宽度未配置，产线号【{0}】
                throw new CommonException("pm.pm_pd_const_bt_001",
                        pmProContYield.getPmContFilterRequest().getMscLineNo());
            }
            BigDecimal slabCutGap = pmPdConstBt.getSlabCutGap() == null ? BigDecimal.ZERO : pmPdConstBt.getSlabCutGap();
            pmPdCcSlab.setRollAimLen(
                    pmPdCcSlabDb.getRollAimLen().add(slabCutGap).add(pmProContYieldRespose.getNeedSlabLen()));
        }
        BigDecimal rollWtHp = pmPdCcSlab.getRollAimLen().multiply(pmPdCcSlab.getRollAimWidth())
                .multiply(pmPdCcSlab.getRollAimThick().multiply(PmPlateConstants.DEFAULT_PROD_DENCITY));
        rollWtHp = rollWtHp.divide(BigDecimal.valueOf(1000L * 1000L * 1000L), PmPlateConstants.WT_SCALE,
                RoundingMode.HALF_UP);
        pmPdCcSlab.setRollWtHp(rollWtHp);
        Date steelPlanDate = pmPdCcSlabDb.getSteelPlanDate();
        Date steelLatestDate = pmPdCcSlabDb.getSteelLatestDate();
        for (Entry<String, PmProContFilterResposeDTO> entry : filterOrderMap.entrySet()) {
            if (entry.getValue().getSteelPlanDate() != null) {
                if (steelPlanDate == null || steelPlanDate.after(entry.getValue().getSteelPlanDate())) {
                    steelPlanDate = entry.getValue().getSteelPlanDate();
                }
            }
            if (entry.getValue().getSteelLatestDate() != null) {
                if (steelLatestDate == null || steelLatestDate.after(entry.getValue().getSteelLatestDate())) {
                    steelLatestDate = entry.getValue().getSteelLatestDate();
                }
            }
        }
        pmPdCcSlab.setSteelLatestDate(steelLatestDate);
        pmPdCcSlab.setSteelPlanDate(steelPlanDate);
        pmPdCcSlab.setOrderNumInPlate(pmPdCcSlabDb.getOrderNumInPlate() == null ? filterOrderMap.size()
                : pmPdCcSlabDb.getOrderNumInPlate() + filterOrderMap.size());
        pmPdCcSlab.setCcType(pmProContYield.getPmContFilterRequest().getCcType());
        pmPdSlabRepository.updateByPrimaryKeySelective(update);
        pmPdCcSlabRepository.updateByPrimaryKeySelective(pmPdCcSlab);

        return update;
    }
    
    @Override
    protected String getSlabNo(PmProContYieldRequestDTO pmProContYield) {
        return pmProContYield.getPmPdSlab().getSlabNo();
    }
    
    @Override
    protected String getSlabMoMatNo(PmProContYieldRequestDTO pmProContYield) {
        return null;
    }
    
    @Override
    protected String getWholeBacklogCode(PmProContFilterResposeDTO axisPmProCont,
            PmProContYieldRequestDTO pmProContYield) {
        return Constants.WholeBacklogCode.CODE_14;
    }
    
    @Override
    protected String getOrderSumStatus() {
        return PdStatusHp.PD_STATUS_HP_32;
    }

    @Override
    protected PdEventType pdDeleteEventType() {
        return PdEventType.HP17;
    }

    @Override
    protected PdEventType pdFinshedEventType() {
        return PdEventType.HP16;
    }

    @Override
    protected String getCtEventCode() {
        return CtEventConstants.PM_BLOOM_CONTRACT_BOARD_END;
    }

}
