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

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xisc.pm.api.dto.PmBoardMatInfoDTO;
import com.xisc.pm.api.dto.PmCompositeSlabCalcDTO;
import com.xisc.pm.api.dto.PmPdSlabSplitRequestDTO;
import com.xisc.pm.api.dto.PmProContFilterRequestDTO;
import com.xisc.pm.api.dto.PmProContFilterResposeDTO;
import com.xisc.pm.api.dto.PmProContYieldRequestDTO;
import com.xisc.pm.api.dto.PmProContYieldResposeDTO;
import com.xisc.pm.api.dto.RuleParamDTO;
import com.xisc.pm.app.service.PmPdSlabService;
import com.xisc.pm.app.service.strategy.PdMatTypeStrategyFactory;
import com.xisc.pm.domain.entity.MmSlab;
import com.xisc.pm.domain.entity.PmContMscTech;
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.PmSlabScrapLenBt;
import com.xisc.pm.domain.entity.PmSlabSpecBt;
import com.xisc.pm.domain.entity.PmSmallPltPos;
import com.xisc.pm.domain.repository.MmSlabRepository;
import com.xisc.pm.domain.vo.CtMainVO;
import com.xisc.pm.infra.constant.Constants;
import com.xisc.pm.infra.constant.Constants.CcType;
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.collections.CollectionUtils;
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.hzero.mybatis.domian.Condition;
import org.hzero.mybatis.util.Sqls;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 余材余坯组板
 * 
 * @author jun.zeng
 *
 */
@Component
public class SlabPdMatTypeStratrgy extends DefaultPdMatTypeStrategy<PmPdSlab> {

    @Autowired
    private PmPdSlabService pmPdSlabService;

    @Autowired
    private MmSlabRepository mmSlabRepository;

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

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

    @Override
    public boolean contFilterRuleSetCheck(PmProContFilterRequestDTO pmProContFilterRequestD ,
            PmProContFilterResposeDTO pmProContFilterResposeDTO, List<PmSlabSpecBt> pmSlabSpecBts,
            Map<String, CtMainVO> cmCtMainMap) {
        RuleParamDTO ruleParam = initContFilterRuleParam(PdRuleSetType.S01, pmProContFilterResposeDTO,
                pmProContFilterRequestD, pmSlabSpecBts);
        ruleParam.buildExcludeRuleNo(pmProContFilterRequestD);
        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.S03);
    }

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

    @Override
    public RuleParamDTO pdResultCheckRuleSet() {
        return new RuleParamDTO(PdRuleSetType.S05);
    }
    
    /**
     * 非指定长计算
     * @param pmProContYield
     * @param yieldRespose
     * @param waimAndTaimAndDensity
     * @param needSlabLen
     * @param infurSlabWtGram
     * @param needSlabWtGram
     * @return
     */
    @Override
    public BigDecimal noFixSlabLenCalc(PmProContYieldRequestDTO pmProContYield, PmProContYieldResposeDTO yieldRespose,PmProContProdTtr axisPmProContProdTtr ,BigDecimal effectWt,
            BigDecimal waimAndTaimAndDensity , BigDecimal needSlabLen ,BigDecimal infurSlabWtGram , BigDecimal needSlabWtGram) {
        BigDecimal newInfurSlabWtGram = super.noFixSlabLenCalc(pmProContYield, yieldRespose, axisPmProContProdTtr,effectWt,waimAndTaimAndDensity, needSlabLen, infurSlabWtGram, needSlabWtGram);
        PmProContFilterRequestDTO requestDTO = pmProContYield.getPmContFilterRequest();
        if (pmProContYield.getPmContFilterRequest().getRemainSlabLen() == null) {
            throw new CommonException("pm.pm_pro_cont_009", "剩余板坯长");
        }
        BigDecimal remainSlabLen = requestDTO.getRemainSlabLen().subtract(yieldRespose.getPreCleanSlabLen());
        if(BigDecimal.ZERO.compareTo(remainSlabLen) > 0) {
            // 长坯剩余长度【{0}】，该分割清理前板坯长度【{1}】，剩余长度小于0
            throw new CommonException("pm.pm_pro_cont_060", requestDTO.getRemainSlabLen().toPlainString() , yieldRespose.getPreCleanSlabLen().toPlainString()); 
        }
        List<PmSlabSpecBt> pmSlabSpecBts = pmSlabSpecBtRepository.selectBy(pmProContYield.getTenantId(),
                Utils.mscLineNoToRoolLineNo(requestDTO.getMscLineNo()), requestDTO.getCcType(),
                requestDTO.getFurType(), requestDTO.getMoldThick(), requestDTO.getMoldWidth());
        if(pmSlabSpecBts == null || pmSlabSpecBts.isEmpty()) {
            throw new CommonException("pm.pm_slab_spec_bt_001",
                    Utils.mscLineNoToRoolLineNo(requestDTO.getMscLineNo()) , requestDTO.getMoldThick().toPlainString() ,requestDTO.getMoldWidth().toPlainString()
                    ,remainSlabLen.toPlainString() , requestDTO.getCcType(),
                    requestDTO.getFurType());
        }
        BigDecimal minInfurLen = null;
        for (PmSlabSpecBt pmSlabSpecBt : pmSlabSpecBts) {
            if(minInfurLen == null || minInfurLen.compareTo(pmSlabSpecBt.getSlabMinLen()) >= 0) {
                minInfurLen = pmSlabSpecBt.getSlabMinLen();
            }
        }
        if(remainSlabLen.compareTo(minInfurLen) > 0) {
            // 剩余板坯长度大于进加热炉最小长度不用添加附加
            yieldRespose.setAddSlabFlag(BaseConstants.Flag.NO.longValue());
        } else {
            if(pmProContYield.isSecondaryDesignFlag()) {
                // 二火开坯，二次设计不需要考虑附加长。
                return newInfurSlabWtGram;
            }
//            if(pmProContYield.isSecondaryDesignFlag() && !BaseConstants.Flag.YES.toString().equals(pmProContYield.getPmPdSlab().getAddSlabFlag())) {
//                // 如果是二次设计，且不是最后一块附加坯，需要补齐
//                yieldRespose.setInfurSlabLen(minInfurLen);
//                yieldRespose.setPreCleanSlabLen(minInfurLen);
//                // 吨
//                yieldRespose.setInfurSlabWt(yieldRespose.getPreCleanSlabLen().multiply(waimAndTaimAndDensity)
//                        .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
//                // 吨
//                yieldRespose.setPreCleanSlabWt(yieldRespose.getPreCleanSlabLen().multiply(waimAndTaimAndDensity)
//                        .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
//                
//            }
            PmSlabScrapLenBt pmSlabScrapLenBt = pmSlabScrapLenBtRepository.selectCanScrapLen(
                    pmProContYield.getTenantId(), requestDTO.getRemainSlabLen(), requestDTO.getMoldThick(),
                    requestDTO.getMoldWidth(), requestDTO.getMscLineNo());
            if (pmSlabScrapLenBt == null || pmSlabScrapLenBt.getCanScrapLen() == null) {
                // 读取余材板坯充当采用判定基准表未找到记录！板坯厚度【mold_thick】，板坯宽度【mold_width】，板坯长度【remain_slab_len】
                throw new CommonException("pm.pm_slab_scrap_len_001", requestDTO.getMoldThick(),
                        requestDTO.getMoldWidth(), requestDTO.getRemainSlabLen());
            }
            if (!pmProContYield.isSecondaryDesignFlag() && remainSlabLen.compareTo(pmSlabScrapLenBt.getCanScrapLen()) > 0) {
                // 若剩余板坯长度大于允许切废长度，返回add_slab_flag =
                // -1，报错：板坯附加长度【剩余板坯长度】大于允许切废长度【CAN_SCRAP_LEN】，组板失败！
                throw new CommonException("pm.pm_slab_scrap_len_002", remainSlabLen,
                        pmSlabScrapLenBt.getCanScrapLen());
            }
            
            yieldRespose.setAddSlabFlag(BaseConstants.Flag.YES.longValue());
            yieldRespose.setInfurSlabLen(requestDTO.getRemainSlabLen());
            yieldRespose.setPreCleanSlabLen(requestDTO.getRemainSlabLen());
            // 吨
            yieldRespose.setInfurSlabWt(yieldRespose.getPreCleanSlabLen().multiply(waimAndTaimAndDensity)
                    .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
            // 吨
            yieldRespose.setPreCleanSlabWt(yieldRespose.getPreCleanSlabLen().multiply(waimAndTaimAndDensity)
                    .divide(PmPlateConstants.TEN_GRAM_TO_TON, PmPlateConstants.WT_SCALE, RoundingMode.HALF_UP));
            
        }
        return newInfurSlabWtGram;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public PmPdSlab generateResult(PmProContYieldRequestDTO pmProContYield,
            Map<String, PmProContFilterResposeDTO> filterOrderMap, PmCompositeSlabCalcDTO pmCompositeSlabCalc,
            PmProContYieldResposeDTO pmProContYieldRespose, PmContMscTech axisPmContMscTech,
            PmProContProdTtr axisPmProContProdTtr) {
        // 如果是余材板坯组板，更新组板板坯表
        return pmPdSlabService.updatePmPdSlab(pmProContYield, axisPmContMscTech, axisPmProContProdTtr, filterOrderMap,
                pmProContYieldRespose, pmCompositeSlabCalc);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void generateCmd(PmPdSlab matParam, List<PmPdContSum> pmPdContSums, List<PmSmallPltPos> pmSmallPltPoss,
            String mscLineNo) {
        // 余坯组板执行时不生成命令表数据，分割确认时生成
    }

    @Override
    public void yiledCalcBeforeCheck(PmProContYieldRequestDTO pmProContYield, boolean isExec) {
        pmProContYield.getPmContFilterRequest().check();
//        if (StringUtils.isBlank(pmProContYield.getVirtualSlabNo())) {
//            throw new CommonException("pm.pm_pro_cont_039");
//        }
        // 如果材料设计类型是S，增加板坯状态校验
        pmPdSlabService.yiledCalcBeforeCheck(pmProContYield , isExec);
    }

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

    @Override
    protected String getSlabNo(PmProContYieldRequestDTO pmProContYield) {
        // 余坯组板的钢板号取分割信息的钢板号
        return pmProContYield.getPmPdSlab() == null ? "" : pmProContYield.getPmPdSlab().getSlabNo();
    }

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

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

    @Override
    protected PdEventType pdFinshedEventType() {
        return PdEventType.HP10;
    }
    
    @Override
    public void ctEventRec(PmPdSlab t, List<PmSmallPltPos> pmSmallPltPoss,
            Map<String, PmProContFilterResposeDTO> filterOrderMap, PmProContYieldRequestDTO pmProContYield,
            PmCompositeSlabCalcDTO pmCompositeSlabCalc, PmProContYieldResposeDTO pmProContYieldRespose) {
        if(pmProContYield.isSlabAutoAddSplitFlag()) {
            PmPdSlabSplitRequestDTO splitInfoRequest = new PmPdSlabSplitRequestDTO();
            splitInfoRequest.setTenantId(pmProContYield.getTenantId());
            splitInfoRequest.setSlabNo(pmProContYield.getPmPdSlab().getSlabNo());
            pmPdSlabService.confirmSplitInfoBySlabNo(splitInfoRequest);
        }
        super.ctEventRec(t, pmSmallPltPoss, filterOrderMap, pmProContYield, pmCompositeSlabCalc, pmProContYieldRespose);
    }

    @Override
    protected Long ctEventBoardPpFlag(PmPdSlab t, PmProContYieldRequestDTO pmProContYield) {
        return 0L;
    }

    @Override
    public List<PmBoardMatInfoDTO> selectBoardMatByOrderNo(String orderNo, Long organizationId) {
        MmSlab queryParam = new MmSlab();
        queryParam.setTenantId(organizationId);
//        queryParam.setOrderNo(orderNo);
        queryParam.setRecSts("2");
        queryParam.setSlabStatusList(Lists.newArrayList("29"));
        List<MmSlab> mmSlabs = mmSlabRepository.selectList(queryParam);
        List<PmBoardMatInfoDTO> pmBoardMatInfoDTOs = new ArrayList<PmBoardMatInfoDTO>();

        List<MmSlab> filterMmSlabList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(mmSlabs)) {
            filterMmSlabList = mmSlabs.stream().filter(item -> StringUtils.isNotEmpty(item.getSubBacklogCode()) &&
                            StringUtils.isNotBlank(item.getSubBacklogCode()) && !item.getSubBacklogCode().substring(1, 3).equals("B2") &&
                            !item.getSubBacklogCode().substring(1, 3).equals( "B3") &&
                            !"4".equals(item.getComplexDecideCode()) &&
                            StringUtils.isNotEmpty(item.getSlabNo()) &&
                            !item.getSlabNo().substring(0, 1).equals( "F") && !item.getSlabNo().substring(0, 1) .equals( "E") &&
                            StringUtils.isNotEmpty(item.getPono()) && StringUtils.isNotEmpty(item.getStNo()) &&
                            item.getSlabActThk() != null && item.getSlabActThk().compareTo(PmPlateConstants.ACCURACY) > 0 &&
                            item.getSlabActWid() != null && item.getSlabActWid().compareTo(PmPlateConstants.ACCURACY) > 0 &&
                            item.getSlabActLen() != null && item.getSlabActLen().compareTo(PmPlateConstants.ACCURACY) > 0 &&
                            ((item.getSlabActWgt() != null && item.getSlabActWgt().compareTo(PmPlateConstants.ACCURACY) > 0) ||
                                    (item.getSlabGrossWgt() != null && item.getSlabGrossWgt().compareTo(PmPlateConstants.ACCURACY) > 0)))
                    .collect(Collectors.toList());
        }

        PmProContProdTtr record = new PmProContProdTtr();
        record.setOrderNo(orderNo);
        record = this.pmProContProdTtrRepository.selectOne(record);

        if (record == null) {
            return new ArrayList<>();
        }

        PmContMscTech pmContMscTech = new PmContMscTech();
        pmContMscTech.setOrderNo(orderNo);
        List<PmContMscTech> pmContMscTechs = this.pmContMscTechRepository.select(pmContMscTech);
        if (pmContMscTechs == null || pmContMscTechs.isEmpty()) {
            return new ArrayList<>();
        }
        Map<String, PmContMscTech> pmContMscTechMap = Maps.uniqueIndex(pmContMscTechs, PmContMscTech::getMscLineNo);
        List<String> rollLineNoList = new ArrayList<>();
        for (MmSlab mmSlab : filterMmSlabList) {
            if (pmContMscTechMap.containsKey(mmSlab.getMscLineNo())) {
                rollLineNoList.add(Utils.mscLineNoToRoolLineNo(mmSlab.getMscLineNo()));
            }
        }
        if(rollLineNoList == null || rollLineNoList.isEmpty()) {
            return new ArrayList<>();
        }
        List<PmSlabSpecBt> pmSlabSpecBts = pmSlabSpecBtRepository
                .selectByCondition(Condition.builder(PmSlabSpecBt.class)
                        .andWhere(Sqls.custom().andIn(PmSlabSpecBt.FIELD_ROLL_LINE_NO, rollLineNoList)
                                .andEqualTo(PmSlabSpecBt.FIELD_TENANT_ID, organizationId)
                                .andEqualTo(PmSlabSpecBt.FIELD_CC_TYPE, CcType.TYPE_1))
                        .build());
        if(pmSlabSpecBts == null || pmSlabSpecBts.isEmpty()) {
            return new ArrayList<>();
        }
        Map<String , List<PmSlabSpecBt>> pmSlabSpecBtMap = pmSlabSpecBts.stream().collect(Collectors.groupingBy(PmSlabSpecBt::getRollLineNo));
        
        for (MmSlab mmSlab : filterMmSlabList) {
            if (!pmContMscTechMap.containsKey(mmSlab.getMscLineNo())) {
                continue ;
            }
            
            PmProContFilterResposeDTO pmProContFilterResposeDTO = BeanUtils.copy(record, PmProContFilterResposeDTO.class);
            pmProContFilterResposeDTO = BeanUtils.copy(pmContMscTechMap.get(mmSlab.getMscLineNo()), pmProContFilterResposeDTO);
            String rollLineNo = Utils.mscLineNoToRoolLineNo(mmSlab.getMscLineNo());
            PmProContFilterRequestDTO pmProContFilterRequest = new PmProContFilterRequestDTO();
            pmProContFilterRequest.setTenantId(organizationId);
            pmProContFilterRequest.setCcType(CcType.TYPE_1);
            pmProContFilterRequest.setFurType(BaseConstants.Flag.YES.toString());
            pmProContFilterRequest.setStNo(mmSlab.getStNo());
            pmProContFilterRequest.setRollDirectCode(pmProContFilterResposeDTO.getRollDirectCode()); 
            pmProContFilterRequest.setMatDesignKind(PdMatType.S.getCode());
            pmProContFilterRequest.setMoldThick(mmSlab.getSlabActThk());
            pmProContFilterRequest.setMoldWidth(mmSlab.getSlabActWid());
            if(!contFilterRuleSetCheck(pmProContFilterRequest , pmProContFilterResposeDTO, pmSlabSpecBtMap.get(rollLineNo), null)) {
                continue;
            }

            PmBoardMatInfoDTO pmBoardMatInfoDTO = new PmBoardMatInfoDTO();
            pmBoardMatInfoDTO.setSlabId(mmSlab.getSlabId());
            pmBoardMatInfoDTO.setSlabNo(mmSlab.getSlabNo());
            pmBoardMatInfoDTO.setMatActLen(mmSlab.getSlabActLen());
            pmBoardMatInfoDTO.setMatActThick(mmSlab.getSlabActThk());
            pmBoardMatInfoDTO.setMatActWidth(mmSlab.getSlabActWid());
            pmBoardMatInfoDTO.setMatActLength(mmSlab.getSlabActLen());
            pmBoardMatInfoDTO.setSgSign(mmSlab.getSgSign());
            pmBoardMatInfoDTO.setStNo(mmSlab.getStNo());
            pmBoardMatInfoDTO.setMatStatus(mmSlab.getSlabStatus());
            pmBoardMatInfoDTO.setHoldCauseCode(mmSlab.getHoldCauseCode());
            pmBoardMatInfoDTO.setMscLineNo(mmSlab.getMscLineNo());
            pmBoardMatInfoDTOs.add(pmBoardMatInfoDTO);
        }
        return pmBoardMatInfoDTOs;
    }

    @Override
    protected Long getSmallPosPpFlag() {
        return PdProdFlag.SMALL_POS_VIRTUAL_SLAB.getCode();
    }

    @Override
    protected String getWholeBacklogCode(PmProContFilterResposeDTO axisPmProCont, PmProContYieldRequestDTO pmProContYield) {
//        MmSlab mmSlab = mmSlabRepository.selectBySlabNo(pmProContYield.getPmPdSlab().getSlabNo(), pmProContYield.getTenantId());
//        return mmSlab == null ? null : mmSlab.getWholeBacklogCode();
        return Constants.WholeBacklogCode.CODE_13;
    }

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

    @Override
    protected String getMoMatNo(String pdMatNo, PmProContYieldRequestDTO pmProContYield) {
        return this.getSlabNo(pmProContYield);
    }

    @Override
    protected String getProcCd() {
//        return Constants.ProdcCd.CD_P1A;
        return Constants.ProdcCd.CD_13B;
    }

    @Override
    protected void checkCommandYield(PmProContYieldResposeDTO yieldRespose, PmProContYieldRequestDTO pmProContYield) {
        // 出钢材组板、余坯组板非指定长时，校验：若命令收得率小于限制收得率LIMIT_YIELD，返回消息：命令收得率【命令收得率】小于限制收得率【限制收得率】，不能组板！
        Long flag = 0L;
        if (flag.equals(pmProContYield.getPmContFilterRequest().getFixSlabLenFlag()) && pmProContYield.getPmContFilterRequest().getLimitYield() != null
                && pmProContYield.getPmContFilterRequest().getLimitYield().compareTo(yieldRespose.getCommandYield()) > 0) {
            throw new CommonException("pm.pm_pro_cont_066", yieldRespose.getCommandYield(), pmProContYield.getPmContFilterRequest().getLimitYield());
        }
    }
}
