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

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.UserAnswer;
import cn.reghao.tnb.content.app.exam.model.dto.UserResult;
import cn.reghao.tnb.content.app.exam.model.po.*;
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 final QuestionMapper questionMapper;
    private final QuestionOptionMapper questionOptionMapper;
    private final PaperQuestionMapper paperQuestionMapper;
    private final PaperResultMapper paperResultMapper;
    private final PaperAnswerMapper paperAnswerMapper;

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

    public int submitExamPaper(UserResult userResult) {
        int paperId = userResult.getPaperId();
        List<PaperQuestion> paperQuestions = paperQuestionMapper.findByPaperId(paperId);
        Map<Integer, PaperQuestion> paperQuestionsMap = paperQuestions.stream()
                .collect(Collectors.toMap(PaperQuestion::getQuestionId, k -> k));

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

        // pid -> List<UserAnswer>
        Map<Integer, List<UserAnswer>> group = userResult.getUserAnswers().stream()
                .collect(Collectors.groupingBy(UserAnswer::getPid));
        List<PaperAnswer> paperAnswers = new ArrayList<>();
        for (Map.Entry<Integer, List<UserAnswer>> entry : group.entrySet()) {
            int pid = entry.getKey();
            List<UserAnswer> answers = entry.getValue();
            if (pid == 0) {
                List<PaperAnswer> list1 = calculateScore(answers, scoreMap);
                paperAnswers.addAll(list1);
            } else {
                List<PaperQuestion> list = paperQuestionMapper.findByPaperIdAndPid(paperId, pid);
                Map<Integer, 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();
        PaperResult paperResult = new PaperResult(paperId, objectiveScore);
        int resultId = save(paperResult, paperAnswers);
        return resultId;
    }

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

    private List<PaperAnswer> calculateScore(List<UserAnswer> userAnswers, Map<Integer, Integer> scoreMap) {
        List<PaperAnswer> list = new ArrayList<>();
        for (UserAnswer userAnswer : userAnswers) {
            PaperAnswer paperAnswer = new PaperAnswer(userAnswer);

            int questionId = userAnswer.getQuestionId();
            String submitAnswer = userAnswer.getAnswer();
            Question question = questionMapper.findById(questionId);
            List<QuestionOption> correctOptions = questionOptionMapper.findByQuestionId(questionId).stream()
                    .filter(QuestionOption::getCorrect)
                    .collect(Collectors.toList());

            int type = question.getType();
            if (type == QuestionType.QUESTION1.getCode() || type == QuestionType.QUESTION4.getCode()) {
                // 单选和判断
                int option = Integer.parseInt(submitAnswer)+1;
                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() || type == QuestionType.QUESTION3.getCode()) {
                // 多选和不定项
                List<Integer> submitAnswers = Arrays.stream(submitAnswer.split(","))
                        .map(str -> Integer.parseInt(str) + 1).collect(Collectors.toList());
                Set<Integer> correctAnswers = correctOptions.stream()
                        .map(QuestionOption::getPos)
                        .collect(Collectors.toSet());

                if (submitAnswers.size() == correctAnswers.size()) {
                    long total = submitAnswers.stream().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.QUESTION5.getCode()) {
                // 填空题
            } else if (type == QuestionType.QUESTION6.getCode()) {
                // 问答题
            } else if (type == QuestionType.QUESTION8.getCode()) {
                // 組题
            } else {
            }

            list.add(paperAnswer);
        }

        return list;
    }
}
