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

import cn.reghao.jutil.jdk.db.Page;
import cn.reghao.jutil.jdk.db.PageList;
import cn.reghao.jutil.jdk.result.Result;
import cn.reghao.tnb.common.db.KeyValue;
import cn.reghao.tnb.content.app.exam.db.mapper.*;
import cn.reghao.tnb.content.app.exam.model.dto.PaperAddDto;
import cn.reghao.tnb.content.app.exam.model.po.*;
import cn.reghao.tnb.content.app.exam.model.query.PaperQuery;
import cn.reghao.tnb.content.app.exam.model.vo.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * @author reghao
 * @date 2024-08-27 17:21:01
 */
@Service
public class PaperService {
    private final PaperMapper paperMapper;
    private final PaperQuestionMapper paperQuestionMapper;
    private final QuestionMapper questionMapper;
    private final QuestionOptionMapper questionOptionMapper;

    public PaperService(PaperMapper paperMapper, PaperQuestionMapper paperQuestionMapper,
                        QuestionMapper questionMapper, QuestionOptionMapper questionOptionMapper) {
        this.paperMapper = paperMapper;
        this.paperQuestionMapper = paperQuestionMapper;
        this.questionMapper = questionMapper;
        this.questionOptionMapper = questionOptionMapper;
    }

    public Result createExamPaper(PaperAddDto paperAddDto) {
        Paper paper = new Paper(paperAddDto);
        List<PaperQuestion> questions = paperAddDto.getQuestions();
        save(paper, questions);
        return Result.success();
    }

    @Transactional(rollbackFor = Exception.class)
    public void save(Paper paper, List<PaperQuestion> list) {
        paperMapper.save(paper);
        int paperId = paper.getId();
        list.forEach(paperQuestion -> {
            paperQuestion.setPaperId(paperId);
            if (!paperQuestion.getChildren().isEmpty()) {
                paperQuestion.setChild(true);
            }
        });

        List<PaperQuestion> list1 = list.stream()
                .map(paperQuestion -> {
                    if (paperQuestion.getChildren().isEmpty()) {
                        return null;
                    }

                    int questionId = paperQuestion.getQuestionId();
                    paperQuestion.getChildren().forEach(paperQuestion1 -> {
                        paperQuestion1.setPaperId(paperId);
                        paperQuestion1.setPid(questionId);
                    });
                    return paperQuestion.getChildren();
                })
                .filter(Objects::nonNull)
                .flatMap(Collection::stream)
                .collect(Collectors.toList());

        paperQuestionMapper.saveAll(list);
        if (!list1.isEmpty()) {
            paperQuestionMapper.saveAll(list1);
        }
    }

    public Result deleteExamPaper(int paperId) {
        paperMapper.deleteById(paperId);
        return Result.success();
    }

    public PageList<PaperView> getExamPapers(PaperQuery paperQuery) {
        int total = paperMapper.countByCriteria(paperQuery);
        Page page = paperQuery.getPage();
        List<Paper> list = paperMapper.findPaperByPage(page, paperQuery);
        List<PaperView> list1 = list.stream().map(PaperView::new).collect(Collectors.toList());
        return PageList.pageList(page, total, list1);
    }

    public List<QuestionInfo> getPaperQuestions(int paperId) {
        List<PaperQuestion> questions = paperQuestionMapper.findByPaperId(paperId);
        // questionId -> pos
        Map<Integer, PaperQuestion> questionMap = questions.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);
            int questionId = question.getId();
            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, int pid) {
        List<Question> questions = questionMapper.findByPid(pid);
        Map<Integer, 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<Integer, PaperQuestion> questionMap) {
        int questionId = question.getId();
        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);
        }

        return questionInfo;
    }

    public PaperDetail getPaperDetail(int paperId) {
        Paper paper = paperMapper.findById(paperId);
        List<QuestionInfo> questions = getPaperQuestions(paperId);
        return new PaperDetail(paper, questions);
    }

    public List<KeyValue> getKeyValues() {
        PaperQuery paperQuery = new PaperQuery.Builder().pageSize(10).build();
        Page page = paperQuery.getPage();
        List<Paper> list = paperMapper.findPaperByPage(page, paperQuery);
        return list.stream().map(paper -> {
            int paperId = paper.getId();
            String paperName = paper.getName();
            return new KeyValue(paperId, paperName);
        }).collect(Collectors.toList());
    }
}
