package com.xisc.pm.app.service.autoboard.core;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.Callable;

import org.apache.commons.lang.StringUtils;

import com.xisc.pm.app.service.autoboard.config.CalcLog;
import com.xisc.pm.app.service.autoboard.dto.PmAutoBoardExecParam;
import com.xisc.pm.app.service.autoboard.dto.PmAutoBoardExecResult;
import com.xisc.pm.app.service.autoboard.dto.PmAutoBoardPolicyGroupDTO;
import com.xisc.pm.app.service.autoboard.dto.PmAutoPdLog;
import com.xisc.pm.app.service.autoboard.dto.PmContBoardParam;
import com.xisc.pm.app.service.autoboard.dto.PmOrderGroupDTO;
import com.xisc.pm.infra.enums.LineNoEnums;
import com.xisc.pm.infra.util.Utils;

import io.choerodon.core.exception.CommonException;

/**
 * 组板版本执行
 * 
 * @author jun.zeng
 *
 */
public class PmAutoBoardExecCallable implements Callable<PmAutoBoardExecResult> {

    // 断面执行顺序链表
    private PmAutoBoardExecParam execParam;

    // 初始化待处理的已收池合同数据
    private Map<String, PmContBoardParam> orignData;

    // 策略组
    private PmAutoBoardPolicyGroupDTO policyGroup;

    private int version = 0;
    
    // 执行结束计数标记，从零开始计算，表示执行N+1次
    private Integer execFinshedCountFlag = 3;

    public PmAutoBoardExecCallable(PmAutoBoardExecParam execParam, Map<String, PmContBoardParam> orignData,
            PmAutoBoardPolicyGroupDTO policyGroup, int version) {
        this.execParam = execParam;
        this.orignData = orignData;
        this.policyGroup = policyGroup;
        this.version = version;
    }

    /**
     * 版本计算会执行多次
     *  第一次，轧制产线号不为30，合同单重大于6.8的小板优先跑五米
     *  第二次，所有合同所有产线全部执行
     *  第三次，如果支持转置，会进行转置校验
     *  第四次，S型增强计算，支持S1，S2，S3，S7
     */
    @Override
    public PmAutoBoardExecResult call() throws Exception {
        CalcLog.setVersion(version);
        try {
            PmAutoBoardExecResult execResult = new PmAutoBoardExecResult();
            execResult.setVersion(version);
            int i = 0 ;
            do {
                Set<String> execFlag = new HashSet<>();
                PmAutoBoardExecParam current = execParam;
                while (current != null && !execFlag.contains(current.getMoldId())) {
                    LineNoEnums lineNoEnums = LineNoEnums.getByMscLineNo(current.getMscLineNo()) ;
                    if(i > 0) {
                        current.setSecondTimeFlag(true);
                    }
                    if(i > 1 && policyGroup.getInvertedFlag() != null && policyGroup.getInvertedFlag() == 1L
                            && ("10".equals(lineNoEnums.getRollLineNo()) 
                                    || "20".equals(lineNoEnums.getRollLineNo()))) {
                        current.setTransflag(true);
                    }
                    if(i > 2) {
                        CalcLog.openLog();
                        current.setSPlusEnable(true);
                    }
                    // 防止循环执行
                    execFlag.add(current.getMoldId());
                    CalcLog.log("断面【"+ current.getMoldId() +"】开始执行");
                    // 根据断面筛选有效合同
                    List<PmContBoardParam> orderList = filterOrderList(current, policyGroup, orignData, execResult);
                    if(!execResult.judgeMscLineSumWt(current , null)) {
                        CalcLog.log("断面【"+ current.getMoldId() +"】执行完成");
                        continue;
                    };
                    // 开始组板
                    b: for (String formModeCode : current.getPolicy().getFormModeCodeList()) {
                        // 策略排序列表
                        List<PmOrderGroupDTO> orderGroupDTOs = policyGroup.getOrderGroup();
    
                        p: for (PmOrderGroupDTO orderGroup : orderGroupDTOs) {
                            PolicyGroupProgram program = PolicyGroupProgram
                                    .build(formModeCode, orderGroup.getGroupDimenSion() ,orderGroup.getGroupSeq() , policyGroup, current)
                                    .appendData(orderList, execResult);
                            // 获取组板队列
                            Map<String, Queue<PmContBoardParam>> queueGroup = program.getQueue();
                            for (Entry<String, Queue<PmContBoardParam>> entry : queueGroup.entrySet()) {
                                Queue<PmContBoardParam> priorityQueue = entry.getValue();
                                DefaultPmPdProgram executor = PmPdProgramFactory.build(priorityQueue, execResult, current,
                                        policyGroup, formModeCode);
                                if(executor != null) {
                                    // 暂时采用同步方式，如果是异步需要调整执行结果参数，结果记录需要为线程安全的。
                                    boolean canExecFlag = executor.call();
                                    if(!canExecFlag) {
                                        // 如果断面
                                        break b;
                                    }
    //                                CalcLog.log("组板方式:" + formModeCode + " 分组优先级：" + orderGroup.getPriority().toString() + " 执行完成");
                                } else {
                                    CalcLog.log("不支持该组板方式：" + formModeCode);
                                    break p;
                                }
                            }
                        }
                    }
                    CalcLog.log("断面【"+ current.getMoldId() +"】执行完成");
                    current = current.getNextNode();
                }
            } while(i++ < execFinshedCountFlag);
            CalcLog.log("版本执行完成" + execResult.getBoardSummaryData());
            execResult.setEndTime(System.currentTimeMillis());
            return execResult;
        } finally {
            CalcLog.clear();
            CalcLog.closeLog();
        }
    }

    /**
     * 通过断面筛选合同数据
     * 
     * @param current     当前执行的断面
     * @param policyGroup 策略组
     * @param orignData   原始数据
     * @param execResult  组板结果
     * @return
     */
    private List<PmContBoardParam> filterOrderList(PmAutoBoardExecParam current, PmAutoBoardPolicyGroupDTO policyGroup,
            Map<String, PmContBoardParam> orignData, PmAutoBoardExecResult execResult) {
        List<PmContBoardParam> filterList = new ArrayList<PmContBoardParam>();
        Map<String, BigDecimal> summaryStRemainingWt = new HashMap<String, BigDecimal>();
        Map<String, List<PmContBoardParam>> stTmpFilterList = new HashMap<String, List<PmContBoardParam>>();

        for (Entry<String, PmContBoardParam> entry : orignData.entrySet()) {
            // 方便组板，讲取样方式为A的数据隔离，方便后续单独分组
            if (entry.getValue().getPickPatternCode() != null && "A".equals(entry.getValue().getPickPatternCode())) {
                entry.getValue().setPickPatternCodeType(entry.getValue().getOrderNo());
            } else {
                entry.getValue().setPickPatternCodeType("DefaultGroup");
            }
            if(StringUtils.isNotBlank(current.getPolicy().getConsignCust()) && !current.getPolicy().getConsignCust().equals(entry.getValue().getOrderCustCname())) {
                // 策略上维护了限制收货单位
                continue;
            }

            if (policyGroup.getRemainingPriorityFlag() != null
                    && policyGroup.getRemainingPriorityFlag().intValue() == 1) {

            }
            if (!entry.getValue().getMscLineNo().contains(current.getMscLineNo())) {
                // 匹配产线
                continue;
            }
            if(!current.isSecondTimeFlag()) {
                String rollLineNo = Utils.mscLineNoToRoolLineNo(current.getMscLineNo());
                if((!"30".equals(rollLineNo)) && entry.getValue().getOrderUnitAim().compareTo(BigDecimal.valueOf(6.8)) >= 0) {
                    // 如果轧制产线号不为30，合同单重大于6.8的小板优先跑五米
                    continue;
                }
            }
            // 剔除已组板合同
            BigDecimal qty = execResult.calcRemainingPdQty(entry.getValue());
            if (BigDecimal.ZERO.compareTo(qty) >= 0) {
                // 已经无欠量，不能继续参与组板
                continue;
            }
            // 判断合同指定产线
            if (policyGroup.getAssignMscLineMap().containsKey(entry.getKey())
                    && !policyGroup.getAssignMscLineMap().get(entry.getKey()).contains(current.getMscLineNo())) {
                continue;
            }
            // 判断合同指定断面
            if (policyGroup.getAssignMoldMap().containsKey(entry.getKey())
                    && !policyGroup.getAssignMoldMap().get(entry.getKey()).contains(current.getMoldId())) {
                // 如果合同存在指定断面配置，如果未维护当前断面。则不参与当前断面组板
                continue;
            }
            BigDecimal minThick = !entry.getValue().getSlabThkMinMap().containsKey(current.getMscLineNo())? null : 
                entry.getValue().getSlabThkMinMap().get(current.getMscLineNo());
            BigDecimal maxThick = !entry.getValue().getSlabThkMaxMap().containsKey(current.getMscLineNo()) ? null : 
                entry.getValue().getSlabThkMaxMap().get(current.getMscLineNo());
            if (null == current.getMoldThick() || (minThick != null && current.getMoldThick().compareTo(minThick) < 0) || (maxThick != null && current.getMoldThick().compareTo(maxThick) > 0)) {
                CalcLog.log(new PmAutoPdLog(current.getRequestId(), execResult.getVersion() , current.getMoldId()).setAxisOrderNo(entry.getValue().getOrderNo()).setMessage(String.format("合同【%s】的允许板坯厚度范围【%s】（合同要求）-【%s】（连铸板坯生产规格基准表）与指定板坯厚度【%s】不匹配。", entry.getValue().getOrderThick().toPlainString(), minThick.toPlainString(),
                        maxThick == null ? "" : maxThick.toPlainString(), current.getMoldThick() == null ? "" : current.getMoldThick().toPlainString())));
                continue;
            }
            // 剔除断面
            if (!current.judgeOrderSpec(entry.getValue())) {
                BigDecimal wt = qty.multiply(entry.getValue().getOrderUnitAim());
                // 合同不满足断面规格参数。剔除
                if (!summaryStRemainingWt.containsKey(entry.getValue().getStNo())) {
                    summaryStRemainingWt.put(entry.getValue().getStNo(), BigDecimal.ZERO);
                    stTmpFilterList.put(entry.getValue().getStNo(), new ArrayList<>());
                }
                summaryStRemainingWt.put(entry.getValue().getStNo(),
                        summaryStRemainingWt.getOrDefault(entry.getValue().getStNo(), BigDecimal.ZERO).add(wt));
                stTmpFilterList.get(entry.getValue().getStNo()).add(entry.getValue());
                continue;
            }
            filterList.add(entry.getValue());
        }
        for (Entry<String, List<PmContBoardParam>> entry : stTmpFilterList.entrySet()) {
            BigDecimal stRemainingWt = summaryStRemainingWt.getOrDefault(entry.getKey(), BigDecimal.ZERO);
            if (current.getSteelSpecificationWeight() != null
                    && current.getSteelSpecificationWeight().compareTo(stRemainingWt) <= 0) {
                // 若组内的ORDER_WT相加的和>STEEL_SPECIFICATION_WEIGHT（钢种规格重量）
                filterList.addAll(entry.getValue());
            }
        }
        return filterList;
    }

}
