package com.ruoyi.web.service;

import com.google.common.collect.Maps;
import com.ruoyi.syzy.domain.BBusiWishLocationSubmits;
import com.ruoyi.syzy.domain.BBusiWishScoreGroups;
import com.ruoyi.syzy.mapper.BBusiWishLocationSubmitsMapper;
import com.ruoyi.syzy.mapper.BBusiWishScoreGroupsMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ScoreRankService {
    private BBusiWishScoreGroupsMapper wishScoreGroupsMapper;
    private BBusiWishLocationSubmitsMapper wishLocationSubmitsMapper;
    private Map<String, ScoreGroupCalculator> locationYearScoreGroupMapperMap = Maps.newHashMap(); // 一分一段
    private Map<String, Map<Integer, List<BBusiWishScoreGroups>>> locationYearBatchRangeMap = Maps.newHashMap(); // 批次表
    private Map<String, Integer[]> locationYearEqRateMap = Maps.newHashMap();

    public ScoreRankService(BBusiWishScoreGroupsMapper wishScoreGroupsMapper, BBusiWishLocationSubmitsMapper wishLocationSubmitsMapper) {
        this.wishScoreGroupsMapper = wishScoreGroupsMapper;
        this.wishLocationSubmitsMapper = wishLocationSubmitsMapper;
    }

    public void clean() {
        synchronized (locationYearScoreGroupMapperMap) {
            locationYearScoreGroupMapperMap.clear();
        }
        synchronized (locationYearScoreGroupMapperMap) {
            locationYearBatchRangeMap.clear();
        }
        synchronized (locationYearScoreGroupMapperMap) {
            locationYearEqRateMap.clear();
        }
    }

    // 分数转位次 mode=当年的文科/理科/物理/历史
    public Integer getRank(String location, Integer year, String mode, Integer score) {
        ScoreGroupCalculator sgMapper =  getScoreCalculatorMapper(location, year, mode);
        return sgMapper.getRank2(year, mode, score);
    }

    // 位次转分数 mode=当年的文科/理科/物理/历史
    public Integer getScore(String location, Integer year, String mode, Integer rank) {
        if (null == rank) {
            return null;
        }
        ScoreGroupCalculator sgMapper = getScoreCalculatorMapper(location, year, mode);
        return sgMapper.getScore(year, mode, rank);
    }

    public Integer getRankEqRank(String location, Integer maxYear, Integer currYear, String mode, Integer score, Integer rank, Integer eqYear) {
        BBusiWishScoreGroups currScoreRange = getScoreRangeByScore(location, maxYear, mode, currYear, score);
        Integer eqRank = null;
        if(null != currScoreRange) {
            BBusiWishScoreGroups eqScoreRange = getScoreRangeByBatch(location, currYear, currScoreRange.getMode(), eqYear, currScoreRange.getType());
            eqRank = getEqRank(currScoreRange, eqScoreRange, location, currYear, currYear, score, rank, eqYear);
        }
        return eqRank;
    }

    // 不需要等效分时使用
    public Integer getEqRank(String location, Integer maxYear, Integer currYear, String mode, Integer score, Integer eqYear) {
        BBusiWishScoreGroups currScoreRange = getScoreRangeByScore(location, maxYear, mode, currYear, score);
        Integer eqRank = null;
        if(null != currScoreRange) {
            BBusiWishScoreGroups eqScoreRange = getScoreRangeByBatch(location, currYear, currScoreRange.getMode(), eqYear, currScoreRange.getType());
            eqRank = getEqRank(currScoreRange, eqScoreRange, location, currYear, currYear, score, null, eqYear);
        }
        return eqRank;
    }
    // 需要等效分时使用，计算等效位次(rate = (matchRank - matchLow) / (matchHigh - matchLow),  tmpRank(year) = lowRankRank + (highRank - lowRank) * rate); 等效分(等效位数在当年对应的分值)
    public Integer getEqRank(BBusiWishScoreGroups currScoreRange, BBusiWishScoreGroups eqScoreRange, String location, Integer maxYear, Integer srcYear, Integer srcScore, Integer srcRank, Integer eqYear) {
        if (null == currScoreRange || null == eqScoreRange) {
            log.warn("无有效批次范围: {},{},{},{}", location, maxYear, srcYear, srcScore);
            return null;
        }
        String srcMode = currScoreRange.getMode();
        ScoreGroupCalculator sgMapper =  getScoreCalculatorMapper(location, srcYear, srcMode);
        if (null == sgMapper) {
            log.warn("无有效一分一段: {},{},{}", location, srcYear, srcMode);
            return null;
        }
        if(null == srcRank) {
            srcRank = sgMapper.getRank(srcYear, srcMode, srcScore);
        }
        if (null == srcRank) {
            log.warn("分数不在一分一段: {},{},{},{}", location, srcYear, srcMode, srcScore);
            return null;
        }
        String key = location + "_" + maxYear + "_" + srcMode + "_" + srcYear + "_" + eqYear + "_" + currScoreRange.getType();
        Integer[] data = locationYearEqRateMap.get(key);
        Integer eqMinRank = null, egRange = null, matchMinRank, matchRange;
        if (null == data) {
            synchronized (locationYearEqRateMap) {
                if (null == (data = locationYearEqRateMap.get(key))) {
                    matchMinRank = sgMapper.getRank(srcYear, currScoreRange.getMode(), currScoreRange.getMaxScore());
                    Integer matchMaxRank = sgMapper.getRank(srcYear, currScoreRange.getMode(), currScoreRange.getScore());
                    matchRange = null != matchMaxRank && null != matchMinRank ? matchMaxRank - matchMinRank : null;

                    ScoreGroupCalculator eqMapper = getScoreCalculatorMapper(location, eqYear, eqScoreRange.getMode());
                    Integer eqMaxRank = null;
                    if (null != eqScoreRange && null != eqMapper) {
                        eqMinRank = eqMapper.getRank2(eqYear, eqScoreRange.getMode(), eqScoreRange.getMaxScore());
                        eqMaxRank = eqMapper.getRank2(eqYear, eqScoreRange.getMode(), eqScoreRange.getScore());
                        egRange = null != eqMaxRank && null != eqMinRank ? eqMaxRank - eqMinRank : null;
                    }
                    data = new Integer[]{matchMinRank, matchRange, eqMinRank, egRange};
                    locationYearEqRateMap.put(key, data);
                }
            }

        }
        matchMinRank = data[0];
        matchRange = data[1];
        eqMinRank = data[2];
        egRange = data[3];
        if(null == matchMinRank || null == matchRange || null == eqMinRank || null == egRange) {
            log.warn("参数不全: {},{},{},{},值: {},{},{},{}", location, srcYear, srcMode, srcScore, matchMinRank, matchRange, eqMinRank, egRange);
            return null;
        }
        Double matchRate = (srcRank - matchMinRank + 1) * 1.0 / (matchRange + 1);
        Integer eqRank = (int) Math.floor(eqMinRank + matchRate * egRange);
        return eqRank;
    }

    public BBusiWishScoreGroups getScoreGroups(String location, Integer year, String mode, Integer score) {
        ScoreGroupCalculator sgMapper =  getScoreCalculatorMapper(location, year, mode);
        return sgMapper.getScoreGroupByScore(year, mode, score);
    }

    /**
     * 根据批次查找对应的批次
     * @return
     */
    public BBusiWishScoreGroups getScoreRangeByBatch(String location, Integer maxYear, String course, Integer year, Integer batch) {
        Map<Integer, List<BBusiWishScoreGroups>> yearBatchRangeMap = buildScoreBatchRange(location, maxYear, course);
        if(null == yearBatchRangeMap) {
            throw new RuntimeException("需要先初始化环境");
        }
        List<BBusiWishScoreGroups> batchRangeList = yearBatchRangeMap.get(year);
        if(null == batchRangeList) {
            return null;
        }
        Integer needType = batch < 4 && batchRangeList.get(0).getStatus() > 0 ? 1 : batch;
        Optional<BBusiWishScoreGroups> scoreGroupsOptional = batchRangeList.stream().filter(t -> t.getType().equals(needType)).findFirst();
        BBusiWishScoreGroups scoreGroup = null;
        if (scoreGroupsOptional.isPresent() && null != (scoreGroup = scoreGroupsOptional.get()) && null == scoreGroup.getMaxScore()) {
            ScoreGroupCalculator sgMapper = getScoreCalculatorMapper(location, year, scoreGroup.getMode());
            scoreGroup.setMaxScore(sgMapper.getMaxScore(year, scoreGroup.getMode()));
        }
        return scoreGroup;
    }

    /**
     * 根据分数定位所在批次
     */
    public BBusiWishScoreGroups getScoreRangeByScore(String location, Integer maxYear, String course, Integer year, Integer score) {
        Map<Integer, List<BBusiWishScoreGroups>> yearBatchRangeMap = buildScoreBatchRange(location, maxYear, course);
        if(null == yearBatchRangeMap) {
            throw new RuntimeException("需要先初始化环境");
        }
        List<BBusiWishScoreGroups> batchRangeList = yearBatchRangeMap.get(year);
        Optional<BBusiWishScoreGroups> scoreGroupsOptional = batchRangeList.stream().filter(t -> score >= t.getScore() && (null == t.getMaxScore() || score <= t.getMaxScore())).findFirst();
        if(!scoreGroupsOptional.isPresent()) {
            return null;
        }
        BBusiWishScoreGroups scoreGroup = null;
        if (scoreGroupsOptional.isPresent() && null != (scoreGroup = scoreGroupsOptional.get()) && null == scoreGroup.getMaxScore()) {
            ScoreGroupCalculator sgMapper = getScoreCalculatorMapper(location, year, scoreGroup.getMode());
            scoreGroup.setMaxScore(sgMapper.getMaxScore(year, scoreGroup.getMode()));
        }
        return scoreGroup;

    }

    /**
     *
     * @param location
     * @param maxYear
     * @param course 文科/历史 或 理科/物理 批次线组，State 标名是否混合
     * @return mode = course
     */
    private Map<Integer, List<BBusiWishScoreGroups>> buildScoreBatchRange(String location, Integer maxYear, String course) {
        String key = location + "_" + course;
        Map<Integer, List<BBusiWishScoreGroups>> yearBatchRangeMap = locationYearBatchRangeMap.get(key);
        if(null != yearBatchRangeMap) {
            return yearBatchRangeMap;
        }
        synchronized (locationYearBatchRangeMap) {
            if (null != (yearBatchRangeMap = locationYearBatchRangeMap.get(key))) {
                return yearBatchRangeMap;
            }
            Integer minYear = maxYear - 3;
            BBusiWishLocationSubmits query = new BBusiWishLocationSubmits();
            query.setLocation(location);
            List<BBusiWishLocationSubmits> lsList = wishLocationSubmitsMapper.selectBBusiWishLocationSubmitsList(query).stream().sorted(new Comparator<BBusiWishLocationSubmits>() {
                @Override
                public int compare(BBusiWishLocationSubmits o1, BBusiWishLocationSubmits o2) {
                    return o1.getScore().compareTo(o2.getScore());
                }
            }).filter(ls -> {
                Integer year = Integer.parseInt(ls.getYear());
                return course.equals(ls.getCourse()) &&  year >= minYear && year <= maxYear;
            }).collect(Collectors.toList());
            // 判断是否旧转新高考
            Boolean isMixCourse = lsList.stream().map(t -> t.getCourse()).collect(Collectors.toSet()).size() > 1;

            // 构建立年度与批次范围表
            yearBatchRangeMap = Maps.newHashMap();
            for (BBusiWishLocationSubmits ls : lsList) {
                Integer year = Integer.parseInt(ls.getYear());
                List<BBusiWishScoreGroups> rangeList = yearBatchRangeMap.get(year);
                BBusiWishScoreGroups last = null;
                if (null == rangeList) {
                    rangeList = new ArrayList<>();
                    yearBatchRangeMap.put(year, rangeList);
                } else {
                    last = rangeList.get(rangeList.size() - 1);
                    last.setMaxScore(ls.getScore() - 1);
                }
                if (isMixCourse && null != last && last.getType() < 4) { // 以本科最低为本科批底线
                    last.setMaxScore(null); // 合并本科一二批次，要取掉最高分
                } else {
                    BBusiWishScoreGroups range = new BBusiWishScoreGroups();
                    range.setType(isMixCourse && ls.getType() < 4 ? 1 : ls.getType());
                    range.setStatus(isMixCourse ? 1 : 0); // 备注是否交叉状态
                    range.setMode(ls.getCourse()); // 文科/理科/物理/历史
                    range.setScore(ls.getScore());
                    rangeList.add(range);
                }
            }
            locationYearBatchRangeMap.put(key, yearBatchRangeMap);
            return yearBatchRangeMap;
        }
    }

    /**
     *
     * @param location
     * @param year
     * @param mode 文科/理科/物理/历史
     * @return
     */
    private ScoreGroupCalculator getScoreCalculatorMapper(String location, Integer year, String mode) {
        String key = location + "_" + year + "_" + mode;
        ScoreGroupCalculator sgMapper = locationYearScoreGroupMapperMap.get(key);
        if (null == sgMapper) {
            synchronized (locationYearScoreGroupMapperMap) {
                sgMapper = locationYearScoreGroupMapperMap.get(key);
                if (null == sgMapper) {
                    BBusiWishScoreGroups sgCond = new BBusiWishScoreGroups();
                    sgCond.setLocation(location);
                    sgCond.setYear(year);
                    sgCond.setMode(mode);
                    sgMapper = new ScoreGroupCalculator(wishScoreGroupsMapper.selectBBusiWishScoreGroupsList(sgCond));
                    locationYearScoreGroupMapperMap.put(key, sgMapper);
                }
            }
        }
        return sgMapper;
    }

}
