package cn.teammodel.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.teammodel.common.ErrorCode;
import cn.teammodel.common.FiveEducations;
import cn.teammodel.common.IdRequest;
import cn.teammodel.common.PK;
import cn.teammodel.config.exception.ServiceException;
import cn.teammodel.model.dto.Appraise.*;
import cn.teammodel.model.dto.admin.common.GroupDto;
import cn.teammodel.model.dto.admin.common.RGroupList;
import cn.teammodel.model.dto.admin.common.RMember;
import cn.teammodel.model.entity.User;
import cn.teammodel.model.entity.appraise.*;
import cn.teammodel.model.entity.school.ClassInfo;
import cn.teammodel.model.entity.school.School;
import cn.teammodel.model.entity.school.Student;
import cn.teammodel.model.vo.appraise.AppraiseRecordVo;
import cn.teammodel.model.vo.appraise.StudentReportVo;
import cn.teammodel.repository.*;
import cn.teammodel.security.utils.SecurityUtil;
import cn.teammodel.service.EvaluationService;
import cn.teammodel.utils.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.azure.cosmos.models.CosmosPatchOperations;
import com.azure.cosmos.models.PartitionKey;
import com.azure.spring.data.cosmos.core.query.CosmosPageRequest;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import lombok.Data;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.jfree.chart.ChartUtils;
import org.jfree.chart.JFreeChart;
import org.jfree.data.general.DefaultPieDataset;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @author winter
 * @create 2023-11-20 17:47
 */
@Service
public class EvaluationServiceImpl implements EvaluationService {
    /**
     * 评价记录对象的类型
     */
    private final static String TARGET_STUDENT = "student";
    private final static String TARGET_CLASS = "class";
    @Resource
    private ClassRepository classRepository;
    @Resource
    private StudentRepository studentRepository;
    @Resource
    private SchoolRepository schoolRepository;
    @Resource
    private AppraiseRecordRepository appraiseRecordRepository;
    @Resource
    private AppraiseRepository appraiseRepository;

    /**
     * 通用的获取用户学校和学段对应的 appraise 的方法: 判断参数,判断数据是否为空 <br/>
     * 从 token 中获取 schoolId
     */
    private Appraise findAppraise(String periodId) {
        User loginUser = SecurityUtil.getLoginUser();
        String schoolId = loginUser.getSchoolId();

        // 拿到要新增节点的原始数据
        Appraise appraise = appraiseRepository.findAppraiseBySchoolIdAndPeriodIdAndCode(schoolId, periodId, PK.PK_APPRAISE);
        if (appraise == null) {
            throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "该学段下评价项数据不存在");
        }
        return appraise;
    }

    @Override
    public Appraise getTree(GetEvaluateTreeDto getEvaluateTreeDto) {
        String periodId = getEvaluateTreeDto.getPeriodId();
        User loginUser = SecurityUtil.getLoginUser();
        String schoolId = loginUser.getSchoolId();
        Appraise appraise = appraiseRepository.findAppraiseBySchoolIdAndPeriodIdAndCode(schoolId, periodId, PK.PK_APPRAISE);

        // 如果传参 period 所在没有 tree 的话,则复制一份模板 tree 给他 (处理节点 id)
        if (appraise == null) {
            appraise = RepositoryUtil.findOne(appraiseRepository.findTemplateTree(), "获取模板评价树失败");
            if (appraise == null) {
                throw new ServiceException("模板评价树不存在");
            }
            // refresh
            refreshAppraiseTree(appraise);
            appraise.setPeriodId(periodId);
            appraise.setSchoolId(schoolId);
            appraise.setId(null);
            appraise = appraiseRepository.save(appraise);
        }

        return this.buildTree(appraise);
    }


    /**
     * 将 appraise 进行替换(天才)
     */
    private void refreshAppraiseTree(Appraise appraise) {
        List<AppraiseTreeNode> nodes = appraise.getNodes();
        List<AchievementRule> rules = appraise.getAchievementRules();
        List<AppraiseTreeNode> children = nodes.stream().filter(item -> item.getPid() != null).collect(Collectors.toList());
        // 将非 root 的 nodes 通过 pid 收集成 list, 再遍历 nodes, 将其每一个 id 对应 map 中的 list 的 pid修改成新的 id 即可
        Map<String, List<AppraiseTreeNode>> pidNodeMap = children.stream().collect(Collectors.groupingBy(AppraiseTreeNode::getPid));
        nodes.forEach(item -> {
            String newId = UUID.randomUUID().toString();
            String oldId = item.getId();
            List<AppraiseTreeNode> appraiseTreeNodes = pidNodeMap.get(oldId);
            if (appraiseTreeNodes != null) {
                appraiseTreeNodes.forEach(node -> node.setPid(newId));
            }
            // 处理每个节点属性
            item.setId(newId);
            item.setCreator("template");
        });

        // refresh rules
        rules.forEach(item -> item.setId(UUID.randomUUID().toString()));
    }

    @Override
    public Appraise buildTree(Appraise appraise) {
        if (appraise == null) {
            return null;
        }

        List<AppraiseTreeNode> nodes = appraise.getNodes();
        List<AppraiseTreeNode> parents = new ArrayList<>();

        // pid 为 null 或者 "" 则为 parents
        for (AppraiseTreeNode node : nodes) {
            if (StringUtils.isBlank(node.getPid())) {
                parents.add(node);
            }
        }
        // 迭代构建孩子节点
        for (AppraiseTreeNode parent : parents) {
            buildChildren(parent, nodes);
        }
        // 排序
        parents = parents.stream().sorted(Comparator.comparing(AppraiseTreeNode::getOrder)).collect(Collectors.toList());
        appraise.setNodes(parents);
        return appraise;
    }

    @Override
    public void flattenTree(List<AppraiseTreeNode> trees, List<AppraiseTreeNode> nodes) {
        // 递归结束条件
        if (ObjectUtils.isEmpty(trees)) return;

        // 这里的每个 tree 都是组装好的树,区别于单个节点
        for (AppraiseTreeNode tree : trees) {
            // 递归
            flattenTree(tree.getChildren(), nodes);
            // 回溯时删除 children,添加进列表
            tree.setChildren(null);
            nodes.add(tree);
        }
    }

    @Override
    public Appraise insertNode(InsertNodeDto insertNodeDto) {
        String periodId = insertNodeDto.getPeriodId();
        String pid = insertNodeDto.getPid();
        String name = insertNodeDto.getName();
        String logo = insertNodeDto.getLogo();
        Integer order = insertNodeDto.getOrder();
        Integer score = insertNodeDto.getScore();
        Boolean isPraise = insertNodeDto.getIsPraise();

        Appraise appraise = findAppraise(periodId);
        User loginUser = SecurityUtil.getLoginUser();
        List<AppraiseTreeNode> originNodes = appraise.getNodes();

        AppraiseTreeNode newNode = new AppraiseTreeNode();
        // 根节点直接添加
        if (StringUtils.isNotEmpty(pid)) {
            boolean invalid = originNodes.stream().noneMatch(item -> pid.equals(item.getId()));
            if (invalid) {
                throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "父节点不存在");
            }
        }
        if (StringUtils.isNotEmpty(pid)) {
            AppraiseTreeNode parent = originNodes.stream().filter(x -> pid.equals(x.getId()))
                    .findFirst()
                    .orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "pid 无效"));
            String level2 = parent.getName();

            // 新节点是三级节点(二级节点不用特殊处理
            String ppid = parent.getPid();
            if (StringUtils.isNotEmpty(ppid)) {
                String[] path = new String[3];
                AppraiseTreeNode root = originNodes.stream().filter(x -> ppid.equals(x.getId()))
                        .findFirst()
                        .orElseThrow(() -> new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "值周树结构错误"));
                // 层数不为三级,抛出异常
                if (StringUtils.isNotEmpty(root.getPid())) {
                    throw new ServiceException(ErrorCode.SYSTEM_ERROR.getCode(), "值周树结构错误");
                }
                String level1 = root.getName();
                path[0] = level1;
                path[1] = level2;
                path[2] = name;
                newNode.setPath(path);
                newNode.setPraise(isPraise);
                newNode.setLogo(logo);
                newNode.setScore(score);
            }
        }

        newNode.setId(UUID.randomUUID().toString());
        newNode.setPid(pid);
        newNode.setName(name);
        newNode.setOrder(order);
        newNode.setCreatorId(loginUser.getId());
        newNode.setCreator(loginUser.getName());
        newNode.setCreateTime(Instant.now().toEpochMilli());

        originNodes.add(newNode);
        return buildTree(appraiseRepository.save(appraise));
    }

    @Override
    public Appraise updateNode(UpdateNodeDto updateNodeDto) {
        String updateNodeId = updateNodeDto.getId();
        String newNodeName = updateNodeDto.getName();

        Appraise appraise = findAppraise(updateNodeDto.getPeriodId());
        List<AppraiseTreeNode> originNodes = appraise.getNodes();
        // 每个节点都有 id, 直接校验是否合法
        AppraiseTreeNode updateNode = originNodes.stream()
                .filter(item -> updateNodeId.equals(item.getId()))
                .findFirst()
                .orElseThrow(() -> new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "更新节点不存在"));
        String originNodeName = updateNode.getName();
        updateNode.setName(newNodeName);
        updateNode.setOrder(updateNodeDto.getOrder());

        // 一级/二级
        if (updateNode.getPath() == null || updateNode.getPath().length == 0) {
            int index = StringUtils.isEmpty(updateNode.getPid()) ? 0 : 1;
            originNodes.forEach(x -> {
                // 修改三级节点的 path
                if (x.getPath() == null) {
                    return;
                }
                String[] path = x.getPath();
                if (originNodeName.equals(path[index])) {
                    path[index] = newNodeName;
                    x.setPath(path);
                }
            });
        } else {
            // 三级
            updateNode.setLogo(updateNodeDto.getLogo());
            updateNode.setPraise(updateNodeDto.isPraise());
            updateNode.getPath()[2] = newNodeName;
        }

        return buildTree(appraiseRepository.save(appraise));
    }

    @Override
    public Appraise deleteNode(DeleteNodeDto deleteNodeDto) {
        // 删除指定节点,可能是(一级,二级,三级),设计一个通用的
        Appraise appraise = findAppraise(deleteNodeDto.getPeriodId());
        List<AppraiseTreeNode> nodes = appraise.getNodes();
        List<AppraiseTreeNode> nodesToDelete = new ArrayList<>();

        // 收集需要删除的节点
        for (AppraiseTreeNode node : nodes) {
            if (node.getId().equals(deleteNodeDto.getId())) {
                // 删除当前节点
                nodesToDelete.add(node);
                // 递归删除其孩子节点
                this.collectNodesToDelete(node.getId(), nodes, nodesToDelete);
                break;
            }
        }
        if (ObjectUtils.isEmpty(nodesToDelete)) {
            throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "删除节点不存在");
        }
        nodes.removeAll(nodesToDelete);

        return buildTree(appraiseRepository.save(appraise));
    }

    // 定义辅助类（可选）
    @Data
    private static class TargetInfo {
        private String classId;
        private String className;
        private String periodId;
        private String name;
        private String avatar;
        private String gender;

        public TargetInfo(Student student) {
            this.classId = student.getClassId();
            this.periodId = student.getPeriodId();
            this.name = student.getName();
            this.avatar = student.getPicture();
            this.gender = student.getGender();
        }

        public TargetInfo(ClassInfo classInfo) {
            this.classId = classInfo.getId();
            this.className = classInfo.getName();
            this.periodId = classInfo.getPeriodId();
            this.name = classInfo.getName();
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void vote(AppraiseVoteDto appraiseVoteDto) {
        // 1. 获取基础参数
        List<String> targetIds = appraiseVoteDto.getTargetIds();
        List<String> appraiseIds = appraiseVoteDto.getAppraiseIds();
        boolean spread = appraiseVoteDto.isSpread();
        boolean pushParent = appraiseVoteDto.isPushParent();
        String targetType = appraiseVoteDto.getTargetType();
        String from = appraiseVoteDto.getDevice();
        User loginUser = SecurityUtil.getLoginUser();
        String schoolId = loginUser.getSchoolId();

        // 2. 参数校验
        if (targetIds == null || targetIds.isEmpty()) {
            throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "目标ID列表不能为空");
        }
        if (appraiseIds == null || appraiseIds.isEmpty()) {
            throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "评价项ID列表不能为空");
        }

        // 3. 批量查询目标信息
        Map<String, TargetInfo> targetInfoMap = getTargetInfos(targetIds, targetType, schoolId);

        // 4. 处理每个目标
        for (String targetId : targetIds) {
            TargetInfo targetInfo = targetInfoMap.get(targetId);
            processSingleTarget(
                    targetId, targetInfo, appraiseIds, spread, pushParent, targetType,
                    schoolId, loginUser, from
            );
        }
    }

    private Map<String, TargetInfo> getTargetInfos(List<String> targetIds, String targetType, String schoolId) {

        if (targetType.equals(TARGET_STUDENT)) {

            Set<String> targetIdsSet = new HashSet<>(targetIds);
            List<Student> students = studentRepository.findAllByCodeAndIdIn(String.format(PK.STUDENT, schoolId),targetIdsSet);

            return students.stream().collect(Collectors.toMap(Student::getId, TargetInfo::new));

        } else if (targetType.equals(TARGET_CLASS)) {

            List<ClassInfo> classes = classRepository.findAllByCodeAndIdIn(String.format(PK.CLASS, schoolId), targetIds);

            return classes.stream().collect(Collectors.toMap(ClassInfo::getId, TargetInfo::new));

        } else {

            throw new ServiceException("不受支持的评价对象类型");

        }

    }

    private void processSingleTarget(
            String targetId, TargetInfo targetInfo, List<String> appraiseIds,
            boolean spread, boolean pushParent, String targetType,
            String schoolId, User loginUser, String from
    ) {
        // 1. 获取学段和学年学期信息
        List<School.Period> periodById = schoolRepository.findPeriodById(schoolId, targetInfo.getPeriodId());
        School.Period period = RepositoryUtil.findOne(periodById, "获取学段失败");
        List<School.Semester> semesters = period.getSemesters();
        String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());

        // 2. 批量查询评价项节点
        Set<String> appraiseIdsSet = new HashSet<>(appraiseIds);
        List<AppraiseTreeNode> allNodes = appraiseRepository.findAllByCodeAndIdIn(PK.PK_APPRAISE, appraiseIdsSet, targetInfo.getPeriodId());
        Map<String, AppraiseTreeNode> nodeMap = allNodes.stream()
                .collect(Collectors.toMap(AppraiseTreeNode::getId, node -> node));

        // 3. 校验评价项有效性
        for (String appraiseId : appraiseIds) {
            AppraiseTreeNode node = nodeMap.get(appraiseId);
            //RepositoryUtil.findOne(nodeMap, "获取评价项失败: " + appraiseId)
            if (node == null) {
                throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "评价项不存在: " + appraiseId);
            }
            if (node.getPath() == null) {
                throw new ServiceException(ErrorCode.PARAMS_ERROR.getCode(), "仅能评价三级评价项: " + appraiseId);
            }
        }

        // 4. 构建评价记录项并统计值
        int totalPraise = 0;
        int totalCriticize = 0;
        int totalScoreIncrement = 0;
        List<AppraiseRecordItem> items = new ArrayList<>();
        for (String appraiseId : appraiseIds) {
            AppraiseTreeNode node = nodeMap.get(appraiseId);
            AppraiseRecordItem item = new AppraiseRecordItem();
            item.setId(UUID.randomUUID().toString());
            item.setAppraiseNode(node);
            item.setCreator(loginUser.getName());
            item.setCreatorId(loginUser.getId());
            item.setCreateTime(Instant.now().toEpochMilli());
            item.setDevice(from);
            if (TARGET_CLASS.equals(targetType)) {
                item.setSpread(spread);
            } else {
                item.setPushParent(pushParent);
            }
            items.add(item);

            if (node.isPraise()) {
                totalPraise++;
                totalScoreIncrement += 1;
            } else {
                totalCriticize++;
                totalScoreIncrement -= 1;
            }
        }

        // 5. 创建或更新评价记录
        String classId = targetInfo.getClassId();
        AppraiseRecord record = appraiseRecordRepository.findAppraiseRecordByTargetIdAndClassIdAndAcademicYearIdAndCode(
                targetId, classId, academicYearId, String.format(PK.PK_APPRAISE_RECORD, schoolId)
        );
        if (record == null || !classId.equals(record.getClassId())) {
            record = new AppraiseRecord();
            record.setTargetId(targetId);
            record.setTargetType(targetType);
            record.setClassId(classId);
            record.setClassName(targetInfo.getClassName());
            record.setName(targetInfo.getName());
            record.setAvatar(targetInfo.getAvatar());
            record.setGender(targetInfo.getGender());
            record.setAcademicYearId(academicYearId);
            record.setPraiseCount(totalPraise);
            record.setCriticizeCount(totalCriticize);
            record.setScore(Math.max(totalScoreIncrement, 0));
            record.setNodes(items);
            record.setCode(String.format(PK.PK_APPRAISE_RECORD, schoolId));
            appraiseRecordRepository.save(record);
        } else {
            CosmosPatchOperations operations = CosmosPatchOperations.create();
            for (AppraiseRecordItem item : items) {
                operations.add("/nodes/-", item);
            }
            operations.increment("/praiseCount", totalPraise);
            operations.increment("/criticizeCount", totalCriticize);
            int newScore = record.getScore() + totalScoreIncrement;
            operations.set("/score", Math.max(newScore, 0));
            appraiseRecordRepository.save(record.getId(), PK.buildOf(PK.PK_APPRAISE_RECORD, schoolId), AppraiseRecord.class, operations);
        }
    }

    @Override
    public List<AppraiseRecordVo> findVoteRecord(FindVoteRecordDto findVoteRecordDto, HttpServletRequest request) {
        String periodId = findVoteRecordDto.getPeriodId();
        String targetId = StringUtils.isBlank(findVoteRecordDto.getTargetId()) ? null : findVoteRecordDto.getTargetId();
        String targetType = StringUtils.isBlank(findVoteRecordDto.getTargetType()) ? null : findVoteRecordDto.getTargetType();
        String classId = StringUtils.isBlank(findVoteRecordDto.getClassId()) ? null : findVoteRecordDto.getClassId();
        Boolean isPraise = findVoteRecordDto.getIsPraise();
        User loginUser = SecurityUtil.getLoginUser();
        String teacherId = loginUser.getId();
        String schoolId = loginUser.getSchoolId();

        List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, periodId);
        String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());

/*        // 2. 分页查询（确保 offset 计算正确）
        int page = findVoteRecordDto.getCurrent();
        int size = findVoteRecordDto.getSize();
        //int offset = page * size; // 计算实际偏移量

        CosmosPageRequest pageRequest =new CosmosPageRequest(page, size,null);*/

        List<AppraiseRecordVo> content = appraiseRecordRepository.searchNodesByCondition(
                targetId,
                targetType,
                classId,
                teacherId,
                academicYearId,
                isPraise,
                String.format(PK.PK_APPRAISE_RECORD, schoolId)
        );

/*        List<AppraiseRecordVo> content = appraiseRecordItemPage.getContent();
        long total = appraiseRecordItemPage.getTotalElements(); // 获取总数*/

        // 3. 提取学生ID，查询学生个人信息
        Set<String> studentIds = content.stream().map(AppraiseRecordVo::getTargetId).collect(Collectors.toSet());
        if (studentIds.isEmpty()) throw new ServiceException(ErrorCode.PARAM_ERROR.getCode(), "没有对应学生信息");
        List<Student> students = studentRepository.findAllByCodeAndIdIn(String.format(PK.STUDENT, schoolId), studentIds);
        Set<String> classIds = students.stream().map(Student::getClassId).filter(Objects::nonNull).collect(Collectors.toSet());
        List<ClassInfo> classes = classRepository.findAllByCodeAndIdIn(String.format(PK.CLASS, schoolId), classIds);

        // 4. 补充学生和班级信息
        for (AppraiseRecordVo appraiseRecordVo : content) {
            students.stream().filter(student -> student.getId().equals(appraiseRecordVo.getTargetId()))
                    .findFirst()
                    .ifPresent(student -> {
                        appraiseRecordVo.setClassId(student.getClassId());
                    });
            classes.stream().filter(classInfo -> classInfo.getId().equals(appraiseRecordVo.getClassId()))
                    .findFirst()
                    .ifPresent(classInfo -> {
                        appraiseRecordVo.setClassName(classInfo.getName());
                    });
        }

        // 5. 返回分页结果

        return content;
    }

    @Override
    public void recallVote(RecallVoteDto recallVoteDto) {
        String recordId = recallVoteDto.getRecordId();
        String nodeId = recallVoteDto.getNodeId();
        User loginUser = SecurityUtil.getLoginUser();
        String schoolId = loginUser.getSchoolId();
        String userId = loginUser.getId();

        Optional<AppraiseRecord> optional = appraiseRecordRepository.findById(recordId, PK.buildOf(PK.PK_APPRAISE_RECORD, schoolId));
        AppraiseRecord appraiseRecord = optional.orElseThrow(() -> new ServiceException("该记录不存在"));
        AppraiseRecordItem record = appraiseRecord.getNodes()
                .stream()
                .filter(item -> nodeId.equals(item.getId()))
                .findFirst().orElseThrow(() -> new ServiceException("该记录节点不存在"));
        // 鉴权(不是创建老师不能撤回)
        if (!loginUser.getRoles().contains("admin") && !userId.equals(record.getCreatorId())) {
            throw new ServiceException(ErrorCode.NO_AUTH_ERROR.getCode(), "您不是创建老师，不能撤回");
        }
        // 删除评价项并且恢复评分
        appraiseRecord.getNodes().removeIf(item -> nodeId.equals(item.getId()));
        boolean praise = record.getAppraiseNode().isPraise();
        if (praise) {
            appraiseRecord.setPraiseCount(appraiseRecord.getPraiseCount() - 1);
            appraiseRecord.setScore(appraiseRecord.getScore() - 1);
        }else {
            appraiseRecord.setCriticizeCount(appraiseRecord.getCriticizeCount() - 1);
            appraiseRecord.setScore(appraiseRecord.getScore() + 1);
        }
        if (appraiseRecord.getScore() < 0) {
            appraiseRecord.setScore(0);
        }
        //int score = record.getAppraiseNode().getScore() == null ? 0 : record.getAppraiseNode().getScore();
        //Integer newScore = appraiseRecord.getScore() - score;
        //appraiseRecord.setScore(newScore);
        // 保存
        appraiseRecordRepository.save(appraiseRecord);
    }


    public List<StudentReportVo> Reports(ReportDto reportDto) {
        // 获取当前登录用户和学校信息
        User user = SecurityUtil.getLoginUser();
        String schoolId = user.getSchoolId();

        // 获取学生 ID 列表
        List<String> studentIds = reportDto.getIds();

        if (CollectionUtils.isEmpty(studentIds)) {
            throw new ServiceException(ErrorCode.PARAM_ERROR.getCode(), "学生 ID 列表不能为空");
        }
        Set<String> studentIdSet = new HashSet<>(studentIds);
        // 批量查询学生信息
        List<Student> students = studentRepository.findAllByCodeAndIdIn(String.format(PK.STUDENT, schoolId),studentIdSet);
        if (CollectionUtils.isEmpty(students)) {
            throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "未找到对应的学生信息");
        }

        // 获取学段 ID 和班级 ID
        String periodId = students.get(0).getPeriodId(); // 假设所有学生属于同一个学段
        List<String> classIds = students.stream().map(Student::getClassId).collect(Collectors.toList());

        // 查询学期信息
        List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, periodId);
        String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());

        // 批量查询评价记录
        List<AppraiseRecord> appraiseRecords = appraiseRecordRepository.findAllRecord(
                studentIds, classIds, academicYearId, String.format(PK.PK_APPRAISE_RECORD, schoolId)
        );
        if (CollectionUtils.isEmpty(appraiseRecords)) {
            throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "未找到对应的评价记录");
        }

        // 查询班级内学生总数
        Map<String, Integer> classStudentCountMap = new HashMap<>();
        for (String classId : classIds) {
            int count = studentRepository.countByClassIdAndCode(classId, String.format(PK.STUDENT, schoolId));
            classStudentCountMap.put(classId, count);
        }

        // 查询成就规则
        Appraise appraise = RepositoryUtil.findOne(appraiseRepository.findRulesById(schoolId, periodId), "当前成就规则还未创建");
        List<AchievementRule> rules = appraise.getAchievementRules();
        if (CollectionUtils.isEmpty(rules)) {
            throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "当前成就规则为空");
        }

        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);
        List<CompletableFuture<StudentReportVo>> futures = new ArrayList<>();

        // 并行处理每个学生的报告生成任务
        for (Student student : students) {
            CompletableFuture<StudentReportVo> future = CompletableFuture.supplyAsync(() -> {
                try {
                    return generateStudentReport(student, appraiseRecords, classStudentCountMap, rules, academicYearId, schoolId);
                } catch (Exception e) {
                   System.out.println("生成报告失败：" + e.getMessage());
                    return null;
                }
            }, executor);
            futures.add(future);
        }

        // 等待所有任务完成并收集结果
        List<StudentReportVo> reportVos = futures.stream()
                .map(CompletableFuture::join)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        // 关闭线程池
        executor.shutdown();

        return reportVos;
    }

    private StudentReportVo generateStudentReport(Student student, List<AppraiseRecord> appraiseRecords,
                                                  Map<String, Integer> classStudentCountMap, List<AchievementRule> rules,
                                                  String academicYearId, String schoolId) {
        String studentId = student.getId();
        String classId = student.getClassId();

        // 查找当前学生的评价记录
        Optional<AppraiseRecord> appraiseRecordOpt = appraiseRecords.stream()
                .filter(record -> record.getTargetId().equals(studentId))
                .findFirst();

        // 初始化默认值
        Map<String, Integer> praiseDistribution = StudentReportVo.ofFiveEducation();
        Map<String, Integer> criticalDistribution = StudentReportVo.ofFiveEducation();
        List<AppraiseRecordItem> records = Collections.emptyList();
        int praiseCount = 0;
        int criticizeCount = 0;
        int score = 0;
        float beyondPercent = 0.0f;
        AchievementRule curAchievement = rules.get(rules.size() - 1); // 默认使用最低成就规则
        int n = 1;

        // 如果有评价记录，则填充数据
        if (appraiseRecordOpt.isPresent()) {
            AppraiseRecord appraiseRecord = appraiseRecordOpt.get();

            // 处理评价记录
            if (appraiseRecord.getNodes() != null) {
                // 计算雷达图数据
                for (AppraiseRecordItem record : appraiseRecord.getNodes()) {
                    AppraiseTreeNode appraiseNode = record.getAppraiseNode();
                    String[] path = appraiseNode.getPath();
                    if (path == null || path.length == 0) {
                        continue;
                    }

                    String root = path[0];
                    String key = FiveEducations.getCodeByName(root);
                    if (key != null) {
                        if (appraiseNode.isPraise()) {
                            praiseDistribution.put(key, praiseDistribution.getOrDefault(key, 0) + 1);
                        } else {
                            criticalDistribution.put(key, criticalDistribution.getOrDefault(key, 0) + 1);
                        }
                    }
                }

                // 计算学生排名
                praiseCount = appraiseRecord.getPraiseCount();
                criticizeCount = appraiseRecord.getCriticizeCount();
                score = appraiseRecord.getScore();
                int greaterCount = appraiseRecordRepository.findClassRecord(String.format(PK.PK_APPRAISE_RECORD, schoolId), academicYearId, classId, praiseCount);
                int stuInClassCount = classStudentCountMap.get(classId);
                beyondPercent = (float) (stuInClassCount - greaterCount) / stuInClassCount;

                // 计算成就项 (排序 120(20x3) 60(10x4) 25(5x5) 121
                rules = rules.stream().sorted(Comparator.comparing(AchievementRule::getLevel).reversed()).collect(Collectors.toList());
                int ruleSize = rules.size();
                curAchievement = rules.get(rules.size() - 1);
                for (int i = 0; i <= rules.size() - 1; i++) {
                    AchievementRule curRule = rules.get(i);
                    int flag = praiseCount / curRule.getPromotionCount();
                    if (ruleSize == 1) {
                        n = praiseCount / curRule.getLevelCount() + 1;
                        break;
                    }
                    if (flag >= 1) {
                        if (i == 0) {
                            curAchievement = curRule;
                            AchievementRule lastRule = rules.get(i + 1);
                            n = (praiseCount - lastRule.getPromotionCount()) / curRule.getLevelCount() + 1;
                        } else {
                            AchievementRule nextRule = rules.get(i - 1);
                            curAchievement = nextRule;
                            n = (praiseCount - curRule.getPromotionCount()) / nextRule.getLevelCount() + 1;
                        }
                        break;
                    }
                    if (i == rules.size() - 1) {
                        n = praiseCount / curRule.getLevelCount() + 1;
                    }
                }

                // 获取记录
                records = appraiseRecord.getNodes().stream()
                        .sorted(Comparator.comparing(AppraiseRecordItem::getCreateTime).reversed())
                        .collect(Collectors.toList());
            }
        }

        // 构建报告
        return StudentReportVo.builder()
                .name(student.getName())
                .studentId(studentId)
                .className(student.getClassId()) // 假设班级名可以从学生信息中获取
                .praiseCount(praiseCount)
                .criticizeCount(criticizeCount)
                .score(score) // 默认分数为 0
                .beyondPercent(beyondPercent)
                .topPraiseTeacher(new StudentReportVo.Teacher(null, null)) // 默认值为 null
                .topCriticalTeacher(new StudentReportVo.Teacher(null, null)) // 默认值为 null
                .topPraiseNode(null) // 默认值为 null
                .topCriticalNode(null) // 默认值为 null
                .praiseDistribution(praiseDistribution)
                .criticalDistribution(criticalDistribution)
                .curAchievement(curAchievement)
                .achievementN(n)
                .records(records)
                .build();
    }

    @Override
    public StudentReportVo studentReport(IdRequest idRequest) {
        String studentId = idRequest.getId();
        User user = SecurityUtil.getLoginUser();
        String schoolId = user.getSchoolId();
        // 查询学生信息,学段等
        Student student = RepositoryUtil.findOne(studentRepository.findByIdAndCode(studentId, String.format(PK.STUDENT, schoolId)), "当前学生不存在");
        String periodId = student.getPeriodId();
        String classId = student.getClassId();
        List<School.Semester> semesters = schoolRepository.findSemestersById(schoolId, periodId);
        // 生成学年 id
        String academicYearId = SchoolDateUtil.calculateAcademicYearId(semesters, LocalDate.now());
        // 查询评价文档
        AppraiseRecord appraiseRecord = appraiseRecordRepository.findAppraiseRecordByTargetIdAndClassIdAndAcademicYearIdAndCode(
                studentId,
                classId,
                academicYearId,
                String.format(PK.PK_APPRAISE_RECORD, schoolId)
        );
        // 班级内学生总数
        int stuInClassCount = studentRepository.countByClassIdAndCode(classId, String.format(PK.STUDENT, schoolId));

        if (appraiseRecord == null || appraiseRecord.getNodes() == null) {
            throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "当前学生暂未产生评价数据,无法生成报告");
        }
        Integer praiseCount = appraiseRecord.getPraiseCount();
        List<AppraiseRecordItem> records = appraiseRecord.getNodes();
        records = records.stream().sorted(Comparator.comparing(AppraiseRecordItem::getCreateTime).reversed()).collect(Collectors.toList());
        // 查询成就规则
        Appraise appraise = RepositoryUtil.findOne(appraiseRepository.findRulesById(schoolId, periodId), "当前成就规则还未创建");
        List<AchievementRule> rules = appraise.getAchievementRules();
        if (CollectionUtil.isEmpty(rules)) {
            throw new ServiceException(ErrorCode.OPERATION_ERROR.getCode(), "当前成就规则为空");
        }
        // 计算雷达图
        Map<String, Integer> praiseDistribution = StudentReportVo.ofFiveEducation();
        Map<String, Integer> criticalDistribution = StudentReportVo.ofFiveEducation();
        // 计算数据
        Map<String, Integer> praiseTeacherMap = new HashMap<>();
        Map<String, Integer> criticalTeacherMap = new HashMap<>();
        Map<String, Integer> praiseNodeMap = new HashMap<>();
        Map<String, Integer> criticalNodeMap = new HashMap<>();
        // 计算当前学生排名在全班次位
        int greaterCount = appraiseRecordRepository.findClassRecord(String.format(PK.PK_APPRAISE_RECORD, schoolId), academicYearId, classId, praiseCount);
        float beyondPercent;
        long currentRank = stuInClassCount - greaterCount;
        beyondPercent = (float) currentRank / stuInClassCount;

        // 根据全部数据计算结果
        for (AppraiseRecordItem record : records) {
            AppraiseTreeNode appraiseNode = record.getAppraiseNode();
            String[] path = appraiseNode.getPath();
            // 数据异常则跳过
            if (path == null || path.length == 0) {
                continue;
            }
            String root = path[0];
            // 一级评价项不在预期中
            String key = FiveEducations.getCodeByName(root);
            // 计算雷达图
            if (key != null) {
                if (appraiseNode.isPraise()) {
                    praiseDistribution.put(key, praiseDistribution.getOrDefault(key, 0) + 1);
                } else {
                    criticalDistribution.put(key, criticalDistribution.getOrDefault(key, 0) + 1);
                }
            }

            String teacherId = record.getCreatorId();
            String nodeName = appraiseNode.getName();
            // 根据评价是否为表扬计算数据
            if (appraiseNode.isPraise()) {
                praiseTeacherMap.put(teacherId, praiseTeacherMap.getOrDefault(teacherId, 0) + 1);
                praiseNodeMap.put(nodeName, praiseNodeMap.getOrDefault(nodeName, 0) + 1);
            } else {
                criticalTeacherMap.put(teacherId, criticalTeacherMap.getOrDefault(teacherId, 0) + 1);
                criticalNodeMap.put(nodeName, criticalNodeMap.getOrDefault(nodeName, 0) + 1);
            }
        }
        // 计算成就项 (排序 120(20x3) 60(10x4) 25(5x5) 121
        rules = rules.stream().sorted(Comparator.comparing(AchievementRule::getLevel).reversed()).collect(Collectors.toList());
        int ruleSize = rules.size();
        AchievementRule curAchievement = rules.get(ruleSize - 1);

        int n = 1;
        for (int i = 0; i <= ruleSize - 1; i++) {
            // 头尾的情况要特判(理清逻辑就很简单)
            AchievementRule curRule = rules.get(i);
            int flag = praiseCount / curRule.getPromotionCount();
            // 只有一个节点也特殊处理
            if (ruleSize == 1) {
                n = praiseCount / curRule.getLevelCount() + 1;
                break;
            }
            if (flag >= 1) {
                if (i == 0) {
                    curAchievement = curRule;
                    AchievementRule lastRule = rules.get(i + 1);
                    n = (praiseCount - lastRule.getPromotionCount()) / curRule.getLevelCount() + 1;
                } else {
                    AchievementRule nextRule = rules.get(i - 1);
                    curAchievement = nextRule;
                    n = (praiseCount - curRule.getPromotionCount()) / nextRule.getLevelCount() + 1;
                }
                break;
            }
            if (i == ruleSize - 1) {
                n = praiseCount / curRule.getLevelCount() + 1;
            }
        }

        // 汇总数据
        String topPraiseTeacherId = praiseTeacherMap.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(null);
        AppraiseRecordItem tmp1 = records.stream().filter(item -> item.getCreatorId().equals(topPraiseTeacherId)).findFirst().orElse(null);
        String topPraiseTeacherName = tmp1 == null ? null : tmp1.getCreator();

        String topCriticalTeacherId = criticalTeacherMap.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(null);
        AppraiseRecordItem tmp2 = records.stream().filter(item -> item.getCreatorId().equals(topCriticalTeacherId)).findFirst().orElse(null);
        String topCriticalTeacherName = tmp2 == null ? null : tmp2.getCreator();

        String topPraiseNode = praiseNodeMap.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(null);
        String topCriticalNode = criticalNodeMap.entrySet().stream().max(Map.Entry.comparingByValue()).map(Map.Entry::getKey).orElse(null);

        return StudentReportVo.builder()
                .name(student.getName())
                .studentId(studentId)
                .className(appraiseRecord.getClassName())
                .praiseCount(praiseCount)
                .score(appraiseRecord.getScore())
                .beyondPercent(beyondPercent)
                .topPraiseTeacher(new StudentReportVo.Teacher(topPraiseTeacherId, topPraiseTeacherName))
                .topCriticalTeacher(new StudentReportVo.Teacher(topCriticalTeacherId, topCriticalTeacherName))
                .topPraiseNode(topPraiseNode)
                .topCriticalNode(topCriticalNode)
                .praiseDistribution(praiseDistribution)
                .criticalDistribution(criticalDistribution)
                .curAchievement(curAchievement)
                .achievementN(n)
                .records(records)
                .build();
    }

    @Override
    public void exportStuReportPdf(IdRequest idRequest, HttpServletResponse response) throws IOException, DocumentException {
        // 设置response参数
        response.reset();
        response.setContentType("application/pdf");
        response.setHeader("Content-disposition",
                "attachment;filename=report_student_" + System.currentTimeMillis() + ".pdf");
        ClassPathResource resource = new ClassPathResource("templates/pdf_templates/student_report.pdf");
        InputStream in = resource.getInputStream();
        ServletOutputStream os = response.getOutputStream();

        // 获取报告数据
        StudentReportVo reportVo = this.studentReport(idRequest);
        // 处理 stampter
        PdfReader pdfReader = new PdfReader(in);
        PdfStamper stamper = new PdfStamper(pdfReader, os);

        int criticalCount = reportVo.getCriticalDistribution().values().stream().mapToInt(Integer::intValue).sum();
        Map<String, String> data = new HashMap<>();
        data.put("name", reportVo.getName());
        data.put("studentId", reportVo.getStudentId());
        data.put("className", reportVo.getClassName());
        data.put("totalCount", String.valueOf(reportVo.getPraiseCount() + criticalCount));
        data.put("praiseCount", String.valueOf(reportVo.getPraiseCount()));
        data.put("criticalCount", String.valueOf(criticalCount));
        data.put("beyondPercent", String.valueOf(reportVo.getBeyondPercent()));
        data.put("topPraiseTeacher", StringUtils.isBlank(reportVo.getTopPraiseTeacher().getName()) ? "暂无" : reportVo.getTopPraiseTeacher().getName());
        data.put("topCriticalTeacher", StringUtils.isBlank(reportVo.getTopCriticalTeacher().getName()) ? "暂无" : reportVo.getTopCriticalTeacher().getName());
        data.put("topPraiseNode", StringUtils.isBlank(reportVo.getTopPraiseNode()) ? "暂无" : reportVo.getTopPraiseNode());
        data.put("topCriticalNode", StringUtils.isBlank(reportVo.getTopCriticalNode()) ? "暂无" : reportVo.getTopCriticalNode());

        DefaultPieDataset praiseDistributionDataset = new DefaultPieDataset();
        praiseDistributionDataset.setValue(FiveEducations.VIRTUE.getName(), reportVo.getPraiseDistribution().get(FiveEducations.VIRTUE.getCode()));
        praiseDistributionDataset.setValue(FiveEducations.INTELLIGENCE.getName(), reportVo.getPraiseDistribution().get(FiveEducations.INTELLIGENCE.getCode()));
        praiseDistributionDataset.setValue(FiveEducations.SPORTS.getName(), reportVo.getPraiseDistribution().get(FiveEducations.SPORTS.getCode()));
        praiseDistributionDataset.setValue(FiveEducations.ART.getName(), reportVo.getPraiseDistribution().get(FiveEducations.ART.getCode()));
        praiseDistributionDataset.setValue(FiveEducations.LABOUR.getName(), reportVo.getPraiseDistribution().get(FiveEducations.LABOUR.getCode()));
        JFreeChart praisePieChart = ChartUtil.pieChart("五育表扬指标分布", praiseDistributionDataset);
        ByteArrayOutputStream praiseBos = new ByteArrayOutputStream();
        ChartUtils.writeChartAsJPEG(praiseBos, praisePieChart, 340, 330);

        // 如果分布为空,则填充默认值 1
        DefaultPieDataset criticalDistributionDataset = new DefaultPieDataset();
        criticalDistributionDataset.setValue(FiveEducations.VIRTUE.getName(), reportVo.getCriticalDistribution().get(FiveEducations.VIRTUE.getCode()));
        criticalDistributionDataset.setValue(FiveEducations.INTELLIGENCE.getName(), reportVo.getCriticalDistribution().get(FiveEducations.INTELLIGENCE.getCode()));
        criticalDistributionDataset.setValue(FiveEducations.SPORTS.getName(), reportVo.getCriticalDistribution().get(FiveEducations.SPORTS.getCode()));
        criticalDistributionDataset.setValue(FiveEducations.ART.getName(), reportVo.getCriticalDistribution().get(FiveEducations.ART.getCode()));
        criticalDistributionDataset.setValue(FiveEducations.LABOUR.getName(), reportVo.getCriticalDistribution().get(FiveEducations.LABOUR.getCode()));
        JFreeChart criticalPieChart = ChartUtil.pieChart("五育待改进指标分布", criticalDistributionDataset);
        ByteArrayOutputStream criticalBos = new ByteArrayOutputStream();
        ChartUtils.writeChartAsJPEG(criticalBos, criticalPieChart, 340, 330);

        // 填充表单
        PdfUtil.fillPdfForm(stamper, data);
        PdfUtil.fillImage(stamper, "praiseDistribution", praiseBos.toByteArray());
        PdfUtil.fillImage(stamper, "criticalDistribution", criticalBos.toByteArray());
        // 关闭流
        stamper.setFormFlattening(true);
        stamper.close();
        os.close();
    }

    /**
     * 递归收集 id 的节点及 id 节点的孩子节点 (迭代器删除居然也报错)
     */
    private void collectNodesToDelete(String id, List<AppraiseTreeNode> nodes, List<AppraiseTreeNode> nodesToDelete) {
        for (AppraiseTreeNode node : nodes) {
            if (id.equals(node.getPid())) {
                // 收集
                nodesToDelete.add(node);
                // 递归收集
                collectNodesToDelete(node.getId(), nodes, nodesToDelete);
            }
        }

    }

    /**
     * 递归的构建父亲节点的孩子,以及孩子的孩子 (理论支持无极树,但应该考虑是否增加递归深度)
     */
    private void buildChildren(AppraiseTreeNode parent, List<AppraiseTreeNode> nodes) {
        List<AppraiseTreeNode> children = getChildren(parent, nodes);
        if (children.size() == 0) return;

        parent.setChildren(children);
        for (AppraiseTreeNode child : children) {
            // 如果子节点还有孩子的话,就继续递归构建
            if (getChildren(child, nodes).size() > 0) {
                buildChildren(child, nodes);
            }
        }

    }


    /**
     * 获取节点的孩子节点列表
     */
    private List<AppraiseTreeNode> getChildren(AppraiseTreeNode parent, List<AppraiseTreeNode> nodes) {
        List<AppraiseTreeNode> children = new ArrayList<>();
        for (AppraiseTreeNode node : nodes) {
            if (StringUtils.isNotBlank(node.getPid()) && node.getPid().equals(parent.getId())) {
                children.add(node);
            }
        }
        return children;
    }

}

