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

import cn.reghao.tnb.common.auth.AccountRole;
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.PaperViewType;
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.*;
import cn.reghao.tnb.content.app.util.redis.ds.RedisHash;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

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

/**
 * @author reghao
 * @date 2025-08-12 14:48:05
 */
@Slf4j
@Service
public class PaperViewService {
    private String keyPrefix = "tnb:exam:result";
    private final PaperMapper paperMapper;
    private final PaperQuestionMapper paperQuestionMapper;
    private final QuestionMapper questionMapper;
    private final QuestionOptionMapper questionOptionMapper;
    private final RedisHash redisHash;
    private PaperAnswerMapper paperAnswerMapper;

    public PaperViewService(PaperMapper paperMapper, PaperQuestionMapper paperQuestionMapper,
                            QuestionMapper questionMapper, QuestionOptionMapper questionOptionMapper,
                            RedisHash redisHash, PaperAnswerMapper paperAnswerMapper) {
        this.paperMapper = paperMapper;
        this.paperQuestionMapper = paperQuestionMapper;
        this.questionMapper = questionMapper;
        this.questionOptionMapper = questionOptionMapper;
        this.redisHash = redisHash;
        this.paperAnswerMapper = paperAnswerMapper;
    }

    private int getRole() {
        int role = -1;
        /*if (UserContext.getUserRoles().contains(AccountRole.examAdmin.getValue())) {
            role = 1;
        } else if (UserContext.getUserRoles().contains(AccountRole.examUser.getValue())) {
            role = 2;
        }*/
        return role;
    }

    public PaperDetail getPaperDetail(int paperId, int viewType, int resultId) {
        long loginUser = UserContext.getUserId();
        int role = getRole();
        Paper paper = paperMapper.findById(paperId);
        Map<Long, QuestionAnswer> paperAnswers;
        if (viewType == PaperViewType.PaperPreview.getCode()) {
            if (role > 1) {
                // 用户只有做题的权限
                return new PaperDetail(paperId, 3);
            }

            paperAnswers = getPaperAnswersFromCorrect(paperId);
        } else if (viewType == PaperViewType.PaperExam.getCode()) {
            if (role == 1) {
                // 用户只有管理的权限
                return new PaperDetail(paperId, 2);
            }

            paperAnswers = getPaperAnswersFromCache(paperId, loginUser);
        } else if (viewType == PaperViewType.PaperMark.getCode()) {
            int markType = paper.getMarkType();
            if (markType == 2) {
                // 自己批改
                if (role == 1) {
                    // 试卷由用户自己批改
                    return new PaperDetail(paperId, 4);
                }
            } else if (markType == 3) {
                // 他人批改
                if (role > 1) {
                    // 用户只有做题的权限
                    return new PaperDetail(paperId, 3);
                }
            }

            // 批改试卷不需要客观题
            paperAnswers = getPaperAnswersFromResult(paperId, resultId, false);
        } else if (viewType == PaperViewType.PaperResult.getCode()) {
            if (role == 1) {
                // 用户只有管理的权限
                return new PaperDetail(paperId, 2);
            }

            paperAnswers = getPaperAnswersFromResult(paperId, resultId, true);
        } else {
            // 试卷访问类型 viewType 未知
            return new PaperDetail(paperId, 7);
        }

        int paperViewStatus = 9;
        Set<Integer> questionTypes = paperAnswers.values().stream()
                .collect(Collectors.groupingBy(QuestionAnswer::getQuestionType))
                .keySet();
        Map<Integer, List<QuestionInfo>> questionMap = getPaperQuestions(paperId).stream()
                .filter(questionInfo -> {
                    int questionType = questionInfo.getQuestionType();
                    return questionTypes.contains(questionType);
                })
                .collect(Collectors.groupingBy(QuestionInfo::getQuestionType));
        UserResult userResult = new UserResult(paperId, paperAnswers);
        return new PaperDetail(paper, paperViewStatus, questionMap, userResult);
    }

    public List<QuestionInfo> getPaperQuestions(int paperId) {
        List<PaperQuestion> paperQuestions = paperQuestionMapper.findByPaperId(paperId);
        // questionId -> pos
        Map<Long, PaperQuestion> questionMap = paperQuestions.stream()
                .collect(Collectors.toMap(PaperQuestion::getQuestionId, k -> k));
        List<Question> list1 = questionMapper.findByQuestionIds(new ArrayList<>(questionMap.keySet()));
        return list1.stream().map(question -> {
            QuestionInfo questionInfo = getQuestionInfo(question, questionMap);
            long questionId = question.getQuestionId();
            boolean child = question.getChild();
            if (child) {
                List<QuestionInfo> children = getGroupQuestion(paperId, questionId);
                questionInfo.setChildren(children);
            }
            return questionInfo;
        }).sorted(Comparator.comparingInt(QuestionInfo::getPos)).collect(Collectors.toList());
    }

    private List<QuestionInfo> getGroupQuestion(int paperId, long pid) {
        List<Question> questions = questionMapper.findByPid(pid);
        Map<Long, PaperQuestion> questionMap = paperQuestionMapper.findByPaperIdAndPid(paperId, pid).stream()
                .collect(Collectors.toMap(PaperQuestion::getQuestionId, k -> k));

        return questions.stream()
                .map(question -> getQuestionInfo(question, questionMap))
                .collect(Collectors.toList());
    }

    private QuestionInfo getQuestionInfo(Question question, Map<Long, PaperQuestion> questionMap) {
        long questionId = question.getQuestionId();
        int pos = questionMap.get(questionId).getPos();
        int score = questionMap.get(questionId).getScore();

        QuestionInfo questionInfo = new QuestionInfo(pos, question, score);
        List<QuestionOption> list2 = questionOptionMapper.findByQuestionId(questionId);
        if (!list2.isEmpty()) {
            List<QuestionAnswerInfo> answerInfos = list2.stream()
                    .map(QuestionAnswerInfo::new)
                    .sorted(Comparator.comparingInt(QuestionAnswerInfo::getPos))
                    .collect(Collectors.toList());
            questionInfo.setAnswer(answerInfos);
            questionInfo.setQuestionOptions(answerInfos);
        }

        return questionInfo;
    }

    private Map<Long, QuestionAnswer> getPaperAnswersFromCache(int paperId, long userId) {
        Map<Long, QuestionAnswer> map = new HashMap<>();
        String key = String.format("%s:%s:%s", keyPrefix, paperId, userId);
        redisHash.hgetall(key).forEach((k, v) -> {
            QuestionAnswer questionAnswer = (QuestionAnswer) v;
            long questionId = questionAnswer.getQuestionId();
            Object[] submitAnswer = questionAnswer.getSubmitAnswer();
            int questionType = questionAnswer.getQuestionType();
            if (questionType == QuestionType.QUESTION1.getCode()) {
                if (submitAnswer[0] instanceof String) {
                    Object[] submitAnswer1 = new Object[] {Integer.parseInt((String) submitAnswer[0])};
                    questionAnswer.setSubmitAnswer(submitAnswer1);
                }
            }
            map.put(questionId, questionAnswer);
        });
        if (!map.isEmpty()) {
            return map;
        }

        return getEmptyPaperAnswers(paperId);
    }

    /**
     * 获取空白的试卷答案
     *
     * @param
     * @return
     * @date 2025-08-10 18:08:71
     */
    private Map<Long, QuestionAnswer> getEmptyPaperAnswers(int paperId) {
        Map<Long, QuestionAnswer> map = new HashMap<>();
        List<QuestionInfo> questions = getPaperQuestions(paperId);
        for (QuestionInfo questionInfo : questions) {
            QuestionAnswer questionAnswer = new QuestionAnswer(questionInfo);
            long questionId = questionInfo.getQuestionId();
            int type = questionInfo.getQuestionType();
            List<QuestionOption> questionOptionList = questionOptionMapper.findByQuestionId(questionId);
            long count = questionOptionList.stream().filter(QuestionOption::getCorrect).count();
            List<Object> list = new ArrayList<>();
            for (int i = 0; i < count; i++) {
                if (type == QuestionType.QUESTION1.getCode() || type == QuestionType.QUESTION3.getCode()) {
                    // 单选题和判断题前端使用 radio, 需要 int 类型来表示选中的 radio
                    list.add(-1);
                } else if (type == QuestionType.QUESTION2.getCode()) {
                    // 多选题前端使用 checkbox, 使用 string[] 来表示选中的 checkbox
                    // 空数组表示没有选中
                } else {
                    // 填空题和简答题使用空字符串占位
                    list.add("");
                }
                questionAnswer.setSubmitAnswer(list.toArray(new Object[0]));
            }

            map.putIfAbsent(questionId, questionAnswer);
        }

        return map;
    }

    private Map<Long, QuestionAnswer> getPaperAnswersFromResult(int paperId, int resultId, boolean hasObjective) {
        Map<Long, QuestionAnswer> map = new HashMap<>();
        List<PaperAnswer> paperAnswerList = paperAnswerMapper.findByResultId(resultId);
        paperAnswerList.forEach(paperAnswer -> {
            long questionId = paperAnswer.getQuestionId();
            int questionType = paperAnswer.getQuestionType();
            if (questionType <= QuestionType.QUESTION3.getCode() && !hasObjective) {
                return;
            }

            PaperQuestion paperQuestion = paperQuestionMapper.findPaperQuestion(paperId, questionId);
            int correctScore = paperQuestion.getScore();
            QuestionAnswer questionAnswer = new QuestionAnswer(paperAnswer, correctScore);

            setSubmitAnswer(paperAnswer, questionAnswer);
            setCorrectAnswer(questionAnswer);
            map.put(questionId, questionAnswer);
        });

        return map;
    }

    private void setCorrectAnswer(QuestionAnswer questionAnswer) {
        long questionId = questionAnswer.getQuestionId();
        List<QuestionOption> list = questionOptionMapper.findByQuestionId(questionId);
        List<Object> correctAnswers = list.stream()
                .filter(QuestionOption::getCorrect)
                .map(QuestionOption::getContent)
                .collect(Collectors.toList());
        questionAnswer.setCorrectAnswer(correctAnswers.toArray(new Object[0]));
    }

    private void setSubmitAnswer(PaperAnswer paperAnswer, QuestionAnswer questionAnswer) {
        int type = paperAnswer.getQuestionType();
        String submitAnswer = paperAnswer.getAnswer();
        List<Object> submitAnswerList;
        if (type == QuestionType.QUESTION1.getCode() || type == QuestionType.QUESTION3.getCode()) {
            // 单选题和判断题前端使用 radio, 需要 int 类型来表示选中的 radio
            submitAnswerList = List.of(Integer.parseInt(submitAnswer));
        } else if (type == QuestionType.QUESTION2.getCode()) {
            // 多选题前端使用 checkbox, 使用 string[] 来表示选中的 checkbox
            String[] answerArr = submitAnswer.split(",");
            submitAnswerList = List.of(answerArr);
        } else if (type == QuestionType.QUESTION4.getCode()) {
            // 填空题
            String[] answerArr = submitAnswer.split(",");
            submitAnswerList = List.of(answerArr);
        } else if (type == QuestionType.QUESTION5.getCode()) {
            // 问答题
            submitAnswerList = List.of(submitAnswer);
        } else if (type == QuestionType.QUESTION6.getCode()) {
            // 组合题
            submitAnswerList = List.of(submitAnswer);
        } else {
            submitAnswerList = List.of(submitAnswer);
        }
        questionAnswer.setSubmitAnswer(submitAnswerList.toArray(new Object[0]));
    }

    private Map<Long, QuestionAnswer> getPaperAnswersFromCorrect(int paperId) {
        Map<Long, QuestionAnswer> map = new HashMap<>();
        List<PaperQuestion> paperQuestions = paperQuestionMapper.findByPaperId(paperId);
        Map<Long, PaperQuestion> paperQuestionMap = paperQuestions.stream()
                .collect(Collectors.toMap(PaperQuestion::getQuestionId, k -> k));
        //List<Long> questionIds = paperQuestions.stream().map(PaperQuestion::getQuestionId).collect(Collectors.toList());
        List<Long> questionIds = new ArrayList<>(paperQuestionMap.keySet());

        List<Question> questions = questionMapper.findByQuestionIds(questionIds);
        questions.forEach(question -> {
            long questionId = question.getQuestionId();
            int type = question.getType();
            int correctScore = paperQuestionMap.get(questionId).getScore();
            QuestionAnswer questionAnswer = new QuestionAnswer(question, correctScore);

            List<QuestionOption> list = questionOptionMapper.findByQuestionId(questionId);
            // 正确答案
            List<Object> correctAnswerList;
            if (type == QuestionType.QUESTION1.getCode() || type == QuestionType.QUESTION3.getCode()) {
                correctAnswerList = list.stream()
                        .filter(QuestionOption::getCorrect)
                        .map(QuestionOption::getPos)
                        .collect(Collectors.toList());
            } else if (type == QuestionType.QUESTION2.getCode()) {
                correctAnswerList = list.stream()
                        .filter(QuestionOption::getCorrect)
                        .map(QuestionOption::getPos)
                        .collect(Collectors.toList());
            } else {
                correctAnswerList = list.stream()
                        .filter(QuestionOption::getCorrect)
                        .map(QuestionOption::getContent)
                        .collect(Collectors.toList());
            }

            questionAnswer.setCorrectAnswer(correctAnswerList.toArray(new Object[0]));
            map.put(questionId, questionAnswer);
        });
        return map;
    }
}
