package com.ruoyi.web.service;

import com.google.common.collect.Maps;
import com.ruoyi.web.domain.VoluntaryDto;
import lombok.Data;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Service;

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

/**
 *  冲、稳、保概率判定
 *  冲：[0%, 70%], 稳：(70%, 85%), 保：[85%, 99%]
 */
@Service
public class EnrollRateCalculator {
    private Map<Long, List<RateLevel>> rateRangesMap = Maps.newHashMap();
    private Integer enrollLineRate, enrollRateStep;

    public EnrollRateCalculator() {
        clean();
    }

    public void clean() {
        rateRangesMap.clear();
        enrollLineRate = 65;
        enrollRateStep = 5;
    }

    public RateLevel calSchoolEnrollRate(Integer scoreTotal, Double enrollMinScore, Double score) {
        if (null == enrollMinScore || null == score) {
            return null;
        }
        Long maxScore = Math.round(enrollMinScore * 1.1);
        Long minScore = Math.round(enrollMinScore * 0.9);
        if (score >= maxScore) {
            return new RateLevel(98, VoluntaryDto.EnumPickType.Safety);
        } else if (score <= minScore) {
            return new RateLevel(2, VoluntaryDto.EnumPickType.Danger);
        }
        Double userRate = 2 + (Math.round(score) - minScore) * 96.0 / (maxScore - minScore);
        if (userRate >= 80) {
            return new RateLevel(userRate.intValue(), VoluntaryDto.EnumPickType.Safety);
        } else if (userRate >= 60) {
            return new RateLevel(userRate.intValue(), VoluntaryDto.EnumPickType.Normal);
        } else if (userRate < 40) {
            return new RateLevel(userRate.intValue(), VoluntaryDto.EnumPickType.Danger);
        }
        return new RateLevel(userRate.intValue(), VoluntaryDto.EnumPickType.Danger);
    }

    /**
     * 查找合适的层次
     * @param scoreTotal
     * @param enrollLine
     * @param score
     * @return
     */
    public RateLevel satisfy(Integer scoreTotal, Double enrollLine, Double score) {
        List<RateLevel> rateLevels = init(scoreTotal, enrollLine);
        int intScore = (int) Math.round(score);
        for (int i = 0; i < rateLevels.size(); i++) {
            RateLevel l = rateLevels.get(i);
            if ((intScore > l.min || intScore == l.min && l.incMin) && (intScore < l.max || intScore == l.max && l.incMax)) {
                return l;
            }
        }
        return null;
    }

    /**
     * 初始化缓存
     * @param scoreTotal
     * @param fEnrollLine
     * @return
     */
    public List<RateLevel> init(Integer scoreTotal, Double fEnrollLine) {
        int enrollLine = (int) Math.round(fEnrollLine);
        Long key = scoreTotal * 1000L + enrollLine;
        List<RateLevel> rateLevels = rateRangesMap.get(key);
        if (null == rateLevels) {
            rateLevels = Lists.newArrayList();
            rateRangesMap.put(key, rateLevels);

            // 65%以上的计算
            Integer floatMax = (scoreTotal - enrollLine) / 2; // 向上取分数线与总分间的一半
            Integer aboveSteps = (100 - enrollLineRate + enrollRateStep - 1) / enrollRateStep;
            Integer aboveSize = floatMax / aboveSteps;
            rateLevels.add(new RateLevel(enrollLineRate, enrollLine, enrollLine, true, true)); // 半开半闭区间
            for (int step = 0; step < aboveSteps; step++) {
                Integer min = enrollLine + step * aboveSize;
                Integer max = min + aboveSize;
                Integer rate = enrollLineRate + (step + 1) * enrollRateStep;
                if(rate >= 99) {
                    rateLevels.add(new RateLevel(99, min, 99999, false, false)); // 上限99%不封顶
                    break;
                } else {
                    rateLevels.add(new RateLevel(rate, min, max, false, true)); // 半开半闭区间
                }
            }
            // 65%以下的计算 66
            Integer belowSteps = (enrollLineRate + enrollRateStep - 1) / enrollRateStep;
            Integer belowSize = enrollLine / belowSteps;
            RateLevel range;
            for (int step = 0; step < belowSteps; step++) {
                Integer max = enrollLine - step * belowSize;
                Integer rate = enrollLineRate - (step + 1) * enrollRateStep;
                if(rate <= 0) {
                    rateLevels.add(new RateLevel(0, 0, max, true, false));
                    break;
                } else {
                    rateLevels.add(new RateLevel(rate, max - belowSize, max, true, false));
                }
            }
        }
        return rateLevels;
    }

    @Data
    public static class RateLevel {
        int rate;
        int min;
        int max;
        boolean incMin;
        boolean incMax;
        VoluntaryDto.EnumPickType type; // 冲/稳/宝
        String typeLabel; //

        public RateLevel(int rate, Integer min, Integer max, boolean incMin, boolean incMax) {
            this.rate = rate;
            this.min = min;
            this.max = max;
            this.incMin = incMin;
            this.incMax = incMax;
            // [0%, 70%], 稳：(70%, 85%), 保：[85%, 99%]
            if (rate <= 70) {
                type = VoluntaryDto.EnumPickType.Danger;
                typeLabel = "风险极高";
            } else if (rate < 85) {
                type = VoluntaryDto.EnumPickType.Normal;
                typeLabel = "风险中等";
            } else {
                type = VoluntaryDto.EnumPickType.Safety;
                typeLabel = "风险较低";
            }
        }

        public RateLevel(int rate, VoluntaryDto.EnumPickType type) {
            this.rate = rate;
            this.type = type;
            if(type.equals(VoluntaryDto.EnumPickType.Danger)) {
                typeLabel = "风险极高";
            } else if(type.equals(VoluntaryDto.EnumPickType.Normal)) {
                typeLabel = "风险中等";
            } else if(type.equals(VoluntaryDto.EnumPickType.Safety)) {
                typeLabel = "风险较低";
            } else {
                typeLabel = "";
            }
        }
    }
}
