package com.ruoyi.web.service;

import cn.hutool.core.lang.Dict;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.TreeEntity;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.dz.domain.DzClasses;
import com.ruoyi.dz.mapper.DzClassesMapper;
import com.ruoyi.enums.PaperType;
import com.ruoyi.ie.domain.AMarjorPlan;
import com.ruoyi.ie.mapper.AMarjorPlanMapper;
import com.ruoyi.learn.domain.*;
import com.ruoyi.learn.mapper.LearnDirectedKnowledgeMapper;
import com.ruoyi.learn.mapper.LearnKnowledgeTreeMapper;
import com.ruoyi.learn.mapper.LearnStudentMapper;
import com.ruoyi.learn.mapper.LearnTestPaperMapper;
import com.ruoyi.syzy.domain.BBusiWishUniversities;
import com.ruoyi.syzy.mapper.BBusiWishUniversitiesMapper;
import lombok.Data;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.validation.ValidationException;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class LearnTeacherService {
    private final DzClassesMapper dzClassesMapper;
    private final LearnKnowledgeTreeMapper learnKnowledgeTreeMapper;
    private final LearnStudentMapper learnStudentMapper;
    private final AMarjorPlanMapper marjorPlanMapper;
    private final BBusiWishUniversitiesMapper busiWishUniversitiesMapper;
    private final LearnDirectedKnowledgeMapper learnDirectedKnowledgeMapper;
    private final PaperService paperService;
    private final LearnTestPaperMapper learnTestPaperMapper;

    public LearnTeacherService(DzClassesMapper dzClassesMapper, LearnKnowledgeTreeMapper learnKnowledgeTreeMapper, LearnStudentMapper learnStudentMapper, AMarjorPlanMapper marjorPlanMapper, BBusiWishUniversitiesMapper busiWishUniversitiesMapper, LearnDirectedKnowledgeMapper learnDirectedKnowledgeMapper, PaperService paperService, LearnTestPaperMapper learnTestPaperMapper) {
        this.dzClassesMapper = dzClassesMapper;
        this.learnKnowledgeTreeMapper = learnKnowledgeTreeMapper;
        this.learnStudentMapper = learnStudentMapper;
        this.marjorPlanMapper = marjorPlanMapper;
        this.busiWishUniversitiesMapper = busiWishUniversitiesMapper;
        this.learnDirectedKnowledgeMapper = learnDirectedKnowledgeMapper;
        this.paperService = paperService;
        this.learnTestPaperMapper = learnTestPaperMapper;
    }

    public List<DzClasses> getClasses(Long teacherId)
    {
        return dzClassesMapper.selectClassesForTeacher(teacherId);
    }

    public List<LearnStudent> getStudents(Long batchId, Long classId) {
        return learnStudentMapper.selectClassStudents(batchId, new Long[] {classId});
    }

    public List<BBusiWishUniversities> selectUniversityList(Long teacherId, Long batchId) {
        return busiWishUniversitiesMapper.selectUniversityListForTeacher(teacherId, batchId);
    }

    public List<Dict> selectMajorList(Long universityId, Integer year, Long batchId) {
        return marjorPlanMapper.selectMajorForUniversity(universityId, year, batchId).stream().map(t -> Dict.create().set("id", t.getId()).set("majorGroup", t.getMajorGroup())
                .set("majorName", t.getMajorName()).set("count", t.getXuefei()).set("total", t.getPlanTotal())).collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public String buildPapersAuto(TestPaperVO.TestPapersBuildAutoReq req) {
        // 查询 院校，专业组，专业计划范围, 明细一级时只有一个，否则就是批量
        List<LearnStudent> studentList = learnStudentMapper.selectLearnStudentsByMap(req.toMap());
        if(CollectionUtils.isEmpty(studentList)) {
            throw new ValidationException("无可用计划可检查");
        }
        Set<Long> planIdSet = Sets.newHashSet();
        Map<Long, List<LearnStudent>> universityMap = Maps.newHashMap();
        for(LearnStudent ls : studentList){
            planIdSet.add(ls.getMajorPlanId());
            List<LearnStudent> list = universityMap.get(ls.getUniversityId());
            if(null == list) {
                list = Lists.newArrayList();
                universityMap.put(ls.getUniversityId(), list);
            }
            list.add(ls);
        }
        // 查询已经生成的
        Map<Long, Map<String, LearnTestPaper>> universityDirectPaperMap = learnTestPaperMapper.selectByBatchAndUniversityIds(req.getBatchId(), universityMap.keySet()).stream()
                .collect(Collectors.groupingBy(LearnTestPaper::getUniversityId, Collectors.toMap(LearnTestPaper::getDirectKey, a -> a)));
        if(null == req.getDirectType() || !req.getDirectType()) { // 全量
            LearnPaper paper = new LearnPaper();
            paper.setPaperName(req.getTitle());
            paper.setYear(Calendar.getInstance().get(Calendar.YEAR));
            paper.setPaperType(PaperType.Test.name());
            paper.setSubjectId(req.getSubjectId());
            paper.setDirectKey("");
            for(Long universityId : universityMap.keySet()) {
                Map<String, LearnTestPaper> directPaperMap = universityDirectPaperMap.get(universityId);
                if(null != directPaperMap && directPaperMap.containsKey("")) {
                    continue;
                }
                if(null == paper.getId()) {
                    Pair<LearnPaper, List<LearnPaperQuestion>> paperResult = paperService.buildPaper(null, paper, req.getPaperDef());
                    paperService.savePaper(paper, paperResult.getRight());
                }
                LearnTestPaper testPaper = new LearnTestPaper();
                testPaper.setBatchId(req.getBatchId());
                testPaper.setUniversityId(universityId);
                testPaper.setDirectKey("");
                testPaper.setPaperId(paper.getId());
                learnTestPaperMapper.insertLearnTestPaper(testPaper);
            }
            return "";
        }
        TestPaperVO.PaperDef paperDef = req.getPaperDef();
        if(null != req.getPlanIds() && req.getPlanIds().size() == 1) {
            if(!CollectionUtils.isEmpty(paperDef.getKnowIds())) {
                throw new ValidationException("批量时不支持自定义知识点");
            }
        }
        Map<Long, Map<String, LearnDirectedKnowledge>> universityDirectedKnowledgeMap = learnDirectedKnowledgeMapper.selectByUniversityIds(universityMap.keySet().toArray(new Long[universityMap.size()])).stream().collect(
                Collectors.groupingBy(LearnDirectedKnowledge::getUniversityId, Collectors.toMap(LearnDirectedKnowledge::getDirectKey, a -> a)));
        Map<Long, AMarjorPlan> planMap = marjorPlanMapper.selectAMarjorPlanByIds(planIdSet.toArray(new Long[planIdSet.size()])).stream().collect(Collectors.toMap(AMarjorPlan::getId, a -> a));

        LearnDirectedKnowledge directedKnowledge = null;
        String directedKey = null;

        for(Long universityId : universityMap.keySet()) {
            Map<String, LearnDirectedKnowledge> directedKnowledgeMap = universityDirectedKnowledgeMap.get(universityId);
            for(LearnStudent ls : universityMap.get(universityId)) {
                AMarjorPlan plan = planMap.get(ls.getMajorPlanId());
                String groupName = StringUtils.trimToEmpty(plan.getMajorGroup());
                if(CollectionUtils.isEmpty(paperDef.getKnowIds())) {
                    if(null != (directedKnowledge = directedKnowledgeMap.get((directedKey = groupName + "_" + plan.getMajorName())))) {
                        paperDef.setKnowIds(Stream.of(directedKnowledge.getKnowledges().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
                    } else if(null != (directedKnowledge = directedKnowledgeMap.get(directedKey = groupName))) {
                        paperDef.setKnowIds(Stream.of(directedKnowledge.getKnowledges().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
                    } else {
                        throw new ValidationException("院校没有配置定向知识点，生成失败: " + universityId + ":" + groupName + ":" + plan.getMajorName());
                    }
                } else if(null != directedKnowledgeMap) {
                    if(null != (directedKnowledge = directedKnowledgeMap.get((directedKey = groupName + "_" + plan.getMajorName())))) {
                    } else if(null != (directedKnowledge = directedKnowledgeMap.get(directedKey = groupName))) {
                    } else {
                        throw new ValidationException("院校没有配置定向知识点，生成失败" + universityId + ":" + directedKey);
                    }
                } else {
                    throw new ValidationException("院校没有配置定向知识点，生成失败: " + universityId);
                }
                Map<String, LearnTestPaper> directPaperMap = universityDirectPaperMap.get(universityId);
                if(null != directPaperMap && directPaperMap.containsKey(directedKey)) {
                    continue;
                }
                LearnPaper paper = new LearnPaper();
                paper.setSubjectId(req.getSubjectId());
                paper.setPaperName(req.getTitle());
                paper.setYear(plan.getYear());
                paper.setPaperType(PaperType.Test.name());
                paper.setDirectKey(directedKey);
                Pair<LearnPaper, List<LearnPaperQuestion>> paperResult = paperService.buildPaper(null, paper, paperDef);
                paperService.savePaper(paperResult.getKey(), paperResult.getValue());

                LearnTestPaper testPaper = new LearnTestPaper();
                testPaper.setBatchId(req.getBatchId());
                testPaper.setUniversityId(universityId);
                testPaper.setDirectKey(directedKey);
                testPaper.setPaperId(paper.getId());
                learnTestPaperMapper.insertLearnTestPaper(testPaper);
                directPaperMap.put(directedKey, testPaper);
            }
        }
        return "";
    }

    @Transactional(rollbackFor = Exception.class)
    public String buildPapersManual(TestPaperVO.TestPapersBuildManualReq req) {
        if(null == req.getSubjectId()) {
            throw new ValidationException("未选择科目");
        }
        // 查询 院校，专业组，专业计划范围, 明细一级时只有一个，否则就是批量
        List<LearnStudent> studentList = learnStudentMapper.selectLearnStudentsByMap(req.toMap());
        if(CollectionUtils.isEmpty(studentList)) {
            throw new ValidationException("无可用计划可检查");
        }
        Set<Long> planIdSet = Sets.newHashSet();
        Map<Long, List<LearnStudent>> universityMap = Maps.newHashMap();
        for(LearnStudent ls : studentList){
            planIdSet.add(ls.getMajorPlanId());
            List<LearnStudent> list = universityMap.get(ls.getUniversityId());
            if(null == list) {
                list = Lists.newArrayList();
                universityMap.put(ls.getUniversityId(), list);
            }
            list.add(ls);
        }
        // 查询已经生成的
        Map<Long, Map<String, LearnTestPaper>> universityDirectPaperMap = learnTestPaperMapper.selectByBatchAndUniversityIds(req.getBatchId(), universityMap.keySet()).stream()
                .collect(Collectors.groupingBy(LearnTestPaper::getUniversityId, Collectors.toMap(LearnTestPaper::getDirectKey, a -> a)));

        LearnPaper paper = new LearnPaper();
        paper.setPaperName(req.getTitle());
        paper.setYear(Calendar.getInstance().get(Calendar.YEAR));
        paper.setPaperType(PaperType.Test.name());
        if(null == req.getDirectType() || !req.getDirectType()) { // 全量
            paper.setSubjectId(req.getSubjectId());
            paper.setDirectKey("");
            for(Long universityId : universityMap.keySet()) {
                Map<String, LearnTestPaper> directPaperMap = universityDirectPaperMap.get(universityId);
                if(null != directPaperMap && directPaperMap.containsKey("")) {
                    continue;
                }
                if(null == paper.getId()) {
                    paperService.savePaper(paper, req.getQuestions());
                }
                LearnTestPaper testPaper = new LearnTestPaper();
                testPaper.setBatchId(req.getBatchId());
                testPaper.setUniversityId(universityId);
                testPaper.setDirectKey("");
                testPaper.setPaperId(paper.getId());
                testPaper.setConditions("");
                testPaper.setCreatorId(SecurityUtils.getUserId());
                learnTestPaperMapper.insertLearnTestPaper(testPaper);
            }
            return "";
        } // 定向
        // 准备定向数据
        Map<Long, Map<String, LearnDirectedKnowledge>> universityDirectedKnowledgeMap = learnDirectedKnowledgeMapper.selectByUniversityIds(universityMap.keySet().toArray(new Long[universityMap.size()])).stream().collect(
                Collectors.groupingBy(LearnDirectedKnowledge::getUniversityId, Collectors.toMap(LearnDirectedKnowledge::getDirectKey, a -> a)));
        Map<Long, AMarjorPlan> planMap = marjorPlanMapper.selectAMarjorPlanByIds(planIdSet.toArray(new Long[planIdSet.size()])).stream().collect(Collectors.toMap(AMarjorPlan::getId, a -> a));

        String directedKey = null;
        for(Long universityId : universityMap.keySet()) {
            Map<String, LearnTestPaper> directPaperMap = universityDirectPaperMap.get(universityId);
            Map<String, LearnDirectedKnowledge> directedKnowledgeMap = universityDirectedKnowledgeMap.get(universityId);
            for(LearnStudent ls : universityMap.get(universityId)) { // Group+Name
                AMarjorPlan plan = planMap.get(ls.getMajorPlanId());
                String groupName = StringUtils.trimToEmpty(plan.getMajorGroup());
                if(null != (directedKnowledgeMap.get(directedKey = groupName + "_" + plan.getMajorName()))) {
                    if(null != directPaperMap.get(directedKey)) {
                        continue;
                    }
                } else if(null != directedKnowledgeMap.get(directedKey = groupName)) {
                    if(null != directPaperMap.get(directedKey)) {
                        continue;
                    }
                } else {
                    continue;
                }
                if(null == paper.getId()) {
                    paperService.savePaper(paper, req.getQuestions());
                }
                LearnTestPaper testPaper = new LearnTestPaper();
                testPaper.setBatchId(req.getBatchId());
                testPaper.setUniversityId(universityId);
                testPaper.setDirectKey(directedKey);
                testPaper.setPaperId(paper.getId());
                learnTestPaperMapper.insertLearnTestPaper(testPaper);
                directPaperMap.put(directedKey, testPaper);
            }
        }
        return "";
    }

    public List<TreeNode> getKnowledgeTree(Long subjectId, Long[] planIds) {
        Set<Long> knowledgeIdSet = Sets.newHashSet();
        if(ArrayUtils.isNotEmpty(planIds)) {
            List<AMarjorPlan> planList = marjorPlanMapper.selectAMarjorPlanByIds(planIds);
            AMarjorPlan curr = planList.get(0);
            LearnDirectedKnowledge dkCond = new LearnDirectedKnowledge();
            dkCond.setUniversityId(curr.getUniversityId());
            dkCond.setYear(curr.getYear());
            Map<String, LearnDirectedKnowledge> directedKnowledgeMap = learnDirectedKnowledgeMapper.selectLearnDirectedKnowledgeList(dkCond).stream().collect(Collectors.toMap(LearnDirectedKnowledge::getDirectKey, t -> t));
            LearnDirectedKnowledge directedKnowledge = null;
            for(AMarjorPlan plan : planList) {
                String groupName = StringUtils.trimToEmpty(plan.getMajorGroup());
                if(null != (directedKnowledge = directedKnowledgeMap.get(groupName + "_" + plan.getMajorName()))) {
                    knowledgeIdSet.addAll(Stream.of(directedKnowledge.getKnowledges().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
                } else if(null != (directedKnowledge = directedKnowledgeMap.get(groupName))) {
                    knowledgeIdSet.addAll(Stream.of(directedKnowledge.getKnowledges().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
                }
            }
        }
        LearnKnowledgeTree ktCond = new LearnKnowledgeTree();
        ktCond.setSubjectId(subjectId);
        List<LearnKnowledgeTree> ktList = learnKnowledgeTreeMapper.selectLearnKnowledgeTreeList(ktCond);
        List<TreeNode> treeNodeList = Lists.newArrayList();
        Map<Long, TreeNode> teMap = ktList.stream().collect(Collectors.toMap(LearnKnowledgeTree::getId, t -> new TreeNode(t.getId(), t.getName())));
        for(LearnKnowledgeTree kt : ktList) {
            if(null == kt.getPid()) {
                treeNodeList.add(teMap.get(kt.getId()));
                continue;
            }
            teMap.get(kt.getPid()).getChildren().add(teMap.get(kt.getId()));
        }
        for(TreeNode tn : treeNodeList) {
            tn.setChecked(knowledgeIdSet);
        }
        return treeNodeList;
    }

    @Data
    public static class TreeNode {
        private Long id;
        private String name;
        private Integer status;
        List<TreeNode> children = Lists.newArrayList();
        public TreeNode(Long id, String name) {
            this.id = id;
            this.name = name;
            this.status = 0;
        }

        public boolean setChecked(Set<Long> idSet) {
            boolean checkSelf = idSet.contains(id);
            if(CollectionUtils.isEmpty(children)) {
                this.status = checkSelf ? 1 : 0;
                return true;
            }
            int count = 0;
            for(TreeNode tn : children) {
                if(checkSelf || idSet.contains(tn.getId())) {
                    tn.setStatus(1);
                    count++;
                }
            }
            if(children.size() == count) {
                this.status = 1;
                return true;
            }
            return false;
        }
    }
}
