package com.ruoyi.web.service;

import com.google.common.collect.Lists;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.mxjb.domain.MultipleWayData;
import com.ruoyi.mxjb.domain.MxjbMultipleWayAsset;
import com.ruoyi.mxjb.domain.MxjbMultipleWayCond;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class MultipleWayDecider {
    private MultipleWayData.MultipleWayForm f;
    private Map<String, List<MxjbMultipleWayCond>> cMap;
    private Map<String, List<String>> dictTypeMap;
    private Map<String, Map<String, String>> dictTypeValueMap;

    public MultipleWayDecider(MultipleWayData.MultipleWayForm form, Map<String, List<MxjbMultipleWayCond>> dcsMap,
                              Map<String, List<String>> dictTypeMap, Map<String, Map<String, String>> dictTypeValueMap) {
        this.f = form;
        this.cMap = dcsMap;
        this.dictTypeMap = dictTypeMap;
        this.dictTypeValueMap = dictTypeValueMap;
    }

    public List<MultipleWayData.MultipleWayConclusion> determine() {
        List<MultipleWayData.MultipleWayConclusion> conclusionList = Lists.newArrayList();
        for (String d : cMap.keySet()) {
            MultipleWayData.MultipleWayConclusion conclusion = new MultipleWayData.MultipleWayConclusion();
            conclusion.setDirection(d);
            conclusion.setValid(true);
            for (MxjbMultipleWayCond c : cMap.get(d)) {
                MxjbMultipleWayAsset a = c.getAsset();
                if (null == a) {
                    continue;
                }
                if (StringUtils.isNotBlank(c.getSex()) && !checkStringEqual(f.getGender(), c.getSex())) { // 当有辅助性别时需要先检查性别
                    continue;
                }
                if (StringUtils.isNotBlank(c.getAge()) && !checkNumberRangeClosed(new Double(f.getAge()), null, "值", c.getAge())) { // 当有辅助年龄时需要先检查性别
                    continue;
                }
                if (StringUtils.isNotBlank(c.getLocation()) && !f.getProvinceName().contains(c.getLocation())) { // 当有辅助省份时先检查省份
                    continue;
                }
                Boolean bFlag = null;
                if("户籍".equals(a.getType())) {
                    String[] ns = a.getProperty().split(",");
                    bFlag = checkInterest(getStringValue(f, ns[ns.length - 1]), getOptionList(a.getOptionType(), getOptionValue(c, a)), 1);
                } else if("体质指数".equals(a.getType())) {
                    bFlag = checkNumberRangeClosed(calculateBMI(), null, a.getOptionType(), getOptionValue(c, a));
                } else if("标准体重".equals(a.getType())) {
                    Double stdWeight = calculateStdWeight();
                    bFlag = checkNumberRangeClosed(getDoubleValue(f, a.getProperty()), stdWeight,  a.getOptionType(), getOptionValue(c, a));
                } else if("视力".equals(a.getType())) {
                    boolean flag = true;
                    String ov = getOptionValue(c, a);
                    for (String n : a.getProperty().split(",")) {
                        if(!(flag = checkNumberRangeClosed(getDoubleValue(f, n), null,  a.getOptionType(), ov))) {
                            break;
                        }
                    }
                    bFlag = flag;
                } else {
                    switch (a.getOp()) { // range, eq, in, interest
                        case  "range":
                            bFlag = checkNumberRangeClosed(getDoubleValue(f, a.getProperty()), null, a.getOptionType(), getOptionValue(c, a));
                            break;
                        case "eq":
                            bFlag = checkStringEqual(getStringValue(f, a.getProperty()), getOptionValue(a.getOptionType(), getOptionValue(c, a)));
                            break;
                        case "interest" :
                            bFlag = checkInterest(getStringValue(f, a.getProperty()), getOptionList(a.getOptionType(), getOptionValue(c, a)), 1);
                            break;
                        case "numrange" :
                            bFlag = checkNumberInterestClosed(getStringValue(f, a.getProperty()), a.getOptionType(), getOptionValue(c, a));
                        default:
                    }
                }
                if (null == bFlag) {
                    bFlag = true;
                } else if (c.getReverse() > 0L) {
                    bFlag = !bFlag;
                }
                if (!bFlag) {
                    conclusion.setValid(false);
                    // -- 您的<成绩>不符合<军校、军医>的报考条件
                    conclusion.getFailures().add(getMessage(c, a, a.getType() , c.getDirection()));
                }
            }
            // -- 您符合<边防子女预科班>的报考条件
            if (conclusion.getValid()) {
                conclusion.getFailures().add("您符合" + conclusion.getDirection() + "的报考条件");
            }
            conclusionList.add(conclusion);
        }
        return conclusionList;
    }

    private boolean checkStringEqual(String input, String config) {
        return config.equals(input);
    }

    private boolean checkInterest(String input, List<String> values, Integer minCount) {
        if (CollectionUtils.isEmpty(values)) { // 无配置则无限制
            return true;
        }
        Integer matchCount = CollectionUtils.intersection(Arrays.asList(input.split(",")), values).size();
        return matchCount >= minCount;
    }


    private boolean checkNumberInterestClosed(String value, String optionType, String optionValue) {
        // multiple_way_fee_expectable   元以下 ， 无以上 千
        // multiple_way_score  600分及以上， 200分以下
        Double[] r1 = getOptionRange(null, optionType, value); // 实际范围
        Double[] r2 = getOptionRange(null, optionType, optionValue); // 目标范围
        return (null == r1[0] || inRange(r1[0], r2[0], r2[1])) && (null == r1[1] || inRange(r1[1], r2[0], r2[1]));
    }

    // 检查是否在指定区间
    private boolean checkNumberRangeClosed(Double value, Double scale, String optionType, String optionValue) {
        // multiple_way_fee_expectable   元以下 ， 无以上 千
        // multiple_way_score  600分及以上， 200分以下
        Double[] range = getOptionRange(scale, optionType, optionValue);
        return (null == range[0] || value >= range[0]) && (null == range[1] || value <= range[1]);
    }

    private boolean inRange(Double v, Double min, Double max) {
        return (null == min || v >= min) && (null == max || v <= max);
    }

    private Double[] getOptionRange(Double scale, String optionType, String optionValue) {
        Double min = null, max = null;
        if ("字典值".equals(optionType)) {
            boolean downFlag = optionValue.contains("下");
            String[] vals = optionValue.replaceAll("及|以|上|下|分|元", "").replaceAll("万", "0000").replaceAll("千", "000").split("-");
            if (vals.length == 1) {
                if (downFlag) {
                    max = NumberUtils.toDouble(vals[0], 0.0);
                } else {
                    min = NumberUtils.toDouble(vals[0], 0.0);
                }
            } else {
                min = NumberUtils.toDouble(vals[0], 0.0);
                max = NumberUtils.toDouble(vals[1], 0.0);
            }
        } else {
            String[] vals = optionValue.split("-");
            min = StringUtils.isNotBlank(vals[0]) ? Double.parseDouble(vals[0]) : null;
            max = vals.length > 1 ? Double.parseDouble(vals[1]) : null;
        }
        if(null != scale) {
            if (null != min) {
                min *= scale;
            }
            if (null != max) {
                max *= scale;
            }
        }
        return new Double[] {min, max};
    }
    private List<String> getOptionList(String optionType, String optionValue) {
        if ("字典".equals(optionType) || "配置".equals(optionType)) {
            return dictTypeMap.get(optionValue);
        }
        return Arrays.asList(optionValue.split(","));
    }
    private String getOptionValue(String optionType, String optionValue) {
        if ("字典值".equals(optionType)) {
            return optionValue;
        }
        return optionValue;
    }

    private String getStringValue(Object f, String name) {
        String v = null;
        try {
            v = BeanUtils.getProperty(f, name);
            return v;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
    private Double getDoubleValue(Object f, String name) {
        String v = getStringValue(f, name);
        return NumberUtils.toDouble(v, 0.0);
    }

    public static String getOptionValue(MxjbMultipleWayCond c, MxjbMultipleWayAsset a) {
         return null == c.getOptionValues() ? a.getOptionValues() : c.getOptionValues();
    }

    private String getMessage(MxjbMultipleWayCond c, MxjbMultipleWayAsset a, String type, String direction) {
        if (null != c.getMessage()) {
            return c.getMessage();
        } else if (null != a.getMessage()) {
            return a.getMessage();
        }
        return "您的" + type + "不符合" + direction + "的报考条件";
    }

    /**
     * 计算标准体重，及上下限值
     * @return
     */
    private double calculateStdWeight() { // 体重(kg)/身高(M)2
        Integer stdWeight = f.getHeight() - 110;
        return stdWeight * 1.0;
    }

    /**
     * 计算 BMI 指数
     * @return
     */
    private double calculateBMI() { // 体重(kg)/身高(M)2
        double height = f.getHeight() / 100.0;
        return f.getWeight() * 1.0 / (height * height);
    }
}
