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

import cn.reghao.jutil.jdk.converter.DateTimeConverter;
import cn.reghao.jutil.jdk.web.db.Page;
import cn.reghao.jutil.jdk.web.db.PageList;
import cn.reghao.jutil.jdk.web.result.Result;
import cn.reghao.tnb.common.auth.UserContext;
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.ExamQuery;
import cn.reghao.tnb.content.app.exam.model.query.PaperQuery;
import cn.reghao.tnb.content.app.exam.model.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

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

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

    public Result createExamPaper(PaperAddDto paperAddDto) {
        Paper paper = new Paper(paperAddDto);
        List<PaperQuestion> paperQuestions = paperAddDto.getPaperQuestions();
        save(paper, paperQuestions);
        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);
        });
        /*List<PaperQuestion> list1 = list.stream()
                .map(paperQuestion -> {
                    if (paperQuestion.getChildren().isEmpty()) {
                        return null;
                    }

                    long 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<EvalPaper> getEvalPapers(PaperQuery paperQuery) {
        long loginUser = UserContext.getUserId();
        int total = paperMapper.countByCriteria(paperQuery);
        Page page = paperQuery.getPage();
        List<Paper> list = paperMapper.findPaperByPage(page, paperQuery);
        List<EvalPaper> list1 = list.stream()
                .map(paper -> {
                    int paperId = paper.getId();
                    int markType = paper.getMarkType();
                    PaperResult paperResult = getPaperResult(paperId, loginUser);
                    int status;
                    // 1. 试卷未完成
                    // 2. 试卷已完成, 自己批改
                    // 3. 试卷已完成, 他人批改
                    // 4. 试卷已完成, 已批改
                    int userScore = 0;
                    if (paperResult == null) {
                        status = 1;
                    } else if (paperResult.getMarked()) {
                        status = 4;
                        userScore = paperResult.getUserScore();
                    } else if (markType == 2) {
                        status = 2;
                    } else {
                        status = 3;
                    }

                    return new EvalPaper(paper, status, userScore);
                })
                .collect(Collectors.toList());

        return PageList.pageList(page, total, list1);
    }

    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(paper -> {
                    int subjectId = paper.getSubjectId();
                    String subject = subjectMapper.findById(subjectId).getName();

                    boolean expired = false;
                    LocalDateTime startTime = paper.getStartTime();
                    if (startTime != null) {
                        long expireAt = DateTimeConverter.msTimestamp(startTime) + paper.getDuration()*60*1000L;
                        expired = expireAt < System.currentTimeMillis();
                    }
                    return new PaperView(subject, paper, expired);
                })
                .collect(Collectors.toList());

        return PageList.pageList(page, total, list1);
    }

    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;
    }

    public PaperResult getPaperResult(int paperId, long userId) {
        ExamQuery examQuery = new ExamQuery.Builder()
                .loginUser(userId)
                .paperId(paperId)
                .build();
        Page page = examQuery.getPage();
        List<PaperResult> list = paperResultMapper.findPaperResultByPage(page, examQuery);
        return list.isEmpty() ? null : list.get(0);
    }

    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());
    }
}
