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

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.function.Function;

import org.hzero.core.base.BaseConstants;

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.PmContBoardParam;
import com.xisc.pm.domain.entity.PmSdivStLimitBt;
import com.xisc.pm.infra.constant.PmPlateConstants;
import com.xisc.pm.infra.enums.FormPlateMode;
import com.xisc.pm.infra.util.Utils;

import io.choerodon.core.exception.CommonException;

/**
 * 策略分组程序
 * 
 * @author jun.zeng
 *
 */
public class PolicyGroupProgram {

    // 分组后的优先队列。
    private Map<String, Queue<PmContBoardParam>> priorityQueueMap = new HashMap<>();
    
    // 分钟后的优先队列- LinkedList 实现

    // 组板方式
    private FormPlateMode formPlateMode;

    // 排序规则
    private Comparator<PmContBoardParam> comparator;
    
    private PmAutoBoardPolicyGroupDTO policyGroup;
    
    private PmAutoBoardExecParam current;
    
    // 分组条件
    private List<Function<PmContBoardParam, Object>> functions = new ArrayList<Function<PmContBoardParam, Object>>();

    public enum GroupDimenSion {
        SAME_WID, SAME_CUSTOMER, SAME_ORDER_NO_ERP, SAME_DELIVY_PLACE_NAME, SAME_LEN
    }

    /**
     * 填充数据进行分组过滤
     * 
     * @param data       当前断面组板数据
     * @param execResult 组板执行结果
     */
    public PolicyGroupProgram appendData(List<PmContBoardParam> data, PmAutoBoardExecResult execResult) {
        p:for (PmContBoardParam pmContBoardParam : data) {
            // 1. 过滤数据
            // 剔除组板方式限制的合同
            if (FormPlateMode.S.getType().equals(formPlateMode.getType())) {
                if (pmContBoardParam.getPlanerFlag() != null
                        && BaseConstants.Flag.NO.toString().equals(pmContBoardParam.getPlanerFlag())) {
                    // 组板方式S 轧制倍尺标记可否不能为零
                    continue;
                }
                
                if (PmPlateConstants.CUT_MODE_S.equals(pmContBoardParam.getCutModeCodeMap().get(this.current.getMscLineNo()))) {
                    // 组板方式为S， 切断方式不能为S
                    continue;
                }
                if(BaseConstants.Flag.NO.toString().equals(pmContBoardParam.getPlanerFlag())){
                    // 组板方式为S，剖分标记不能为0
                    continue;
                };
                Map<String , List<PmSdivStLimitBt>> pmSdivStLimitBtMap = policyGroup.getPmSdivStLimitBtMap() ;
                if(pmSdivStLimitBtMap != null && pmSdivStLimitBtMap.containsKey(pmContBoardParam.getStNo())) {
                    List<PmSdivStLimitBt> pmSdivStLimitBts = pmSdivStLimitBtMap.get(pmContBoardParam.getStNo()) ;
                    for (PmSdivStLimitBt pmSdivStLimitBt : pmSdivStLimitBts) {
                        if(PmPlateConstants.ALL.equals(pmSdivStLimitBt.getSgSign()) || pmContBoardParam.getSgSign().equals(pmSdivStLimitBt.getSgSign())) {
                            if(current.getMoldThick().compareTo(pmSdivStLimitBt.getSlabThickFrom()) >= 0 
                                    && current.getMoldThick().compareTo(pmSdivStLimitBt.getSlabThickTo()) <= 0) {
                                if(current.getMoldWidth().compareTo(pmSdivStLimitBt.getSlabWidthFrom()) >= 0 
                                        && current.getMoldWidth().compareTo(pmSdivStLimitBt.getSlabWidthTo()) <= 0) {
                                    // S型组板满足部分组板现在不参与组板
                                    continue p;
                                }
                            }
                        }
                    }
                }
            }
            // 2. 分组
            String groupBy = getGroupKey(pmContBoardParam);
            if (!priorityQueueMap.containsKey(groupBy)) {
                LinkedList<PmContBoardParam> priorityQueue = new LinkedList<>();
                priorityQueueMap.put(groupBy, priorityQueue);
            }
            priorityQueueMap.get(groupBy).add(pmContBoardParam);
        }
        for (Entry<String, Queue<PmContBoardParam>> entry : priorityQueueMap.entrySet()) {
            LinkedList<PmContBoardParam> linkedList = (LinkedList<PmContBoardParam>)entry.getValue();
            linkedList.sort(comparator);
        }
        
        return this;
    }

    /**
     * 获取分组Key
     * 
     * @param pmContBoardParam
     * @return
     */
    public String getGroupKey(PmContBoardParam pmContBoardParam) {
        if (functions == null || functions.isEmpty()) {
            throw new CommonException("分组条件不能为空");
        }
        StringBuffer key = new StringBuffer();
        for (Function<PmContBoardParam, Object> function : functions) {
            Object obj = function.apply(pmContBoardParam);
            if(obj == null) {
                key.append("Null");
                key.append("-");
                continue;
            }
            if (obj instanceof BigDecimal) {
                key.append(((BigDecimal) obj).toPlainString());
            }else if (obj instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String , String> map = (Map) obj;
                String value = map.get(current.getMscLineNo());
                key.append(value == null ? "Null":value);
            } else {
                key.append(obj.toString());
            }
            key.append("-");
        }
        return key.toString();
    }
    
    /**
     * 部分规则集提前校验。
     */
    private void ruleSetGroupCondition(String formPlateMode , PmAutoBoardExecParam current) {
        FormPlateMode formPlateModeEnmu = FormPlateMode.getByCode(formPlateMode);
        // TODO 待完善
        // 合同厚度相同
        this.functions.add(PmContBoardParam::getOrderThick);
        // 合同钢种相同
        this.functions.add(PmContBoardParam::getStNo);
        // 26切断方式相同
        this.functions.add(PmContBoardParam::getCutModeCodeMap);
        // 32边部形态相等
        this.functions.add(PmContBoardParam::getProdSideMark);
        // 34全程热处理类型匹配
        this.functions.add(PmContBoardParam::getWholeHeatTreatmentTypeMap);
        // 35热处理母板单位取样的合同单独组板
        this.functions.add(PmContBoardParam::getPickPatternCodeType);
        // 见证项目一致
        this.functions.add(PmContBoardParam::getWitnessProNo);
        if(FormPlateMode.S.getType().equals(formPlateModeEnmu.getType())) {
            if(!current.isSPlusEnable()) {
                // S型组板必须同长
                this.functions.add(PmContBoardParam::getSmallPlateLen);
            }
        }
        if(FormPlateMode.A1.getCode().equals(formPlateModeEnmu.getCode())) {
            // S型组板必须同长
            this.functions.add(PmContBoardParam::getOrderWid);
        }
    }
    
    private static int cehckString(String value1 , String value2) {
        if(value1 == null && value2 == null) {
            return 0;
        }
        if(value1 == null) {
            return 1 ;
        } 
        if(value2 == null) {
            return -1 ;
        } 
        return value1.compareTo(value2);
    }

    public static PolicyGroupProgram build(String formPlateMode, List<String> groupByList,
            List<String> orderByList, PmAutoBoardPolicyGroupDTO policyGroup, PmAutoBoardExecParam current) {
        PolicyGroupProgram groupProgram = new PolicyGroupProgram(FormPlateMode.getByCode(formPlateMode) , policyGroup , current);
        groupProgram.ruleSetGroupCondition(formPlateMode , current);
        for (String groupBy : groupByList) {
            if (GroupDimenSion.SAME_WID.name().equals(groupBy)) {
                groupProgram.functions.add(PmContBoardParam::getOrderWid);
            } else if (GroupDimenSion.SAME_ORDER_NO_ERP.name().equals(groupBy)) {
                groupProgram.functions.add(PmContBoardParam::getErpOrderNo);
            } else if (GroupDimenSion.SAME_CUSTOMER.name().equals(groupBy)) {
                groupProgram.functions.add(PmContBoardParam::getOrderCustCname);
            } else if (GroupDimenSion.SAME_DELIVY_PLACE_NAME.name().equals(groupBy)) {
                groupProgram.functions.add(PmContBoardParam::getDelivyPlace);
            } else if(GroupDimenSion.SAME_LEN.name().equals(groupBy)) {
                groupProgram.functions.add(PmContBoardParam::getSmallPlateLen);
            }
        }
        // 尾量优先
        // 是否重要客户？
        // 交货期
        groupProgram.comparator = new Comparator<PmContBoardParam>() {

            @Override
            public int compare(PmContBoardParam o1, PmContBoardParam o2) {
                FormPlateMode formPlateModeEnmu = FormPlateMode.getByCode(formPlateMode);
                if(FormPlateMode.A2.getCode().equals(formPlateMode) || FormPlateMode.S.getType().equals(formPlateModeEnmu.getType())) {
                    int widFlag = o2.getOrderWid().compareTo(o1.getOrderWid());
                    if(widFlag != 0) {
                        return widFlag;
                    }
                }
                if(orderByList != null && !orderByList.isEmpty()) {
                    for (String orderBy : orderByList) {
                        if (GroupDimenSion.SAME_ORDER_NO_ERP.name().equals(orderBy)) {
                            int value = cehckString(o1.getErpOrderNo(),o2.getErpOrderNo());
                            if(value != 0) {
                                return value;
                            }
                        } else if (GroupDimenSion.SAME_CUSTOMER.name().equals(orderBy)) {
                            int value = cehckString(o1.getOrderCustCname(),o2.getOrderCustCname());
                            if(value != 0) {
                                return value;
                            }
                        } else if (GroupDimenSion.SAME_DELIVY_PLACE_NAME.name().equals(orderBy)) {
                            int value = cehckString(o1.getDelivyPlace(),o2.getDelivyPlace());
                            if(value != 0) {
                                return value;
                            }
                        }
                    }
                }
                if (policyGroup.getRemainingPriorityFlag() != null
                        && policyGroup.getRemainingPriorityFlag().intValue() == 1 && policyGroup.getContractAllowance() != null ) {
                    // 尾量优先
                    if (policyGroup.getSummaryContractNum() != null) {
                        BigDecimal contractNum1 = policyGroup.getSummaryContractNum()
                                .getOrDefault(Utils.removeLastThreeChars(o1.getOrderNo()), BigDecimal.ZERO);
                        BigDecimal contractNum2 = policyGroup.getSummaryContractNum()
                                .getOrDefault(Utils.removeLastThreeChars(o2.getOrderNo()), BigDecimal.ZERO);
                        int a = 0;
                        int b = 0;
                        if (contractNum1.compareTo(policyGroup.getContractAllowance()) <= 0) {
                            a = 1;
                        }
                        if (contractNum2.compareTo(policyGroup.getContractAllowance()) <= 0) {
                            b = 1;
                        }
                        if (a - b != 0) {
                            return a - b;
                        }
                    }
                }
                // 重要客户优先
                Long proority1 = current.getPolicy().getCustomerVip().getOrDefault(o1.getOrderCustCname(), 0L);
                Long proority2 = current.getPolicy().getCustomerVip().getOrDefault(o2.getOrderCustCname(), 0L);
                if (proority1.longValue() != proority2.longValue()) {
                    return proority1.compareTo(proority2);
                } else {
                    // 交期
                    return o2.getDelivyDate().compareTo(o1.getDelivyDate());
                }
                
            }
        };
        return groupProgram;
    }

    private PolicyGroupProgram(FormPlateMode formPlateMode ,PmAutoBoardPolicyGroupDTO policyGroup, PmAutoBoardExecParam current) {
        this.formPlateMode = formPlateMode;
        this.current = current;
        this.policyGroup = policyGroup;
    }

    /**
     * 获取分组结果
     * 
     * @return
     */
    public Map<String, Queue<PmContBoardParam>> getQueue() {
        return priorityQueueMap;
    }

}
