package cn.reghao.tnb.content.app.exam.service;

import cn.reghao.tnb.common.auth.UserContext;
import cn.reghao.tnb.content.app.exam.db.mapper.*;
import cn.reghao.tnb.content.app.exam.model.constant.QuestionType;
import cn.reghao.tnb.content.app.exam.model.dto.UserResult;
import cn.reghao.tnb.content.app.exam.model.po.*;
import cn.reghao.tnb.content.app.exam.model.vo.QuestionAnswer;
import cn.reghao.tnb.content.app.util.redis.ds.RedisHash;
import cn.reghao.tnb.content.app.util.redis.ds.RedisOps;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author reghao
 * @date 2024-08-24 15:17:48
 */
@Service
public class ExamService {
    private String keyPrefix = "tnb:exam:result";
    private final QuestionMapper questionMapper;
    private final QuestionOptionMapper questionOptionMapper;
    private final PaperQuestionMapper paperQuestionMapper;
    private final PaperResultMapper paperResultMapper;
    private final PaperAnswerMapper paperAnswerMapper;
    private RedisHash redisHash;
    private RedisOps redisOps;

    public ExamService(QuestionMapper questionMapper, QuestionOptionMapper questionOptionMapper,
                       PaperQuestionMapper paperQuestionMapper, PaperResultMapper paperResultMapper,
                       PaperAnswerMapper paperAnswerMapper, RedisHash redisHash, RedisOps redisOps) {
        this.questionMapper = questionMapper;
        this.questionOptionMapper = questionOptionMapper;
        this.paperQuestionMapper = paperQuestionMapper;
        this.paperResultMapper = paperResultMapper;
        this.paperAnswerMapper = paperAnswerMapper;
        this.redisHash = redisHash;
        this.redisOps = redisOps;
    }

    public void cacheUserAnswers(UserResult userResult) {
        long loginUser = UserContext.getUserId();
        int paperId = userResult.getPaperId();
        String key = String.format("%s:%s:%s", keyPrefix, paperId, loginUser);

        List<QuestionAnswer> submitUserAnswers = userResult.getSubmitUserAnswers();
        for (QuestionAnswer userAnswer : submitUserAnswers) {
            long questionId = userAnswer.getQuestionId();
            redisHash.hset(key, ""+questionId, userAnswer);
        }
    }

    public int submitExamPaper(UserResult userResult) {
        int paperId = userResult.getPaperId();
        List<PaperQuestion> paperQuestions = paperQuestionMapper.findByPaperId(paperId);
        // 客观题数量
        Long totalObjectiveQuestions = paperQuestions.stream()
                .filter(paperQuestion -> {
                    int type = paperQuestion.getType();
                    return type <= QuestionType.QUESTION3.getCode();
                }).count();

        Map<Long, Integer> scoreMap = paperQuestions.stream()
                .collect(Collectors.toMap(PaperQuestion::getQuestionId, PaperQuestion::getScore));

        // pid -> List<QuestionAnswer>
        Map<Long, List<QuestionAnswer>> group = userResult.getSubmitUserAnswers().stream()
                .collect(Collectors.groupingBy(QuestionAnswer::getPid));
        List<PaperAnswer> paperAnswers = new ArrayList<>();
        for (Map.Entry<Long, List<QuestionAnswer>> entry : group.entrySet()) {
            long pid = entry.getKey();
            List<QuestionAnswer> answers = entry.getValue();
            if (pid == 0) {
                List<PaperAnswer> list1 = calculateScore(answers, scoreMap);
                paperAnswers.addAll(list1);
            } else {
                List<PaperQuestion> list = paperQuestionMapper.findByPaperIdAndPid(paperId, pid);
                Map<Long, Integer> scoreMap1 = list.stream()
                        .collect(Collectors.toMap(PaperQuestion::getQuestionId, PaperQuestion::getScore));
                List<PaperAnswer> list2 = calculateScore(answers, scoreMap1);
                paperAnswers.addAll(list2);
            }
        }

        int objectiveScore = paperAnswers.stream()
                .map(PaperAnswer::getScore)
                .filter(Objects::nonNull)
                .mapToInt(Integer::intValue)
                .sum();

        boolean marked = totalObjectiveQuestions.intValue() == paperQuestions.size();
        PaperResult paperResult = new PaperResult(paperId, objectiveScore, marked);
        int resultId = savePaperResult(paperResult, paperAnswers);

        long loginUser = UserContext.getUserId();
        String key = String.format("%s:%s:%s", keyPrefix, paperId, loginUser);
        redisOps.del(key);
        return resultId;
    }

    @Transactional(rollbackFor = Exception.class)
    public int savePaperResult(PaperResult paperResult, List<PaperAnswer> paperAnswers) {
        paperResultMapper.save(paperResult);
        int resultId = paperResult.getId();
        paperAnswers.forEach(paperAnswer -> paperAnswer.setResultId(resultId));
        paperAnswerMapper.saveAll(paperAnswers);
        return resultId;
    }

    /**
     * 对客观题进行算分
     *
     * @param
     * @return
     * @date 2025-08-06 10:18:57
     */
    private List<PaperAnswer> calculateScore(List<QuestionAnswer> userAnswers, Map<Long, Integer> scoreMap) {
        List<PaperAnswer> list = new ArrayList<>();
        for (QuestionAnswer userAnswer : userAnswers) {
            PaperAnswer paperAnswer = new PaperAnswer(userAnswer);
            long questionId = userAnswer.getQuestionId();
            Object[] submitAnswers = userAnswer.getSubmitAnswer();
            Question question = questionMapper.findByQuestionId(questionId);
            List<QuestionOption> correctOptions = questionOptionMapper.findByQuestionId(questionId).stream()
                    .filter(QuestionOption::getCorrect)
                    .collect(Collectors.toList());

            String answer = "";
            int type = question.getType();
            if (type == QuestionType.QUESTION1.getCode() || type == QuestionType.QUESTION3.getCode()) {
                // 单选和判断
                int option = (int)submitAnswers[0];
                answer += option;
                int correctOption = correctOptions.get(0).getPos();
                if (option == correctOption) {
                    paperAnswer.setCorrect(true);
                    paperAnswer.setScore(scoreMap.get(questionId));
                } else {
                    paperAnswer.setCorrect(false);
                    paperAnswer.setScore(0);
                }
            } else if (type == QuestionType.QUESTION2.getCode()) {
                // 多选
                StringBuilder sb = new StringBuilder();
                for (Object obj : submitAnswers) {
                    sb.append((String) obj).append(",");
                }
                answer = sb.toString();
                int idx = answer.lastIndexOf(",");
                answer = answer.substring(0, idx);

                Set<Integer> correctAnswers = correctOptions.stream()
                        .map(QuestionOption::getPos)
                        .collect(Collectors.toSet());

                if (submitAnswers.length == correctAnswers.size()) {
                    long total = Arrays.stream(submitAnswers).filter(correctAnswers::contains).count();
                    if (correctAnswers.size() == total) {
                        paperAnswer.setCorrect(true);
                        paperAnswer.setScore(scoreMap.get(questionId));
                        list.add(paperAnswer);
                        continue;
                    }
                }
                paperAnswer.setCorrect(false);
                paperAnswer.setScore(0);
            } else if (type == QuestionType.QUESTION4.getCode()) {
                // 填空题
                StringBuilder sb = new StringBuilder();
                for (Object obj : userAnswer.getSubmitAnswer()) {
                    sb.append(obj).append(",");
                }
                answer = sb.toString();
                int idx = answer.lastIndexOf(",");
                answer = answer.substring(0, idx);
            } else if (type == QuestionType.QUESTION5.getCode()) {
                // 问答题
                answer = (String) userAnswer.getSubmitAnswer()[0];
            } else if (type == QuestionType.QUESTION6.getCode()) {
                // 组合题
                answer = (String) userAnswer.getSubmitAnswer()[0];
            } else {
                answer = (String) userAnswer.getSubmitAnswer()[0];
            }
            paperAnswer.setAnswer(answer);
            list.add(paperAnswer);
        }

        return list;
    }
}
