package com.admin.major.manager;


import com.admin.major.constant.MajorConstants;
import com.admin.major.domain.*;
import com.admin.major.domain.dto.MajorShuntCountDTO;
import com.admin.major.service.*;
import com.google.common.collect.Lists;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
@AllArgsConstructor
public class MajorAdjustManager {

    private IMajorApplyService majorApplyService;
    private ISpecialApplyService specialApplyService;
    private IStudentInfoService studentInfoService;
    private IEnhanceStudentInfoService enhanceStudentInfoService;
    private IEnhanceMajorInfoService enhanceMajorInfoService;
    private IAdjustResultService adjustResultService;

    // 获取志愿函数集
    private static final List<Function<MajorApply,String>> APPLY_MAJOR_LIST = Arrays.asList(
            MajorApply::getMajorId1st,
            MajorApply::getMajorId2nd,
            MajorApply::getMajorId3rd,
            MajorApply::getMajorId4th,
            MajorApply::getMajorId5th
    );


    @Data
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    private static class AdjustContext{
        private String year;
        private Long collegeId;
        private List<StudentInfo> studentInfos;

        private Map<String, AtomicLong> majorCounter;
        private List<AdjustResult> adjustResultList;

    }

    /**
     * 分流入口
     * @param year 年份
     * @param collegeId 学院
     */
    public void adjust(String year,Long collegeId)  {
        AdjustContext context = AdjustContext.builder()
                .year(year)
                .collegeId(collegeId)
                .adjustResultList(Lists.newArrayList())
                .build();

        this.initPreAdjustStudent(context);
        this.initMajorCounter(context);
        this.adjustMajor(context);
        this.saveResult(context);
    }

    /**
     *
     * 保存分流结果
     * @param context 上下文
     */
    private void saveResult(AdjustContext context){
        adjustResultService.deleteAdjustResultByDynamic(new AdjustResult(){{
            setBatchId(context.getYear());
            setCollegeId(context.getCollegeId());
            //设置不删除特殊审批和调剂的
            setAdjustFlag(0L);
            setSpecialFlag(0L);
        }});

        adjustResultService.saveBatch(context.getAdjustResultList());

        // 更新分流信息到学生表
//        enhanceStudentInfoService.updateStudentMajor(context.year,context.collegeId);
//        // 更新分流审核结果 todo 仅全部修改，细粒度不高，有必要再修改
//        majorApplyService.updateMajorApplyStatus(context.year,context.collegeId);
    }

    /**
     * 初始化需要分流的学生信息
     * @param context 上下文
     */
    private void initPreAdjustStudent(AdjustContext context){
        StudentInfo studentInfo = new StudentInfo();
        studentInfo.setYear(context.year);
        studentInfo.setCollegeId(context.collegeId);
        List<StudentInfo> studentInfos = studentInfoService.selectStudentInfoList(studentInfo);

        if(CollectionUtils.isEmpty(studentInfos)) throw new RuntimeException("数据异常");

//        SpecialApply specialApply = new SpecialApply();
//        specialApply.setYear(context.year);
//        specialApply.setCollegeId(context.collegeId);
//        specialApply.setApplyStatus(MajorConstants.APPLY_PASS);
//        List<SpecialApply> specialApplies = specialApplyService.selectSpecialApplyList(specialApply);
//        // 过滤已通过特别申请的学生
//        Set<String> specialIds = specialApplies.stream()
//                .map(SpecialApply::getStudentId)
//                .collect(Collectors.toSet());
//        studentInfos.removeIf(dto-> specialIds.contains(dto.getStudentId()));
        // 实时写入，不在分流时写入
//        specialApplies
//                .forEach(apply-> {
//                    AdjustResult adjustResult = AdjustResult.builder()
//                            .batchId(context.year)
//                            .collegeId(context.getCollegeId())
//                            .adjustFlag(MajorConstants.NO)
//                            .specialFlag(MajorConstants.YES)
//                            .admitMajorId(apply.getMajorId())
//                            .studentId(apply.getStudentId())
//                            .build();
//                    context.adjustResultList.add(adjustResult);
//                });


        // 去除已经分流的学生,
        AdjustResult adjustResultParam = new AdjustResult();
        adjustResultParam.setBatchId(context.year);
        adjustResultParam.setCollegeId(context.collegeId);
//        adjustResultParam.setSpecialFlag(1L);
//        adjustResultParam.setAdjustFlag(1L);
        adjustResultParam.setParams(new HashMap<String, Object>() {{
            put("isAdjusted", 1);
        }});
        List<AdjustResult> adjustResults = adjustResultService.selectAdjustResultList(adjustResultParam);
        Set<String> studentIds = adjustResults.stream()
                .map(AdjustResult::getStudentId)
                .collect(Collectors.toSet());
        studentInfos.removeIf(dto-> studentIds.contains(dto.getStudentId()));
        context.setStudentInfos(studentInfos);
    }

    /**
     * 初始化专业信息
     * @param context 上下文
     */
    private void initMajorCounter(AdjustContext context){
        MajorInfo majorInfoParam = new MajorInfo();
        majorInfoParam.setCollegeId(context.getCollegeId());
        majorInfoParam.setYear(context.year);
        majorInfoParam.setStatus(MajorConstants.VALID_STATUS);
        List<MajorShuntCountDTO> majorQuota = enhanceMajorInfoService.getMajorQuota(majorInfoParam);

        Map<String, AtomicLong> majorCounter = majorQuota.stream()
                .collect(Collectors.toMap(MajorShuntCountDTO::getMajorId,
                        info -> info.getSinUpStudentsNum() ==null ?
                                new AtomicLong(info.getTotalStudentsNum() )
                                : new AtomicLong(info.getTotalStudentsNum() - info.getSinUpStudentsNum())));
        context.setMajorCounter(majorCounter);
    }

    /**
     * 进行专业分流
     * 以成绩排序
     *
     */
    private void adjustMajor(AdjustContext context){
        // 按分数排序
        List<StudentInfo> studentInfos = context.getStudentInfos();
        studentInfos.sort(Comparator.comparingDouble(dto-> {
            if(dto.getScore() == null){
                return Double.MAX_VALUE;
            }
            return -dto.getScore().doubleValue();
        }));

        // 建立专业申请表索引
        MajorApply majorApply = new MajorApply();
        majorApply.setYear(context.year);
        majorApply.setCollegeId(context.getCollegeId());

        // 查询所有提交记录
        List<MajorApply> majorApplyList = majorApplyService.selectMajorApplyList(majorApply);
        //查找已提交的和未提交的
//        majorApplyList.addAll(_majorApplyList);
        if(CollectionUtils.isEmpty(majorApplyList)) return;
        Map<String, MajorApply> studentIdToApply = majorApplyList.stream()
                .collect(Collectors.toMap(MajorApply::getStudentId, dto -> dto));

        // 开始分流
        List<StudentInfo> noMajorStudentList = Lists.newArrayList();
        for (int i = 0; i < studentInfos.size(); i++) {
            StudentInfo studentInfo = studentInfos.get(i);
            // 少数民族学生
            if( studentInfo.getIsMinority()){
                noMajorStudentList.add(studentInfo);
                continue;
            }

            MajorApply applyInfo = studentIdToApply.get(studentInfo.getStudentId());
            if (applyInfo == null ){
                noMajorStudentList.add(studentInfo);
                continue;
            }
            // 对应专业名额
            boolean adjusted = normalAdjust(context, applyInfo);

            // 有学生没有分流，即专业已经分完了
            if (!adjusted) {
                noMajorStudentList.addAll(studentInfos.subList(i, studentInfos.size()));
                break;
            }
        }
        if(!CollectionUtils.isEmpty(noMajorStudentList)){
            handleNoMajorStudent(context, noMajorStudentList);
        }
    }

    /**
     * 普通分流法方式
     * @param context 上下文
     * @param applyInfo 申请信息
     * @return 是否分配专业
     */
    private boolean normalAdjust(AdjustContext context, MajorApply applyInfo) {
        Map<String, AtomicLong> majorCounter = context.getMajorCounter();
        for (Function<MajorApply, String> getMajorIdMethod : APPLY_MAJOR_LIST) {
            String majorId = getMajorIdMethod.apply(applyInfo);
            AtomicLong atomicLong = majorCounter.get(majorId);
            if (atomicLong != null) {
                if (atomicLong.getAndAdd(-1) > 0L) {
                    AdjustResult adjustResult = AdjustResult.builder()
                            .batchId(context.year)
                            .collegeId(context.getCollegeId())
                            .admitMajorId(majorId)
                            .adjustFlag(MajorConstants.NO)
                            .specialFlag(MajorConstants.NO)
                            .studentId(applyInfo.getStudentId())
                            .build();
                    context.adjustResultList.add(adjustResult);
                    log.debug("普通分流: {}", adjustResult);
                    return true;
                }
            }
        }
        return false;
    }


    private void handleNoMajorStudent(AdjustContext context, List<StudentInfo> studentInfos){
        List<AdjustResult> collect = studentInfos.stream()
                .map(info -> {
                    return AdjustResult.builder()
                            .batchId(context.year)
                            .collegeId(context.getCollegeId())
                            .adjustFlag(MajorConstants.NO)
                            .specialFlag(MajorConstants.NO)
                            .studentId(info.getStudentId())
                            .build();
                }).collect(Collectors.toList());
        context.adjustResultList.addAll(collect);

    }
}
