package com.sundot.airport.exam.common.manager.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.sundot.airport.exam.common.CheckUtil;
import com.sundot.airport.exam.common.manager.GradeManager;
import com.sundot.airport.exam.common.model.GradeQuesResult;
import com.sundot.airport.exam.common.model.GradeResult;
import com.sundot.airport.exam.common.model.params.UserOption;
import com.sundot.airport.exam.common.service.ExamRecOptService;
import com.sundot.airport.exam.common.service.ExamRecService;
import com.sundot.airport.exam.domain.*;
import com.sundot.airport.exam.enums.CloudSchoolBizExceptionEnum;
import com.sundot.airport.exam.enums.CorrectStatusEnum;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.sundot.airport.exam.enums.QuesCompScoreTypeEnum.*;
import static com.sundot.airport.exam.enums.QuesTypeEnum.*;

/**
 * <b>功能名：GradeManagerImpl</b><br>
 * <b>说明：</b> 判卷通用业务处理实现层 <br>
 * <b>著作权：</b> Copyright (C) 2021 HUIFANEDU  CORPORATION<br>
 * <b>修改履历：</b><br>
 *
 * @author 2021-07-07 Mokou
 */
@Service
public class GradeManagerImpl implements GradeManager {
    private static final Logger log = LoggerFactory.getLogger(GradeManagerImpl.class);

    @Resource
    private ExamRecService examRecService;
    @Resource
    private ExamRecOptService examRecOptService;

    @Override
    public GradeResult grade(String erId, List<UserOption> userOptions) {
        // 查询考试快照
        Optional<ExamRec> examRec = examRecService.get(erId);
        CheckUtil.boolCheck(!examRec.isPresent(), CloudSchoolBizExceptionEnum.EXAMREC_NOT_FOUND);

        // 查询选项快照 结果可加工得到 计分规则、题目、全部答案(包含正确和错误答案)
        List<ExamRecOpt> examRecOptList = examRecOptService.byErId(erId);

        // 初始容量(不精确)
        int initialCap = (int) (examRecOptList.size() / 0.75) + 1;
        // 保存题目ID与用户选项的关系
        Map<String, List<UserOption>> quesIdUserOptMap = new HashMap<>(initialCap);
        // 保存题目ID与积分规则的映射关系
        Map<String, TestPaperQues> quesIdRuleMap = new HashMap<>(initialCap);
        // 保存题目ID与题目对象的映射关系
        Map<String, Ques> quesIdQuesMap = new HashMap<>(initialCap);
        // 保存题目ID与选项列表对象的映射关系
        Map<String, List<QuesOpt>> quesIdOptMap = new HashMap<>(initialCap);
        // 保存题目ID与正确选项列表对象的映射关系
        Map<String, List<QuesOpt>> quesIdCorrectOptMap = new HashMap<>(initialCap);
        // 保存题目ID与选项快照列表对象的映射关系 用于更新选项快照
        Map<String, List<ExamRecOpt>> quesIdExamRecOptMap = new HashMap<>(initialCap);
        // 保存题目ID与正确情况的映射关系
        Map<String, CorrectStatusEnum> quesIdCorrectStatusMap = new HashMap<>(initialCap);
        // 保存题目ID与填空题答对空的下标的映射关系 Map<题目ID, Map<选项序号, List<正确的空的下标>>>
        Map<String, Map<Integer, List<Integer>>> quesIdCorrectIdxMap = new HashMap<>(initialCap);

        for (UserOption userOpt : userOptions) {
            quesIdUserOptMap.computeIfAbsent(userOpt.getQuId(), k -> new ArrayList<>());
            quesIdUserOptMap.get(userOpt.getQuId()).add(userOpt);
        }

        for (ExamRecOpt examRecOpt : examRecOptList) {
            QuesOpt opt = translateExamRecOptToQuesOpt(examRecOpt);
            // 保存全部选项
            quesIdOptMap.computeIfAbsent(opt.getQuId(), k -> new ArrayList<>());
            quesIdOptMap.get(opt.getQuId()).add(opt);

            // 保存正确选项
            quesIdCorrectOptMap.computeIfAbsent(opt.getQuId(), k -> new ArrayList<>());
            if (opt.getQoCorrect()) {
                quesIdCorrectOptMap.get(opt.getQuId()).add(opt);
            }

            // 保存计分规则
            TestPaperQues rule = translateExamRecOptToTestPaperQues(examRecOpt);
            quesIdRuleMap.putIfAbsent(rule.getQuId(), rule);

            // 保存题目
            Ques ques = translateExamRecOptToQues(examRecOpt);
            quesIdQuesMap.putIfAbsent(ques.getQuId(), ques);

            // 保存选项快照列表
            quesIdExamRecOptMap.computeIfAbsent(opt.getQuId(), k -> new ArrayList<>());
            quesIdExamRecOptMap.get(opt.getQuId()).add(examRecOpt);
        }

        // 总得分
        int totalScore = 0;
        // 正确题目数
        int correctNum = 0;
        // 部分正确
        int partCorrectNum = 0;
        // 错误题目数
        int incorrectNum = 0;
        // 未作答题目数
        int notFilledNum = 0;

        for (String quId : quesIdUserOptMap.keySet()) {
            Ques ques = quesIdQuesMap.get(quId);
            if (ques == null) {
                continue;
            }

            TestPaperQues rule = quesIdRuleMap.get(quId);
            List<QuesOpt> correctOpt = quesIdCorrectOptMap.get(quId);
            List<UserOption> userOpt = quesIdUserOptMap.get(quId);

            // 判题结果
            GradeQuesResult gradeQuesResult;

            if (rule != null) {
                if (rule.getTpqTotalScore() == null) {
                    rule.setTpqTotalScore(0);
                }
                if (rule.getTpqLeftScore() == null) {
                    rule.setTpqLeftScore(0);
                }
                if (rule.getTpqRightScore() == null) {
                    rule.setTpqRightScore(0);
                }

                switch (ques.getQuType()) {
                    case SINGLE: {
                        gradeQuesResult = gradeSingle(rule, correctOpt, userOpt);
                        break;
                    }
                    case MULTIPLE: {
                        gradeQuesResult = gradeMultiple(rule, correctOpt, userOpt);
                        break;
                    }
                    case JUDGMENT: {
                        gradeQuesResult = gradeJudge(rule, correctOpt, userOpt);
                        break;
                    }
                    case FILL_BLANK: {
                        gradeQuesResult = gradeFillBlank(rule, correctOpt, userOpt);
                        break;
                    }
                    case CONNECTION: {
                        gradeQuesResult = gradeConnect(rule, correctOpt, userOpt);
                        break;
                    }
                    default: {
                        continue;
                    }
                }

                switch (gradeQuesResult.getCorrectStatus()) {
                    case ALL_CORRECT: {
                        correctNum++;
                        break;
                    }
                    case PART_CORRECT: {
                        partCorrectNum++;
                        break;
                    }
                    case INCORRECT: {
                        incorrectNum++;
                        break;
                    }
                    case NOT_FILLED: {
                        notFilledNum++;
                        break;
                    }
                    default: {
                    }
                }
                totalScore += gradeQuesResult.getScore();
                quesIdCorrectStatusMap.put(quId, gradeQuesResult.getCorrectStatus());
                quesIdCorrectIdxMap.put(quId, gradeQuesResult.getSortCorrectIdxMap());
            }
        }

        // 需要更新的用户答案快照列表
        List<ExamRecOpt> userAnswerRecList = getUserAnswerRecList(userOptions, quesIdQuesMap, quesIdExamRecOptMap, quesIdCorrectOptMap, quesIdCorrectStatusMap, quesIdCorrectIdxMap);

        GradeResult res = new GradeResult();
        res.setUserScore(totalScore);
        res.setCorrectCount(correctNum);
        res.setPartCorrectCount(partCorrectNum);
        res.setIncorrectCount(incorrectNum);
        res.setNotFilledCount(notFilledNum);
        res.setQuestionCount(quesIdUserOptMap.keySet().size());
        res.setExamRecOptList(userAnswerRecList);
        return res;
    }

    /**
     * <b>方法名: </b> checkOptIsNull <br>
     * <b>说明: 检查填空题是否全部没填</b> <br>
     *
     * @param userOpts 用户选项
     * @return java.util.List<com.sundot.airport.exam.common.model.params.UserOption><br>
     * <b>修改履历: </b>
     * @author 2021/7/31 lity
     */
    private boolean checkOptIsNull(List<UserOption> userOpts) {
        if (CollectionUtils.isEmpty(userOpts)) {
            return true;
        }

        for (UserOption userOpt : userOpts) {
            String eroConnNo = userOpt.getEroConnNo();
            if (StringUtils.isBlank(eroConnNo)) {
                continue;
            }

            List<String> eroConnNoList = jsonArr2List(eroConnNo);
            boolean f = eroConnNoList.stream().anyMatch(StringUtils::isNotBlank);
            if (f) {
                return false;
            }
        }
        return true;

    }

    /**
     * <b>方法名: </b> gradeSingle <br>
     * <b>说明: </b> 判卷 单选题 返回得分<br>
     *
     * @param rule        TestPaperQues 得分规则
     * @param correctOpts List<QuesOpt> 正确答案
     * @param userOpts    List<UserOption> 用户答案
     * @return Integer
     * <b>修改履历: </b>
     * @author 2021/7/6 Mokou
     */
    private GradeQuesResult gradeSingle(TestPaperQues rule, List<QuesOpt> correctOpts, List<UserOption> userOpts) {
        GradeQuesResult res = new GradeQuesResult();

        // 取出正确答案
        QuesOpt correctOpt;
        if (correctOpts != null && correctOpts.size() > 0) {
            correctOpt = correctOpts.get(0);
        } else {
            // 选项列表为空 未作答
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
            return res;
        }

        // 取出用户答案
        UserOption userOption;
        if (userOpts != null && userOpts.size() == 1) {
            userOption = userOpts.get(0);
        } else {
            // 单选题出现多选 按错误处理
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
            return res;
        }

        // 是否答对
        if (StringUtils.isBlank(userOption.getEroConnNo())) {
            // 选项值非法 按错误处理
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.NOT_FILLED);
            return res;
        }

        Integer userChoice = Integer.valueOf(userOption.getEroConnNo());
        boolean bingo = userChoice.equals(correctOpt.getSort());
        if (bingo) {
            res.setScore(rule.getTpqTotalScore() == null ? 0 : rule.getTpqTotalScore());
            res.setCorrectStatus(CorrectStatusEnum.ALL_CORRECT);
            return res;
        } else {
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
            return res;
        }
    }

    /**
     * <b>方法名: </b> gradeMultiple <br>
     * <b>说明: </b> 判卷 多选 返回得分<br>
     *
     * @param rule        TestPaperQues 得分规则
     * @param correctOpts List<QuesOpt> 正确答案
     * @param userOpts    List<UserOption> 用户答案
     * @return Integer
     * <b>修改履历: </b>
     * @author 2021/7/6 Mokou
     */
    private GradeQuesResult gradeMultiple(TestPaperQues rule, List<QuesOpt> correctOpts, List<UserOption> userOpts) {
        GradeQuesResult res = new GradeQuesResult();

        // 无正确答案 题目编辑问题 按错误处理
        if (correctOpts == null || correctOpts.size() == 0) {
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
            return res;
        }

        if (userOpts == null || userOpts.size() == 0) {
            // 选项列表为空 未作答
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.NOT_FILLED);
            return res;
        }

        // 取出不重复的用户答案
        List<Integer> userChoiceList = new ArrayList<>();
        for (UserOption userOpt : userOpts) {
            if (!userChoiceList.contains(userOpt.getEroSort())) {
                userChoiceList.add(userOpt.getEroSort());
            }
        }

        // 取出不重复的正确答案
        List<Integer> correctChoiceList = new ArrayList<>();
        for (QuesOpt correctOpt : correctOpts) {
            if (!correctChoiceList.contains(correctOpt.getSort())) {
                correctChoiceList.add(correctOpt.getSort());
            }
        }

        if (correctChoiceList.containsAll(userChoiceList) && correctChoiceList.size() == userChoiceList.size()) {
            // 用户答案与正确答案完全包含 且大小相同 完全正确
            res.setScore(rule.getTpqTotalScore());
            res.setCorrectStatus(CorrectStatusEnum.ALL_CORRECT);
            return res;
        } else {
            int incorrectNum = 0;
            int correctNum = 0;
            for (Integer userChoice : userChoiceList) {
                if (correctChoiceList.contains(userChoice)) {
                    correctNum++;
                } else {
                    incorrectNum++;
                }
            }

            if (incorrectNum > 0) {
                // 有错选 不得分
                res.setScore(0);
                if (correctNum > 0) {
                    // 有正确 部分正确
                    res.setCorrectStatus(CorrectStatusEnum.PART_CORRECT);
                } else {
                    // 无正确 全错
                    res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
                }
                return res;
            }

            // 漏选
            res.setCorrectStatus(CorrectStatusEnum.PART_CORRECT);
            switch (rule.getTpqCompType()) {
                case LEFT: {
                    // 返回漏选得分
                    res.setScore(rule.getTpqLeftScore());
                    return res;
                }
                case RIGHT: {
                    // 总分-漏选个数*漏选扣分
                    int totalScore = rule.getTpqTotalScore();
                    totalScore -= (correctChoiceList.size() - correctNum) * rule.getTpqRightScore();
                    totalScore = Math.max(totalScore, 0);
                    res.setScore(totalScore);
                    return res;
                }
                default: {
                    // 未定义计分规则 漏选按0分处理
                    res.setScore(0);
                    return res;
                }
            }
        }
    }

    /**
     * <b>方法名: </b> gradeJudge <br>
     * <b>说明: </b> 判卷 判断 返回得分<br>
     *
     * @param rule        TestPaperQues 得分规则
     * @param correctOpts List<QuesOpt> 正确答案
     * @param userOpts    List<UserOption> 用户答案
     * @return Integer
     * <b>修改履历: </b>
     * @author 2021/7/6 Mokou
     */
    private GradeQuesResult gradeJudge(TestPaperQues rule, List<QuesOpt> correctOpts, List<UserOption> userOpts) {
        GradeQuesResult res = new GradeQuesResult();

        // 取出正确答案
        QuesOpt correctOpt;
        if (correctOpts != null && correctOpts.size() > 0) {
            correctOpt = correctOpts.get(0);
        } else {
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
            return res;
        }

        // 取出用户答案
        UserOption userOption;
        if (userOpts != null && userOpts.size() == 1) {
            userOption = userOpts.get(0);
        } else {
            // 判断题等同于 双选项单选题，出现多选 按错误处理
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
            return res;
        }

        // 是否答对
        if (StringUtils.isBlank(userOption.getEroConnNo())) {
            // 选项值非法 按错误处理
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.NOT_FILLED);
            return res;
        }
        Integer userChoice = Integer.valueOf(userOption.getEroConnNo());
        boolean bingo = userChoice.equals(correctOpt.getSort());
        if (bingo) {
            res.setScore(rule.getTpqTotalScore());
            res.setCorrectStatus(CorrectStatusEnum.ALL_CORRECT);
            return res;
        } else {
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
            return res;
        }
    }

    /**
     * <b>方法名: </b> gradeFillBlank <br>
     * <b>说明: </b> 判卷 填空 返回得分<br>
     *
     * @param rule        TestPaperQues 得分规则
     * @param correctOpts List<QuesOpt> 正确答案
     * @param userOpts    List<UserOption> 用户答案
     * @return Integer
     * <b>修改履历: </b>
     * @author 2021/7/6 Mokou
     */
    private GradeQuesResult gradeFillBlank(TestPaperQues rule, List<QuesOpt> correctOpts, List<UserOption> userOpts) {
        GradeQuesResult res = new GradeQuesResult();

        // 无正确答案 题目编辑问题 按错误处理
        if (correctOpts == null || correctOpts.size() == 0) {
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
            return res;
        }

        // 用户未作答
        if (checkOptIsNull(userOpts)) {
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.NOT_FILLED);
            return res;
        }

        // 填空题组号与正确答案的映射关系 答案去掉所有空格、换行 并去重
        Map<Integer, List<String>> sortAnswerMap = new HashMap<>((int) (correctOpts.size() / 0.75) + 1);
        // 填空题组号与用户原始答案的映射关系 答案去掉所有空格、换行 不去重保留重复答案
        Map<Integer, List<String>> sortRawAnswerMap = new HashMap<>((int) (correctOpts.size() / 0.75) + 1);
        for (QuesOpt quesOpt : correctOpts) {
            sortAnswerMap.put(
                    quesOpt.getSort(),
                    jsonArr2List(quesOpt.getQoOption())
                            .stream()
                            .map(item -> item.replaceAll(" ", "").replaceAll("\\\\n", ""))
                            .distinct()
                            .collect(Collectors.toList())
            );
            sortRawAnswerMap.put(
                    quesOpt.getSort(),
                    jsonArr2List(quesOpt.getQoOption())
                            .stream()
                            .map(item -> item.replaceAll(" ", "").replaceAll("\\\\n", ""))
                            .collect(Collectors.toList())
            );
        }

        // 填空题组号与用户答案的映射关系 答案去掉所有空格、换行 并去重
        Map<Integer, List<String>> sortUserAnswerMap = new HashMap<>((int) (userOpts.size() / 0.75) + 1);
        for (UserOption userOption : userOpts) {
            sortUserAnswerMap.put(
                    userOption.getEroSort(),
                    jsonArr2List(userOption.getEroConnNo())
                            .stream()
                            .map(item -> item.replaceAll(" ", "").replaceAll("\\\\n", ""))
                            .distinct()
                            .collect(Collectors.toList())
            );
        }

        // 用户填对数量
        int correctNum = 0;
        // 答案总数量
        int allNum = 0;
        for (Integer sort : sortAnswerMap.keySet()) {
            // 取出当前组号的正确答案列表
            List<String> correctAnswerList = sortAnswerMap.get(sort);
            // 取出当前组号的用户去重答案列表
            List<String> userDistinctAnswerList = sortUserAnswerMap.get(sort);
            // 取出当前组号的用户答案列表
            List<String> userAnswerList = sortRawAnswerMap.get(sort);

            if (correctAnswerList == null || correctAnswerList.size() == 0 || userDistinctAnswerList == null || userDistinctAnswerList.size() == 0) {
                continue;
            }

            allNum += correctAnswerList.size();

            // 用户答对的空的下标
            List<Integer> correctIdxList = new ArrayList<>();

            // 判断组内用户答案是否被正确答案包含 并计数
            for (String userAnswer : userDistinctAnswerList) {
                if (correctAnswerList.contains(userAnswer)) {
                    correctNum++;
                    int correctIdx = userAnswerList.indexOf(userAnswer);
                    if (correctIdx > -1) {
                        correctIdxList.add(correctIdx);
                    }
                }
            }
            res.getSortCorrectIdxMap().put(sort, correctIdxList);
        }

        if (correctNum == 0) {
            // 无正确答案 全错
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
        } else if (allNum > correctNum) {
            // 答案数 > 答对数 部分答对
            res.setCorrectStatus(CorrectStatusEnum.PART_CORRECT);
        } else {
            // 答对数 >= 答案数 全对
            res.setCorrectStatus(CorrectStatusEnum.ALL_CORRECT);
        }

        switch (rule.getTpqCompType()) {
            case LEFT: {
                // 正确数*每空分数
                res.setScore(Math.min(correctNum * rule.getTpqLeftScore(), rule.getTpqTotalScore()));
                return res;
            }
            case RIGHT: {
                int totalScore = rule.getTpqTotalScore();
                totalScore -= (allNum - correctNum) * rule.getTpqRightScore();
                totalScore = Math.max(totalScore, 0);
                res.setScore(totalScore);
                return res;
            }
            default: {
                // 未定义计分规则 按0分处理
                res.setScore(0);
                return res;
            }
        }
    }

    /**
     * <b>方法名: </b> jsonArr2List <br>
     * <b>说明: </b> jsonArray 转 List<String> <br>
     *
     * @param jsonArrStr String jsonArray字符串
     * @return List<String>
     * <b>修改履历: </b>
     * @author 2021/7/15 Mokou
     */
    private List<String> jsonArr2List(String jsonArrStr) {
        if (jsonArrStr == null) {
            return new ArrayList<>();
        }
        try {
            JSONArray jsonArray = JSONArray.parseArray(jsonArrStr);
            return jsonArray.toJavaList(String.class);
        } catch (Exception e) {
            return new ArrayList<>();
        }
    }

    /**
     * <b>方法名: </b> gradeConnect <br>
     * <b>说明: </b> 判卷 连线 返回得分<br>
     *
     * @param rule        TestPaperQues 得分规则
     * @param correctOpts List<QuesOpt> 正确答案
     * @param userOpts    List<UserOption> 用户答案
     * @return Integer
     * <b>修改履历: </b>
     * @author 2021/7/6 Mokou
     */
    private GradeQuesResult gradeConnect(TestPaperQues rule, List<QuesOpt> correctOpts, List<UserOption> userOpts) {
        GradeQuesResult res = new GradeQuesResult();

        // 无正确答案 题目编辑问题 按错误处理
        if (correctOpts == null || correctOpts.size() == 0) {
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
            return res;
        }

        // 用户未作答
        if (userOpts == null || userOpts.size() == 0) {
            res.setScore(0);
            res.setCorrectStatus(CorrectStatusEnum.NOT_FILLED);
            return res;
        }

        // 正确连线的左右对应关系
        Map<Integer, Integer> leftToRightMap = new HashMap<>((int) (correctOpts.size() / 0.75) + 1);
        for (QuesOpt quesOpt : correctOpts) {
            leftToRightMap.put(quesOpt.getSort(), quesOpt.getQoConnSort());
        }

        // 正确答案数量 5对5的连线 allCorrectNum=5
        int allCorrectNum = leftToRightMap.keySet().size();
        // 用户答对的数量
        int correctNum = 0;
        for (UserOption userOption : userOpts) {
            // 用户答案左侧编号
            Integer userLeft = userOption.getEroSort();
            // 用户答案右侧编号
            String eroConnNo = userOption.getEroConnNo();
            if (userLeft == null || StringUtils.isBlank(eroConnNo)) {
                // 出现null 视为本行连错
                continue;
            }

            Integer userRight = Integer.valueOf(userOption.getEroConnNo());
            if (leftToRightMap.get(userLeft).equals(userRight)) {
                correctNum++;
            }
        }

        if (correctNum == 0) {
            // 无正确答案 全错
            res.setCorrectStatus(CorrectStatusEnum.INCORRECT);
        } else if (allCorrectNum > correctNum) {
            // 答案数 > 答对数 部分答对
            res.setCorrectStatus(CorrectStatusEnum.PART_CORRECT);
        } else {
            // 答对数 >= 答案数 全对
            res.setCorrectStatus(CorrectStatusEnum.ALL_CORRECT);
        }

        switch (rule.getTpqCompType()) {
            case LEFT: {
                // 匹配正确数*每项得分
                res.setScore(Math.min(correctNum * rule.getTpqLeftScore(), rule.getTpqTotalScore()));
                return res;
            }
            case RIGHT: {
                // 总分-匹配错误数*漏选扣分
                int totalScore = rule.getTpqTotalScore();
                totalScore -= (allCorrectNum - correctNum) * rule.getTpqRightScore();
                totalScore = Math.max(totalScore, 0);
                res.setScore(totalScore);
                return res;
            }
            default: {
                // 未定义计分规则 按0分处理
                res.setScore(0);
                return res;
            }
        }
    }


    /**
     * <b>方法名: </b> translateExamRecOptToTestPaperQues <br>
     * <b>说明: </b> 提取选项快照中的计分规则 <br>
     *
     * @param examRecOpt ExamRecOpt 选项快照对象
     * @return TestPaperQues
     * <b>修改履历: </b>
     * @author 2021/7/16 Mokou
     */
    private TestPaperQues translateExamRecOptToTestPaperQues(ExamRecOpt examRecOpt) {
        TestPaperQues testPaperQues = new TestPaperQues();
        testPaperQues.setTpqTotalScore(examRecOpt.getEroTpqTotalScore());
        testPaperQues.setTpqCompType(examRecOpt.getEroTpqCompType());
        testPaperQues.setTpqLeftScore(examRecOpt.getEroTpqLeftScore());
        testPaperQues.setTpqRightScore(examRecOpt.getEroTpqRightScore());
        testPaperQues.setQuId(examRecOpt.getEroQuId());
        testPaperQues.setSort(examRecOpt.getEroTpqSort());
        return testPaperQues;
    }

    /**
     * <b>方法名: </b> translateExamRecOptToQues <br>
     * <b>说明: </b> 提取选项快照中的题目 <br>
     *
     * @param examRecOpt ExamRecOpt 选项快照对象
     * @return Ques
     * <b>修改履历: </b>
     * @author 2021/7/16 Mokou
     */
    private Ques translateExamRecOptToQues(ExamRecOpt examRecOpt) {
        Ques ques = new Ques();
        ques.setQuId(examRecOpt.getEroQuId());
        ques.setQuType(examRecOpt.getEroQuType());
        ques.setQuContent(examRecOpt.getEroQuContent());
        return ques;
    }

    /**
     * <b>方法名: </b> translateExamRecOptToQuesOpt <br>
     * <b>说明: </b> 提取选项快照中的选项 <br>
     *
     * @param examRecOpt ExamRecOpt 选项快照对象
     * @return QuesOpt
     * <b>修改履历: </b>
     * @author 2021/7/16 Mokou
     */
    private QuesOpt translateExamRecOptToQuesOpt(ExamRecOpt examRecOpt) {
        QuesOpt quesOpt = new QuesOpt();
        quesOpt.setQoCorrect(examRecOpt.getEroQoCorrect());
        quesOpt.setQoOptionType(examRecOpt.getEroQoOptionType());
        quesOpt.setQoOption(examRecOpt.getEroQoOption());
        quesOpt.setQoConnSort(examRecOpt.getEroQoConnSort());
        quesOpt.setQoConnOptType(examRecOpt.getEroQoConnOptType());
        quesOpt.setQoConnOpt(examRecOpt.getEroQoConnOpt());
        quesOpt.setQuId(examRecOpt.getEroQuId());
        quesOpt.setSort(examRecOpt.getEroSort());
        return quesOpt;
    }

    /**
     * <b>方法名: </b> getUserAnswerRecList <br>
     * <b>说明: </b> 通过前端上传的用户选项和查询到的题目正确答案映射, 解析出需要更新数据库数据的用户选项快照 <br>
     *
     * @param userOptions            List<UserOption> 前端上传的用户答案
     * @param quesIdQuesMap          Map<String, Ques> 题目ID与正确答案列表的映射关系 用于获取题目类型
     * @param quesIdExamRecOptMap    Map<String, List<ExamRecOpt>> 题目ID与全部选项快照的映射关系
     * @param quesIdCorrectOptMap    Map<String, List<QuesOpt>> 题目ID与正确答案列表的映射关系
     * @param quesIdCorrectStatusMap Map<String, CorrectStatusEnum> 题目ID与正确情况的映射关系
     * @param quesIdCorrectIdxMap    Map<String, Map<Integer, List<Integer>>> 题目ID与填空题答对空的下标的映射关系 Map<题目ID, Map<选项序号, List<正确的空的下标>>>
     * @return List<ExamRecOpt>
     * <b>修改履历: </b>
     * @author 2021/7/19 Mokou
     */
    private List<ExamRecOpt> getUserAnswerRecList(List<UserOption> userOptions,
                                                  Map<String, Ques> quesIdQuesMap,
                                                  Map<String, List<ExamRecOpt>> quesIdExamRecOptMap,
                                                  Map<String, List<QuesOpt>> quesIdCorrectOptMap,
                                                  Map<String, CorrectStatusEnum> quesIdCorrectStatusMap,
                                                  Map<String, Map<Integer, List<Integer>>> quesIdCorrectIdxMap
    ) {
        List<ExamRecOpt> res = new ArrayList<>();
        for (UserOption userOption : userOptions) {
            String quId = userOption.getQuId();
            String eroConnNo = userOption.getEroConnNo();
            Integer eroSort = userOption.getEroSort();

            // 当前题目的选项快照列表
            List<ExamRecOpt> examRecOptList = quesIdExamRecOptMap.get(quId);

            // 当前题目的正确答案列表
            List<QuesOpt> correctOptList = quesIdCorrectOptMap.get(quId);

            // 当前题目的每个选项 正确的空的下标  填空题专用
            Map<Integer, List<Integer>> sortCorrectIdxMap = null;
            if (quesIdCorrectIdxMap != null) {
                sortCorrectIdxMap = quesIdCorrectIdxMap.get(quId);
            }

            // 当前题目
            Ques ques = quesIdQuesMap.get(quId);
            if (ques != null && correctOptList != null && !correctOptList.isEmpty() && examRecOptList != null && !examRecOptList.isEmpty()) {
                switch (ques.getQuType()) {
                    case SINGLE:
                    case MULTIPLE:
                    case JUDGMENT: {
                        // 单选 多选 判断 逻辑相同
                        // 遍历本题选项快照 找到用户所选答案(选项排序号与用户所选序号eroConnNo相同) 将答案写入
                        for (ExamRecOpt examRecOpt : examRecOptList) {
                            int userSelect;
                            try {
                                userSelect = Integer.parseInt(eroConnNo);
                            } catch (Exception e) {
                                log.info(
                                        String.format(
                                                "GradeManagerImpl.getUserAnswerRecList 选择类题目答案异常 quId=%s quType=%s eroConnNo=%s",
                                                quId,
                                                ques.getQuType().name(),
                                                eroConnNo
                                        )
                                );
                                continue;
                            }

                            if (examRecOpt.getEroSort().equals(userSelect)) {
                                examRecOpt.setEroConnNo(eroConnNo);
                                examRecOpt.setEroCorrectStatus(quesIdCorrectStatusMap.get(quId));
                                res.add(examRecOpt);
                                break;
                            }
                        }
                        break;
                    }
                    case FILL_BLANK: {
                        // 遍历本题选项快照 找到用户所选答案(选项排序号与用户答案组号eroSort相同) 将答案写入
                        // 并写入正确空的下标列表
                        for (ExamRecOpt examRecOpt : examRecOptList) {
                            if (examRecOpt.getEroSort().equals(eroSort)) {
                                examRecOpt.setEroConnNo(eroConnNo);
                                examRecOpt.setEroCorrectStatus(quesIdCorrectStatusMap.get(quId));
                                if (sortCorrectIdxMap != null && sortCorrectIdxMap.get(eroSort) != null) {
                                    // 写入正确空的下标
                                    List<Integer> correctIdxList = sortCorrectIdxMap.get(eroSort);
                                    examRecOpt.setEroCorrectBlankIdx(JSON.toJSONString(correctIdxList));
                                }
                                res.add(examRecOpt);
                                break;
                            }
                        }
                        break;
                    }
                    case CONNECTION: {
                        // 遍历本题选项快照 找到用户所选答案(选项排序号与用户答案组号eroSort相同) 将答案写入
                        for (ExamRecOpt examRecOpt : examRecOptList) {
                            if (examRecOpt.getEroSort().equals(eroSort)) {
                                examRecOpt.setEroConnNo(eroConnNo);
                                examRecOpt.setEroCorrectStatus(quesIdCorrectStatusMap.get(quId));
                                res.add(examRecOpt);
                                break;
                            }
                        }
                        break;
                    }
                    default: {
                    }
                }
            }
        }
        return res;
    }
}
