/***********************************************************
 * @Description : 考试服务接口实现
 * @author      : 梁山广(Laing Shan Guang)
 * @date        : 2019-05-28 08:06
 * @email       : liangshanguang2@gmail.com
 ***********************************************************/
package lsgwr.exam.service.impl;

import cn.hutool.core.util.IdUtil;// Hutool工具类，用于生成唯一ID等
import cn.hutool.core.util.StrUtil;// Hutool工具类，用于字符串处理
import lsgwr.exam.entity.*;// 导入所有实体类
import lsgwr.exam.enums.QuestionEnum;// 导入枚举类
import lsgwr.exam.service.ExamService;// 导入服务接口
import lsgwr.exam.repository.*; // 导入所有仓库接口
import lsgwr.exam.vo.*;// 导入所有值对象（View Object）
import org.springframework.beans.BeanUtils;// Spring提供的Bean工具类，用于属性复制
import org.springframework.stereotype.Service;// Spring提供的注解，用于声明服务类

import javax.transaction.Transactional;// 事务注解，用于声明事务性方法
import java.util.*;// 导入Java集合框架相关类
// 使用@Service注解声明这是一个服务类，用于实现业务逻辑
@Service
// 使用@Transactional注解声明这个服务类中的所有方法都运行在事务环境中
@Transactional
public class ExamServiceImpl implements ExamService {
    // 注入所有需要的仓库接口
    private final ExamRepository examRepository;

    private final ExamRecordRepository examRecordRepository;

    private final QuestionRepository questionRepository;

    private final UserRepository userRepository;

    private final QuestionLevelRepository questionLevelRepository;

    private final QuestionTypeRepository questionTypeRepository;

    private final QuestionCategoryRepository questionCategoryRepository;

    private final QuestionOptionRepository questionOptionRepository;
    // 通过构造函数注入所有仓库接口
    public ExamServiceImpl(QuestionRepository questionRepository, UserRepository userRepository, QuestionLevelRepository questionLevelRepository, QuestionTypeRepository questionTypeRepository, QuestionCategoryRepository questionCategoryRepository, QuestionOptionRepository questionOptionRepository, ExamRepository examRepository, ExamRecordRepository examRecordRepository) {
        // 依次赋值给对应的成员变量
        this.questionRepository = questionRepository;
        this.userRepository = userRepository;
        this.questionLevelRepository = questionLevelRepository;
        this.questionTypeRepository = questionTypeRepository;
        this.questionCategoryRepository = questionCategoryRepository;
        this.questionOptionRepository = questionOptionRepository;
        this.examRepository = examRepository;
        this.examRecordRepository = examRecordRepository;
    }
    // 实现获取所有问题的接口
    @Override
    public List<QuestionVo> getQuestionAll() {
        // 从问题仓库中获取所有问题实体
        List<Question> questionList = questionRepository.findAll();
        // 将问题实体列表转换为问题值对象列表
        return getQuestionVos(questionList);
    }
    // 私有方法，用于将问题实体列表转换为问题值对象列表
    private List<QuestionVo> getQuestionVos(List<Question> questionList) {
        // 初始化值对象列表
        List<QuestionVo> questionVoList = new ArrayList<>();
        // 遍历问题实体列表
        for (Question question : questionList) {
            // 将每个问题实体转换为值对象
            QuestionVo questionVo = getQuestionVo(question);
            // 将值对象添加到列表中
            questionVoList.add(questionVo);
        }
        // 返回值对象列表
        return questionVoList;
    }
    // 私有方法，用于将单个问题实体转换为问题值对象
    private QuestionVo getQuestionVo(Question question) {
        // 初始化值对象
        QuestionVo questionVo = new QuestionVo();
        // 复制问题实体中的属性到值对象
        BeanUtils.copyProperties(question, questionVo);
        // 设置问题的创建者用户名（通过用户ID从用户仓库中获取）
        questionVo.setQuestionCreator(
                Objects.requireNonNull(
                        userRepository.findById(
                                question.getQuestionCreatorId()
                        ).orElse(null)
                ).getUserUsername());// 使用orElseThrow抛出异常，避免使用requireNonNull和orElse(null)

        // 设置问题的难度描述（通过难度ID从难度仓库中获取）
        questionVo.setQuestionLevel(
                Objects.requireNonNull(
                        questionLevelRepository.findById(
                                question.getQuestionLevelId()
                        ).orElse(null)
                ).getQuestionLevelDescription());

        // 设置问题的类型描述（通过类型ID从类型仓库中获取）
        questionVo.setQuestionType(
                Objects.requireNonNull(
                        questionTypeRepository.findById(
                                question.getQuestionTypeId()
                        ).orElse(null)
                ).getQuestionTypeDescription());

        // 设置问题的分类名称（通过分类ID从分类仓库中获取）
        questionVo.setQuestionCategory(
                Objects.requireNonNull(
                        questionCategoryRepository.findById(
                                question.getQuestionCategoryId()
                        ).orElse(null)
                ).getQuestionCategoryName()
        );

        // 初始化选项值对象列表
        List<QuestionOptionVo> optionVoList = new ArrayList<>();

        // 从选项仓库中获取所有选项实体
        List<QuestionOption> optionList = questionOptionRepository.findAllById(
                Arrays.asList(question.getQuestionOptionIds().split("-"))
        );
        // 从选项仓库中获取所有答案选项实体
        List<QuestionOption> answerList = questionOptionRepository.findAllById(
                Arrays.asList(question.getQuestionAnswerOptionIds().split("-"))
        );

        // 遍历选项实体列表，转换为值对象，并设置答案属性
        for (QuestionOption option : optionList) {
            QuestionOptionVo optionVo = new QuestionOptionVo();
            BeanUtils.copyProperties(option, optionVo);
          // 判断当前选项是否是答案
            for (QuestionOption answer : answerList) {
                if (option.getQuestionOptionId().equals(answer.getQuestionOptionId())) {
                    optionVo.setAnswer(true);
                    break;// 找到答案后无需继续遍历

                }
            }
            // 将选项值对象添加到列表中
            optionVoList.add(optionVo);
        }

        // 设置问题的所有选项值对象列表
        questionVo.setQuestionOptionVoList(optionVoList);
        // 返回问题值对象
        return questionVo;
    }
    // 实现更新问题的接口
    @Override
    public QuestionVo updateQuestion(QuestionVo questionVo) {
        // 初始化答案ID字符串构建器
        StringBuilder questionAnswerOptionIds = new StringBuilder();
        // 初始化选项实体列表
        List<QuestionOption> questionOptionList = new ArrayList<>();
        // 获取问题值对象中的选项值对象列表
        List<QuestionOptionVo> questionOptionVoList = questionVo.getQuestionOptionVoList();
        // 获取选项值对象列表的大小
        int size = questionOptionVoList.size();
        // 遍历选项值对象列表
        for (int i = 0; i < questionOptionVoList.size(); i++) {
            // 获取当前选项值对象
            QuestionOptionVo questionOptionVo = questionOptionVoList.get(i);
            // 初始化选项实体
            QuestionOption questionOption = new QuestionOption();
            // 复制选项值对象中的属性到选项实体
            BeanUtils.copyProperties(questionOptionVo, questionOption);
            // 将选项实体添加到列表中
            questionOptionList.add(questionOption);
            // 判断当前选项是否是答案
            if (questionOptionVo.getAnswer()) {
                if (i != size - 1) {
                    // 如果不是最后一个答案，则添加ID并用"-"连接
                    questionAnswerOptionIds.append(questionOptionVo.getQuestionOptionId()).append("-");
                } else {
                    // 如果是最后一个答案，则直接添加ID
                    questionAnswerOptionIds.append(questionOptionVo.getQuestionOptionId());
                }
            }
        }

        // 1.更新问题
        Question question = questionRepository.findById(questionVo.getQuestionId()).orElse(null);
        // 确保找到的问题不为null，否则将抛出异常
        assert question != null;
        // 将传入的问题对象（questionVo）的属性复制到持久化对象（question）中
        BeanUtils.copyProperties(questionVo, question);
        // 更新问题的答案选项ID字符串（假设是以某种格式串联的ID字符串）
        question.setQuestionAnswerOptionIds(questionAnswerOptionIds.toString());
        // 保存更新后的问题到数据库
        questionRepository.save(question);
        // 2.更新所有的option
        questionOptionRepository.saveAll(questionOptionList);
        // 返回更新后的问题对象（转换为VO对象），方便前端局部刷新
        return getQuestionVo(question);
    }
    // 重写问题创建方法
    @Override
    public void questionCreate(QuestionCreateVo questionCreateVo) {
        // 创建一个新的Question对象
        Question question = new Question();
        // 使用BeanUtils工具类将questionCreateVo中的属性复制到question对象中
        BeanUtils.copyProperties(questionCreateVo, question);
        // 初始化选项列表
        List<QuestionOption> questionOptionList = new ArrayList<>();
        // 获取问题创建对象中的选项列表
        List<QuestionOptionCreateVo> questionOptionCreateVoList = questionCreateVo.getQuestionOptionCreateVoList();
        // 遍历选项创建对象列表
        for (QuestionOptionCreateVo questionOptionCreateVo : questionOptionCreateVoList) {
            // 为每个选项创建一个新的QuestionOption对象
            QuestionOption questionOption = new QuestionOption();
            // 设置选项的内容
            questionOption.setQuestionOptionContent(questionOptionCreateVo.getQuestionOptionContent());
            // 使用Hutool的IdUtil生成一个简单的UUID作为选项的ID
            questionOption.setQuestionOptionId(IdUtil.simpleUUID());
            // 将选项添加到列表中
            questionOptionList.add(questionOption);
        }
        // 将所有选项保存到数据库
        questionOptionRepository.saveAll(questionOptionList);
        // 初始化选项ID和答案选项ID的字符串
        String questionOptionIds = "";
        String questionAnswerOptionIds = "";
        // 遍历选项创建对象列表，获取保存后的选项对象
        for (int i = 0; i < questionOptionCreateVoList.size(); i++) {
            // 获取指定选项
            QuestionOptionCreateVo questionOptionCreateVo = questionOptionCreateVoList.get(i);
            // 获取保存后的指定对象
            QuestionOption questionOption = questionOptionList.get(i);
            // 拼接选项ID
            questionOptionIds += questionOption.getQuestionOptionId() + "-";
            if (questionOptionCreateVo.getAnswer()) {
                // 如果是答案的话
                questionAnswerOptionIds += questionOption.getQuestionOptionId() + "-";
            }
        }
        // 去除选项ID和答案选项ID字符串末尾的"-"
        questionAnswerOptionIds = replaceLastSeparator(questionAnswerOptionIds);
        questionOptionIds = replaceLastSeparator(questionOptionIds);
        // 设置问题对象的选项ID和答案选项ID
        question.setQuestionOptionIds(questionOptionIds);
        // 设置答案选项id组成的字符串
        question.setQuestionAnswerOptionIds(questionAnswerOptionIds);
        // 为问题生成一个UUID作为ID
        question.setQuestionId(IdUtil.simpleUUID());
        // 设置问题的创建时间和更新时间为当前时间
        question.setCreateTime(new Date());
        question.setUpdateTime(new Date());
        // 将问题保存到数据库
        questionRepository.save(question);
    }
    // 重写获取选择项的方法
    @Override
    public QuestionSelectionVo getSelections() {
        // 创建一个新的QuestionSelectionVo对象
        QuestionSelectionVo questionSelectionVo = new QuestionSelectionVo();
        // 设置问题分类列表、问题等级列表和问题类型列表
        questionSelectionVo.setQuestionCategoryList(questionCategoryRepository.findAll());
        questionSelectionVo.setQuestionLevelList(questionLevelRepository.findAll());
        questionSelectionVo.setQuestionTypeList(questionTypeRepository.findAll());

        return questionSelectionVo;
    }

    // 去除字符串末尾的"-"的方法
    public static String trimMiddleLine(String str) {
        // 如果字符串的最后一个字符是"-"，则去除它
        if (str.charAt(str.length() - 1) == '-') {
            str = str.substring(0, str.length() - 1);
        }
        return str;
    }
    // 注意：原代码中有一个方法名为replaceLastSeparator，但提供的实现是trimMiddleLine，这里假设它们功能相同
    @Override
    // 重写获取问题详情的方法
    public QuestionDetailVo getQuestionDetail(String id) {
        // 根据ID从数据库获取问题对象
        Question question = questionRepository.findById(id).orElse(null);
        // 创建一个新的QuestionDetailVo对象
        QuestionDetailVo questionDetailVo = new QuestionDetailVo();
        // 设置问题详情对象的ID、名称和描述
        questionDetailVo.setId(id);
        questionDetailVo.setName(question.getQuestionName());
        questionDetailVo.setDescription(question.getQuestionDescription());
        // 获取问题类型描述
        questionDetailVo.setType(
                Objects.requireNonNull(
                        questionTypeRepository.findById(
                                question.getQuestionTypeId()
                        ).orElse(null)
                ).getQuestionTypeDescription()
        );
        // 获取问题的选项ID字符串，去除末尾的"-"，然后分割成数组
        String optionIdsStr = trimMiddleLine(question.getQuestionOptionIds());
        String[] optionIds = optionIdsStr.split("-");
        // 根据选项ID数组从数据库获取选项列表
        List<QuestionOption> optionList = questionOptionRepository.findAllById(Arrays.asList(optionIds));
        // 设置问题详情对象的选项列表
        questionDetailVo.setOptions(optionList);
        return questionDetailVo;
    }
    // 重写获取所有考试的方法
    @Override
    public List<ExamVo> getExamAll() {
        // 从数据库获取所有考试对象
        List<Exam> examList = examRepository.findAll();
        // 将考试对象列表转换为ExamVo列表
        return getExamVos(examList);
    }
    // 定义一个私有方法，接收一个Exam对象的列表作为参数，返回一个ExamVo对象的列表
    private List<ExamVo> getExamVos(List<Exam> examList) {
        // 初始化一个空的ExamVo列表，用于存放转换后的对象
        List<ExamVo> examVoList = new ArrayList<>();
        // 遍历传入的Exam列表
        for (Exam exam : examList) {
            // 为每个Exam对象创建一个对应的ExamVo对象
            ExamVo examVo = new ExamVo();
            // 使用BeanUtils工具类将Exam对象的属性复制到ExamVo对象中（对于名称和类型相同的属性）
            BeanUtils.copyProperties(exam, examVo);
            // 通过userRepository查找考试创建者的信息，并设置到ExamVo对象的相应属性上
            // 这里使用了Optional的requireNonNull方法，确保找到的User对象不为null
            examVo.setExamCreator(
                    Objects.requireNonNull(
                            userRepository.findById(
                                    exam.getExamCreatorId()
                            ).orElse(null)
                    ).getUserUsername()
            );

            // 初始化单选题的列表，并设置到ExamVo对象的相应属性上
            List<ExamQuestionSelectVo> radioQuestionVoList = new ArrayList<>();
            // 根据考试中的单选题ID字符串，分割成ID列表，并通过questionRepository查找对应的Question对象列表
            List<Question> radioQuestionList = questionRepository.findAllById(
                    Arrays.asList(exam.getExamQuestionIdsRadio().split("-"))
            );
            // 遍历单选题列表，为每个Question对象创建一个对应的ExamQuestionSelectVo对象，并设置选中状态为true
            for (Question question : radioQuestionList) {
                ExamQuestionSelectVo radioQuestionVo = new ExamQuestionSelectVo();
                BeanUtils.copyProperties(question, radioQuestionVo);
                radioQuestionVo.setChecked(true);// 假设考试中的问题在初始化时都被视为已选中
                radioQuestionVoList.add(radioQuestionVo);
            }
            examVo.setExamQuestionSelectVoRadioList(radioQuestionVoList);
            // 创建一个用于存储考试选择题Vo对象的列表
            List<ExamQuestionSelectVo> checkQuestionVoList = new ArrayList<>();
            // 从数据库中获取所有需要检查（可能是单选或多选，但此处标记为检查题）的问题列表
            // 通过分割考试对象中的考试问题ID字符串（使用"-"作为分隔符），并将结果转换为List<Long>
            // 然后调用questionRepository的findAllById方法获取Question对象列表
            List<Question> checkQuestionList = questionRepository.findAllById(
                    Arrays.asList(exam.getExamQuestionIdsCheck().split("-"))
            );
            // 遍历获取到的问题列表
            for (Question question : checkQuestionList) {
                // 为每个问题创建一个新的ExamQuestionSelectVo对象
                ExamQuestionSelectVo checkQuestionVo = new ExamQuestionSelectVo();
                // 使用BeanUtils工具类将Question对象的属性复制到新的ExamQuestionSelectVo对象中
                BeanUtils.copyProperties(question, checkQuestionVo);
                // 设置选中状态为true（表示这个问题在考试中是已经被选中的）
                checkQuestionVo.setChecked(true); // 考试中问题肯定被选中的
                // 将设置好的Vo对象添加到列表中
                checkQuestionVoList.add(checkQuestionVo);
            }
            examVo.setExamQuestionSelectVoCheckList(checkQuestionVoList);

            // 从数据库中获取所有需要判断的问题列表
           // 通过分割考试对象中的考试问题ID字符串（使用"-"作为分隔符），并将结果转换为List<Long>
           // 然后调用questionRepository的findAllById方法获取Question对象列表
            List<ExamQuestionSelectVo> judgeQuestionVoList = new ArrayList<>();
            List<Question> judgeQuestionList = questionRepository.findAllById(
                    Arrays.asList(exam.getExamQuestionIdsJudge().split("-"))
            );
            // 遍历获取到的问题列表
            for (Question question : judgeQuestionList) {
                // 为每个问题创建一个新的ExamQuestionSelectVo对象
                ExamQuestionSelectVo judgeQuestionVo = new ExamQuestionSelectVo();
                // 使用BeanUtils工具类将Question对象的属性复制到新的ExamQuestionSelectVo对象中
                BeanUtils.copyProperties(question, judgeQuestionVo);
                // 设置选中状态为true
                judgeQuestionVo.setChecked(true);
                // 将设置好的Vo对象添加到列表中
                judgeQuestionVoList.add(judgeQuestionVo);
            }
            examVo.setExamQuestionSelectVoJudgeList(judgeQuestionVoList);

            // 将处理好的ExamVo对象添加到列表中
            examVoList.add(examVo);
        }
        // 返回处理好的ExamVo列表
        return examVoList;
    }
    // 重写父类或接口中的方法，用于获取考试问题类型的详细信息
    @Override
    public ExamQuestionTypeVo getExamQuestionType() {
        // 初始化一个ExamQuestionTypeVo对象，用于存储考试题型信息
        ExamQuestionTypeVo examQuestionTypeVo = new ExamQuestionTypeVo();
        // 获取所有的单选题列表，并赋值到ExamVo的属性ExamQuestionSelectVoRadioList上
        List<ExamQuestionSelectVo> radioQuestionVoList = new ArrayList<>();
        // 从数据库中获取所有单选题（通过题型ID筛选），这里的QuestionEnum.RADIO.getId()返回单选题的ID
        List<Question> radioQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.RADIO.getId());
        // 遍历所有单选题
        for (Question question : radioQuestionList) {
            // 为每个单选题创建一个ExamQuestionSelectVo对象
            ExamQuestionSelectVo radioQuestionVo = new ExamQuestionSelectVo();
            // 将Question对象的属性复制到radioQuestionVo对象中
            BeanUtils.copyProperties(question, radioQuestionVo);
            // 将复制后的对象添加到单选题的列表中
            radioQuestionVoList.add(radioQuestionVo);
        }
        // 将单选题列表设置到ExamQuestionTypeVo对象的相应属性中
        examQuestionTypeVo.setExamQuestionSelectVoRadioList(radioQuestionVoList);

        // 初始化多选题的列表
        List<ExamQuestionSelectVo> checkQuestionVoList = new ArrayList<>();
        // 从数据库中获取所有多选题（通过题型ID筛选），这里的QuestionEnum.CHECK.getId()返回多选题的ID
        List<Question> checkQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.CHECK.getId());
        // 遍历所有多选题
        for (Question question : checkQuestionList) {
            // 为每个多选题创建一个ExamQuestionSelectVo对象
            ExamQuestionSelectVo checkQuestionVo = new ExamQuestionSelectVo();
            // 将Question对象的属性复制到checkQuestionVo对象中
            BeanUtils.copyProperties(question, checkQuestionVo);
            // 将复制后的对象添加到多选题的列表中
            checkQuestionVoList.add(checkQuestionVo);
        }
        // 将多选题列表设置到ExamQuestionTypeVo对象的相应属性中
        examQuestionTypeVo.setExamQuestionSelectVoCheckList(checkQuestionVoList);

        // 初始化判断题的列表
        List<ExamQuestionSelectVo> judgeQuestionVoList = new ArrayList<>();
        // 从数据库中获取所有判断题（通过题型ID筛选），这里的QuestionEnum.JUDGE.getId()返回判断题的ID
        List<Question> judgeQuestionList = questionRepository.findByQuestionTypeId(QuestionEnum.JUDGE.getId());
        // 遍历所有判断题
        for (Question question : judgeQuestionList) {
            // 为每个判断题创建一个ExamQuestionSelectVo对象
            ExamQuestionSelectVo judgeQuestionVo = new ExamQuestionSelectVo();
            // 将Question对象的属性复制到judgeQuestionVo对象中
            BeanUtils.copyProperties(question, judgeQuestionVo);
            // 将复制后的对象添加到判断题的列表中
            judgeQuestionVoList.add(judgeQuestionVo);
        }
        // 将判断题列表设置到ExamQuestionTypeVo对象的相应属性中
        examQuestionTypeVo.setExamQuestionSelectVoJudgeList(judgeQuestionVoList);
        // 返回包含所有题型信息的ExamQuestionTypeVo对象
        return examQuestionTypeVo;
    }

    @Override
    public Exam create(ExamCreateVo examCreateVo, String userId) {
        // 创建一个新的考试对象
        Exam exam = new Exam();
        // 将前端传来的考试创建对象（ExamCreateVo）的属性复制到新创建的考试对象中
        BeanUtils.copyProperties(examCreateVo, exam);
        // 为考试生成一个唯一的ID
        exam.setExamId(IdUtil.simpleUUID());
        // 设置考试的创建者ID
        exam.setExamCreatorId(userId);
        // 设置考试的创建时间和更新时间（这里都设置为当前时间）
        exam.setCreateTime(new Date());
        exam.setUpdateTime(new Date());
        // Todo:这两个日志后面是要在前端传入的，这里暂时定为当前日期
        // Todo: 考试的开始和结束日期应该是前端传入的，这里暂时使用当前日期作为占位符
        exam.setExamStartDate(new Date());
        exam.setExamEndDate(new Date());
        // 初始化字符串，用于拼接单选、多选和判断题的ID
        String radioIdsStr = "";
        String checkIdsStr = "";
        String judgeIdsStr = "";
        // 从前端传来的对象中获取单选、多选和判断题的列表
        List<ExamQuestionSelectVo> radios = examCreateVo.getRadios();
        List<ExamQuestionSelectVo> checks = examCreateVo.getChecks();
        List<ExamQuestionSelectVo> judges = examCreateVo.getJudges();
        // 初始化计数器，用于记录每种类型题目的数量
        int radioCnt = 0, checkCnt = 0, judgeCnt = 0;
        // 遍历单选题的列表，拼接被选中的题目ID，并计数
        for (ExamQuestionSelectVo radio : radios) {
            if (radio.getChecked()) {
                radioIdsStr += radio.getQuestionId() + "-";
                radioCnt++;
            }
        }
        // 替换最后一个分隔符，避免字符串末尾出现多余的分隔符
        radioIdsStr = replaceLastSeparator(radioIdsStr);
        // 遍历多选题的列表，拼接被选中的题目ID，并计数
        for (ExamQuestionSelectVo check : checks) {
            if (check.getChecked()) {
                checkIdsStr += check.getQuestionId() + "-";
                checkCnt++;
            }
        }
        // 替换最后一个分隔符
        checkIdsStr = replaceLastSeparator(checkIdsStr);
        // 遍历判断题的列表，拼接被选中的题目ID，并计数
        for (ExamQuestionSelectVo judge : judges) {
            if (judge.getChecked()) {
                judgeIdsStr += judge.getQuestionId() + "-";
                judgeCnt++;
            }
        }
        // 替换最后一个分隔符
        judgeIdsStr = replaceLastSeparator(judgeIdsStr);
        // 将所有被选中的题目ID拼接成一个字符串，并设置到考试对象中
        exam.setExamQuestionIds(radioIdsStr + "-" + checkIdsStr + "-" + judgeIdsStr);
        // 分别设置单选、多选和判断题的ID串到考试对象中
        exam.setExamQuestionIdsRadio(radioIdsStr);
        exam.setExamQuestionIdsCheck(checkIdsStr);
        exam.setExamQuestionIdsJudge(judgeIdsStr);

        // 计算考试的总分数
        // 总分数 = 单选题数量 * 单选题每题分数 + 多选题数量 * 多选题每题分数 + 判断题数量 * 判断题每题分数
        int examScore = radioCnt * exam.getExamScoreRadio() + checkCnt * exam.getExamScoreCheck() + judgeCnt * exam.getExamScoreJudge();
        exam.setExamScore(examScore);
        // 将考试对象保存到数据库中
        examRepository.save(exam);
        // 返回创建好的考试对象
        return exam;
    }
    // 重写update方法，用于更新考试信息
    @Override
    public Exam update(ExamVo examVo, String userId) {
        // 创建一个新的Exam对象
        Exam exam = new Exam();
        // 将examVo对象的属性复制到exam对象中
        BeanUtils.copyProperties(examVo, exam);
        // 设置考试的更新人为当前的用户ID
        exam.setExamCreatorId(userId);
        // 记录考试的更新日期为当前时间
        exam.setUpdateTime(new Date());
        // 初始化字符串变量，用于存储不同类型的题目ID
        String radioIdsStr = "";
        String checkIdsStr = "";
        String judgeIdsStr = "";
        // 获取单选、多选和判断题的列表
        List<ExamQuestionSelectVo> radios = examVo.getExamQuestionSelectVoRadioList();
        List<ExamQuestionSelectVo> checks = examVo.getExamQuestionSelectVoCheckList();
        List<ExamQuestionSelectVo> judges = examVo.getExamQuestionSelectVoJudgeList();
        // 初始化计数器，用于统计每种类型题目的数量
        int radioCnt = 0, checkCnt = 0, judgeCnt = 0;
        // 遍历单选题目列表，拼接被选中的题目ID
        for (ExamQuestionSelectVo radio : radios) {
            if (radio.getChecked()) {
                radioIdsStr += radio.getQuestionId() + "-";
                radioCnt++;
            }
        }
        // 移除最后一个多余的"-"
        radioIdsStr = replaceLastSeparator(radioIdsStr);
        // 遍历多选题目列表，拼接被选中的题目ID
        for (ExamQuestionSelectVo check : checks) {
            if (check.getChecked()) {
                checkIdsStr += check.getQuestionId() + "-";
                checkCnt++;
            }
        }
        // 移除最后一个多余的"-"
        checkIdsStr = replaceLastSeparator(checkIdsStr);
        // 遍历判断题目列表，拼接被选中的题目ID
        for (ExamQuestionSelectVo judge : judges) {
            if (judge.getChecked()) {
                judgeIdsStr += judge.getQuestionId() + "-";
                judgeCnt++;
            }
        }
        // 移除最后一个多余的"-"
        judgeIdsStr = replaceLastSeparator(judgeIdsStr);
        // 设置考试的题目ID字符串，包括单选、多选和判断题
        exam.setExamQuestionIds(radioIdsStr + "-" + checkIdsStr + "-" + judgeIdsStr);
        // 分别设置各类题目的ID字符串
        exam.setExamQuestionIdsRadio(radioIdsStr);
        exam.setExamQuestionIdsCheck(checkIdsStr);
        exam.setExamQuestionIdsJudge(judgeIdsStr);

        // 计算考试的总分数
        int examScore = radioCnt * exam.getExamScoreRadio() + checkCnt * exam.getExamScoreCheck() + judgeCnt * exam.getExamScoreJudge();
        exam.setExamScore(examScore);
        // 保存更新后的考试信息
        examRepository.save(exam);
        // 返回更新后的考试信息
        return exam;
    }
    // 重写getExamCardList方法，用于获取所有考试的信息列表
    @Override
    public List<ExamCardVo> getExamCardList() {
        // 从数据库中获取所有的考试信息
        List<Exam> examList = examRepository.findAll();
        // 创建一个新的列表，用于存储转换后的考试卡片信息
        List<ExamCardVo> examCardVoList = new ArrayList<>();
        // 遍历考试信息列表，将每个考试信息转换为考试卡片信息
        for (Exam exam : examList) {
            ExamCardVo examCardVo = new ExamCardVo();
            BeanUtils.copyProperties(exam, examCardVo);
            // 将转换后的考试卡片信息添加到列表中
            examCardVoList.add(examCardVo);
        }
        // 返回考试卡片信息列表
        return examCardVoList;
    }
    // 重写getExamDetail方法，用于获取指定考试的详细信息
    @Override
    public ExamDetailVo getExamDetail(String id) {
        // 根据ID从数据库中获取考试信息
        Exam exam = examRepository.findById(id).orElse(null);
        // 创建一个新的考试详细信息对象
        ExamDetailVo examDetailVo = new ExamDetailVo();
        // 设置考试信息
        examDetailVo.setExam(exam);
        // 确保考试信息不为空（这里使用assert进行断言，但通常建议使用更优雅的异常处理）
        assert exam != null;
        // 将考试的各类题目ID字符串分割为数组，并设置到考试详细信息对象中
        examDetailVo.setRadioIds(exam.getExamQuestionIdsRadio().split("-"));
        examDetailVo.setCheckIds(exam.getExamQuestionIdsCheck().split("-"));
        examDetailVo.setJudgeIds(exam.getExamQuestionIdsJudge().split("-"));
        // 返回考试详细信息对象
        return examDetailVo;
    }
    // 重写judge方法，用于判断用户考试的得分情况
    @Override
    public ExamRecord judge(String userId, String examId, HashMap<String, List<String>> answersMap) {
        // 开始考试判分~~~
        // 1. 获取考试详情对象和考试对象的选项数组
        ExamDetailVo examDetailVo = getExamDetail(examId);// 获取考试详情
        Exam exam = examDetailVo.getExam();// 获取考试对象
        // 2.然后获取该考试下所有题目信息
        List<String> questionIds = new ArrayList<>();// 存储题目ID的列表
        // 2.1 获取各种题型的题目ID列表
        List<String> radioIdList = Arrays.asList(examDetailVo.getRadioIds());// 单选题ID列表
        List<String> checkIdList = Arrays.asList(examDetailVo.getCheckIds());// 多选题ID列表
        List<String> judgeIdList = Arrays.asList(examDetailVo.getJudgeIds());// 判断题ID列表
        questionIds.addAll(radioIdList);// 将单选题ID添加到总列表
        questionIds.addAll(checkIdList);// 将多选题ID添加到总列表
        questionIds.addAll(judgeIdList);// 将判断题ID添加到总列表
        // 2.2 获取每种题型的分数
        int radioScore = exam.getExamScoreRadio();// 单选题每题分数
        int checkScore = exam.getExamScoreCheck();// 多选题每题分数
        int judgeScore = exam.getExamScoreJudge();// 判断题每题分数
        // 2.3 根据题目ID获取所有题目对象，便于后续操作
        List<Question> questionList = questionRepository.findAllById(questionIds);// 查询所有题目
        Map<String, Question> questionMap = new HashMap<>();// 使用Map存储题目ID和题目对象的映射
        for (Question question : questionList) {
            questionMap.put(question.getQuestionId(), question);// 将题目添加到Map中
        }
        // 3. 根据正确答案，用户作答信息进行判分
        Set<String> questionIdsAnswer = answersMap.keySet();// 获取用户作答的题目ID集合
        // 存储每个题目的得分情况
        Map<String, Integer> judgeMap = new HashMap<>();
        // 用于构建用户作答选项的字符串，用于查看考试详情
        StringBuilder answerOptionIdsSb = new StringBuilder();
        // 用户考试的总分
        int totalScore = 0;
        // 遍历用户作答的题目ID
        for (String questionId : questionIdsAnswer) {
            // 获取题目对象
            Question question = questionMap.get(questionId);
            // 获取题目正确答案的选项ID，并处理格式
            String questionAnswerOptionIds = replaceLastSeparator(question.getQuestionAnswerOptionIds());
            List<String> questionAnswerOptionIdList = Arrays.asList(questionAnswerOptionIds.split("-"));
            Collections.sort(questionAnswerOptionIdList);// 对选项进行排序
            String answerStr = listConcat(questionAnswerOptionIdList);// 将选项列表转换为字符串
            // 获取用户作答的选项ID列表，并处理格式
            List<String> questionUserOptionIdList = answersMap.get(questionId);
            Collections.sort(questionUserOptionIdList);// 对选项进行排序
            String userStr = listConcat(questionUserOptionIdList);// 将选项列表转换为字符串
            // 判断用户作答是否正确
            if (answerStr.equals(userStr)) {
                // 作答正确，根据题型设置分数
                int score = 0;
                if (radioIdList.contains(questionId)) {
                    score = radioScore;// 单选题分数
                }
                if (checkIdList.contains(questionId)) {
                    score = checkScore;// 多选题分数
                }
                if (judgeIdList.contains(questionId)) {
                    score = judgeScore;// 判断题分数
                }
                // 累加总分
                totalScore += score;
                // 记录答案正确及用户作答选项
                answerOptionIdsSb.append(questionId + "@True_" + userStr + "$");
                judgeMap.put(questionId, score);// 记录题目得分
            } else {
                // 作答错误，记0分
                answerOptionIdsSb.append(questionId + "@False_" + userStr + "$");
                judgeMap.put(questionId, 0);// 记录题目得分为0
            }
        }
        // 4. 计算得分，记录考试结果，并保存到数据库中
        ExamRecord examRecord = new ExamRecord();// 创建考试记录对象
        examRecord.setExamRecordId(IdUtil.simpleUUID());// 设置考试记录ID
        examRecord.setExamId(examId);// 设置考试ID
        // 设置用户作答选项字符串，注意处理格式
        examRecord.setAnswerOptionIds(replaceLastSeparator(answerOptionIdsSb.toString()));
        examRecord.setExamJoinerId(userId);// 设置考生ID
        examRecord.setExamJoinDate(new Date());// 设置考试日期
        examRecord.setExamJoinScore(totalScore);// 设置考试总分
        examRecordRepository.save(examRecord);// 保存考试记录到数据库
        return examRecord;// 返回考试记录对象
    }
    // 重写方法，用于获取指定用户的考试记录列表
    @Override
    public List<ExamRecordVo> getExamRecordList(String userId) {
        // 通过用户ID从数据库中查询该用户参与的考试记录，按考试加入日期降序排列
        List<ExamRecord> examRecordList = examRecordRepository.findByExamJoinerIdOrderByExamJoinDateDesc(userId);
        // 创建一个用于存放转换后的考试记录视图对象的列表
        List<ExamRecordVo> examRecordVoList = new ArrayList<>();
        // 遍历查询到的考试记录
        for (ExamRecord examRecord : examRecordList) {
            ExamRecordVo examRecordVo = new ExamRecordVo();// 创建一个新的考试记录视图对象
            // 根据考试记录中的考试ID查询考试详情
            Exam exam = examRepository.findById(examRecord.getExamId()).orElse(null);
            // 设置考试详情到视图对象中
            examRecordVo.setExam(exam);
            // 根据用户ID查询用户详情
            User user = userRepository.findById(userId).orElse(null);
            // 设置用户详情到视图对象中
            examRecordVo.setUser(user);
            // 设置考试记录到视图对象中
            examRecordVo.setExamRecord(examRecord);
            // 将视图对象添加到列表中
            examRecordVoList.add(examRecordVo);
        }
        return examRecordVoList;
    }
    // 重写方法，用于获取特定考试记录的详细信息
    @Override
    public RecordDetailVo getRecordDetail(String recordId) {
        // 通过记录ID从数据库中查询考试记录
        ExamRecord record = examRecordRepository.findById(recordId).orElse(null);
        // 创建一个新的记录详情视图对象
        RecordDetailVo recordDetailVo = new RecordDetailVo();
        // 设置考试记录到视图对象中
        recordDetailVo.setExamRecord(record);
        // 初始化用于存储用户答案和结果的哈希映射
        HashMap<String, List<String>> answersMap = new HashMap<>();
        HashMap<String, String> resultsMap = new HashMap<>();
        // 确保查询到的考试记录不为空
        assert record != null;
        // 获取用户答案字符串，以$分隔每个题目的答案
        String answersStr = record.getAnswerOptionIds();
        // 按$分割字符串，得到每个题目的答案
        String[] questionArr = answersStr.split("[$]");
        // 遍历每个题目的答案
        for (String questionStr : questionArr) {
            System.out.println(questionStr);
            // 题目字符串格式为：题目标题@结果_选项1-选项2-...，分割得到题目和选项
            String[] questionTitleResultAndOption = questionStr.split("_");
            String[] questionTitleAndResult = questionTitleResultAndOption[0].split("@");
            String[] questionOptions = questionTitleResultAndOption[1].split("-");
            // 将题目和答案选项存入answersMap
            answersMap.put(questionTitleAndResult[0], Arrays.asList(questionOptions));
            // 将题目和结果（True/False）存入resultsMap
            resultsMap.put(questionTitleAndResult[0], questionTitleAndResult[1]);
        }
        // 设置答案和结果映射到视图对象中
        recordDetailVo.setAnswersMap(answersMap);
        recordDetailVo.setResultsMap(resultsMap);
        // 获取考试详情，用于获取所有题目的ID
        ExamDetailVo examDetailVo = getExamDetail(record.getExamId());
        // 初始化一个列表用于存放所有题目的ID
        List<String> questionIdList = new ArrayList<>();
        // 将单选、多选、判断题的ID添加到列表中
        questionIdList.addAll(Arrays.asList(examDetailVo.getRadioIds()));
        questionIdList.addAll(Arrays.asList(examDetailVo.getCheckIds()));
        questionIdList.addAll(Arrays.asList(examDetailVo.getJudgeIds()));
        // 根据题目ID列表从数据库中查询所有题目
        List<Question> questionList = questionRepository.findAllById(questionIdList);
        // 初始化一个哈希映射用于存储正确答案
        HashMap<String, List<String>> answersRightMap = new HashMap<>();
        // 遍历所有题目，获取每个题目的正确答案
        for (Question question : questionList) {
            // 去除答案字符串末尾可能出现的特殊字符（如多余的"-"）
            String questionAnswerOptionIdsStr = replaceLastSeparator(question.getQuestionAnswerOptionIds());
            // 分割字符串得到正确答案
            String[] questionAnswerOptionIds = questionAnswerOptionIdsStr.split("-");
            // 将题目ID和正确答案存入answersRightMap
            answersRightMap.put(question.getQuestionId(), Arrays.asList(questionAnswerOptionIds));
        }
        // 设置正确答案映射到视图对象中
        recordDetailVo.setAnswersRightMap(answersRightMap);
        // 返回记录详情视图对象
        return recordDetailVo;
    }

    /**
     * 替换掉字符串的最后一个分隔符（如果它是-、_或$）。
     *
     * @param str 要处理的原始字符串。
     * @return 替换掉最后一个分隔符后的字符串。
     */
    private String replaceLastSeparator(String str) {
        // 获取字符串的最后一个字符。
        String lastChar = str.substring(str.length() - 1);
        // 检查最后一个字符是否是分隔符（-、_或$）。
        if ("-".equals(lastChar) || "_".equals(lastChar) || "$".equals(lastChar)) {
            // 如果最后一个字符是分隔符，则使用StrUtil.sub方法移除它。
            str = StrUtil.sub(str, 0, str.length() - 1);
        }
        // 返回处理后的字符串。
        return str;
    }

    /**
     * 将字符串列表中的元素用-连接起来，并确保最终字符串不以-结尾。
     *
     * @param strList 包含要连接的字符串的列表。
     * @return 拼接好的字符串，确保已经去掉了末尾的-。
     */
    private String listConcat(List<String> strList) {
        // 创建一个StringBuilder对象，用于高效地构建最终的字符串
        StringBuilder sb = new StringBuilder();
        // 遍历字符串列表。
        for (String str : strList) {
            // 将当前字符串添加到StringBuilder中。
            sb.append(str);
            // 在当前字符串后添加一个-作为分隔符。
            sb.append("-");
        }
        // 调用replaceLastSeparator方法，移除StringBuilder中拼接字符串的最后一个-。
        return replaceLastSeparator(sb.toString());
    }
}
