package com.ruoyi.web.service;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.ie.domain.*;
import com.ruoyi.ie.mapper.*;
import com.ruoyi.ie.service.IQuestionsService;
import com.ruoyi.mxjb.domain.MxjbContants;
import com.ruoyi.mxjb.domain.MxjbPaperAnswers;
import com.ruoyi.mxjb.domain.MxjbPaperExaminee;
import com.ruoyi.mxjb.domain.MxjbPaperTest;
import com.ruoyi.mxjb.mapper.MxjbPaperAnswersMapper;
import com.ruoyi.mxjb.mapper.MxjbPaperExamineeMapper;
import com.ruoyi.mxjb.mapper.MxjbPaperTestMapper;
import com.ruoyi.web.domain.Constant;
import com.ruoyi.web.domain.PaperDto;
import com.ruoyi.web.domain.QuestionDto;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
public class MxjbPaperExamService {
    private final KnowledgeTreeMapper knowledgeTreeMapper;
    private final PapersMapper papersMapper;
    private final PaperquesMapper paperquesMapper;
    private final QuestionsMapper questionsMapper;
    private final MxjbPaperTestMapper mxjbPaperTestMapper;
    private final MxjbPaperExamineeMapper mxjbPaperExamineeMapper;
    private final MxjbPaperAnswersMapper mxjbPaperAnswersMapper;
    private final SubjectMapper subjectMapper;

    private final AdminExaminationService adminExaminationService;
    private final IQuestionsService questionsService;
    private final RedisCache redisCache;
    private final CacheService cacheService;

    public MxjbPaperExamService(KnowledgeTreeMapper knowledgeTreeMapper, PapersMapper papersMapper, PaperquesMapper paperquesMapper, QuestionsMapper questionsMapper,
                                MxjbPaperTestMapper mxjbPaperTestMapper, MxjbPaperExamineeMapper mxjbPaperExamineeMapper, MxjbPaperAnswersMapper mxjbPaperAnswersMapper, SubjectMapper subjectMapper, AdminExaminationService adminExaminationService,
                                IQuestionsService questionsService, RedisCache redisCache, CacheService cacheService) {
        this.knowledgeTreeMapper = knowledgeTreeMapper;
        this.papersMapper = papersMapper;
        this.paperquesMapper = paperquesMapper;
        this.questionsMapper = questionsMapper;
        this.mxjbPaperTestMapper = mxjbPaperTestMapper;
        this.mxjbPaperExamineeMapper = mxjbPaperExamineeMapper;
        this.mxjbPaperAnswersMapper = mxjbPaperAnswersMapper;
        this.subjectMapper = subjectMapper;
        this.adminExaminationService = adminExaminationService;
        this.questionsService = questionsService;
        this.redisCache = redisCache;
        this.cacheService = cacheService;
    }

    // 交卷处理
    public AjaxResult saveExamPaper(PaperDto paperDto) {
        Long examineeId = paperDto.getExamineeId();
        MxjbPaperExaminee mxjbPaperExaminee = mxjbPaperExamineeMapper.selectMxjbPaperExamineeById(examineeId);
        if (mxjbPaperExaminee.getState() >= MxjbContants.ExamineeStatusReview) {
            return AjaxResult.error(examineeId + " 已经在批阅的不可保存答案");
        }
        Papers paper = papersMapper.selectPapersById(mxjbPaperExaminee.getPaperId());
        // 保存答案
        PaperDto standPaper = loadPaperDto(paper, mxjbPaperExaminee, false, true);
        String key = adminExaminationService.getExamineeAnswersKey(MxjbContants.ExamineeTypeIeKey, paperDto.getExamineeId());
        if (CollectionUtils.isEmpty(paperDto.getQuestions())) {
            Map<String, QuestionDto> questionDtoMap = redisCache.getCacheMap(key);
            questionDtoMap.remove("0");
            paperDto.setQuestions(com.google.common.collect.Lists.newArrayList(questionDtoMap.values()));
        }

        // 关闭答卷
        MxjbPaperExaminee mxjbExamineeCond = new MxjbPaperExaminee();
        mxjbExamineeCond.setExamineeId(paperDto.getExamineeId());
        mxjbExamineeCond.setState(MxjbContants.ExamineeStatusReview);
        mxjbExamineeCond.setEndTime(new Date());
        mxjbPaperExamineeMapper.updateMxjbPaperExaminee(mxjbExamineeCond);

        mxjbExamineeCond.setState(null);
        mxjbExamineeCond.setEndTime(null);

        mxjbExamineeCond.setChooseTotal(0);
        mxjbExamineeCond.setSubjectiveTotal(0);
        mxjbExamineeCond.setChooseCount(0);
        mxjbExamineeCond.setSubjectiveCount(0);
        mxjbExamineeCond.setScore(0L);
        standPaper.getQuestions().forEach(t -> {
            if (MxjbContants.QuestionTypeChoose.equals(t.getTypeId())) {
                mxjbExamineeCond.setChooseTotal(mxjbExamineeCond.getChooseTotal() + 1);
            } else {
                mxjbExamineeCond.setSubjectiveTotal(mxjbExamineeCond.getSubjectiveTotal() + 1);
            }
        });
        updatePaperAnswers(standPaper, paperDto.getQuestions(), mxjbPaperExaminee.getExamineeId());
        mxjbPaperExamineeMapper.updateMxjbPaperExaminee(mxjbExamineeCond);
        redisCache.deleteObject(key);
        return AjaxResult.success("成功");
    }

    // 阅卷处理
    public AjaxResult scoreExamineeQuestion(PaperDto paperDto, boolean self) {
        Long examineeId = paperDto.getExamineeId();
        MxjbPaperExaminee exist = mxjbPaperExamineeMapper.selectMxjbPaperExamineeById(examineeId);
        if (exist.getState() <= MxjbContants.ExamineeStatusExamine || exist.getState() >= MxjbContants.ExamineeStatusPublish) {
            return AjaxResult.error(examineeId + " 不可阅卷状态");
        }
        if (self && !exist.getCustomerCode().equals(SecurityUtils.getLoginUser().getUser().getCode())) {
            return AjaxResult.error(examineeId + " 不可修改他人试卷");
        }
        Papers paper = papersMapper.selectPapersById(exist.getPaperId());
        // 保存答案
        PaperDto standPaper = loadPaperDto(paper, exist, true, true);
        scoreExamineeAnswers(standPaper, paperDto.getQuestions(), MxjbContants.ExamineeTypeIe, paperDto.getExamineeId());
        return AjaxResult.success(paperDto.getExamineeId());
    }

    public void scoreExamineeAnswers(PaperDto standPaper, List<QuestionDto> questions, String examineeType, Long examineeId) {
        List<MxjbPaperAnswers> mxjbAnswersList = mxjbPaperAnswersMapper.selectMxjbAnswersByPaperExamineeId(examineeId);
        Map<Long, MxjbPaperAnswers> questionAnswersMap = mxjbAnswersList.stream().collect(Collectors.toMap(MxjbPaperAnswers::getQuestionId, Function.identity()));
        Map<Long, QuestionDto> questionMap = standPaper.getQuestions().stream().collect(Collectors.toMap(QuestionDto::getQuestionId, Function.identity()));

        List<String> errorList = com.google.common.collect.Lists.newArrayList();
        for (QuestionDto question : questions) {
            if (null == question.getScore()) {
                errorList.add(question.getQuestionId() + "不能无分");
            }
            MxjbPaperAnswers existAnswer = questionAnswersMap.get(question.getQuestionId());
            QuestionDto standard = questionMap.get(question.getQuestionId());
            if (MxjbContants.QuestionTypeChoose.equals(standard.getTypeId())) {
                boolean right = (existAnswer != null && CollectionUtils.isNotEmpty(standard.getAnswers())
                        && ObjectUtils.equals(MxjbContants.getNormalAnswer(existAnswer.getAnswer(), ""), standard.getAnswers().get(0)));
                if (!ObjectUtils.equals(question.getScore(), right ? standard.getScoreTotal() : 0L)) {
                    errorList.add("第"+question.getSeq()+"题"+"("+question.getQuestionId()+ "）选择题得分不正确" + question.getScore());
                }
            }
        }
        if (errorList.size() > 0) {
            throw new RuntimeException(examineeId + " 校验失败 " + StringUtils.join(errorList, ","));
        }
        for (QuestionDto question : questions) {
            if (null == question.getScore()) {
                question.setScore(0L);
            }
            MxjbPaperAnswers existAnswer = questionAnswersMap.get(question.getQuestionId());
            QuestionDto standard = questionMap.get(question.getQuestionId());
            MxjbPaperAnswers answersCond = new MxjbPaperAnswers();
            answersCond.setCorrect(question.getComment());
            answersCond.setScore(question.getScore());
            answersCond.setScoreRate(Math.round(question.getScore() * 100.0 / standard.getScoreTotal()) / 100.0);
            answersCond.setScoreLevel(answersCond.getScoreRate() >= MxjbContants.ScoreRateLimit ? MxjbContants.ScoreLevelRight : MxjbContants.ScoreLevelWrong);
            answersCond.setState(MxjbContants.InvalidScoreLevelSet.contains(answersCond.getScoreLevel()) ? 0L : 1L);

            if (null == existAnswer) {
                // TODO 用户未交卷，自动生成0分答案？
                answersCond.setExamineeId(examineeId);
                answersCond.setExamineeType(examineeType);
                answersCond.setPaperType(standPaper.getPaperType());
                answersCond.setQuestionId(standard.getQuestionId());
                answersCond.setSeq(standard.getSeq());
                answersCond.setKnowledgeId(standard.getKnowledgeId());
                mxjbPaperAnswersMapper.insertMxjbPaperAnswers(answersCond);
            } else {
                answersCond.setAnswerId(existAnswer.getAnswerId());
                mxjbPaperAnswersMapper.updateMxjbPaperAnswers(answersCond);
            }
        }
    }

    // 发布处理
    public AjaxResult scoreFinish(Long examineeId) {
        MxjbPaperExaminee exist = mxjbPaperExamineeMapper.selectMxjbPaperExamineeById(examineeId);
        if (MxjbContants.ExamineeStatusPublish.equals(exist.getState())) {
            return AjaxResult.error(examineeId + " 已经阅过卷了");
        }
        calculateExamineeScores(exist, true);
        return AjaxResult.success(examineeId + " 阅卷成功");
    }

    public void calculateExamineeScores(MxjbPaperExaminee examinee, boolean buildWrong) {
        Papers paper = papersMapper.selectPapersById(examinee.getPaperId());
        // 保存答案
        PaperDto standPaper = loadPaperDto(paper, examinee, true, true);
        Long totalStandScore = standPaper.getQuestions().stream().filter(t -> t.getScoreTotal() != null).mapToLong(QuestionDto::getScoreTotal).sum();
        MxjbPaperExaminee mxjbExaminee = new MxjbPaperExaminee();
        mxjbExaminee.setExamineeId(examinee.getExamineeId());
        mxjbExaminee.setScore(0L);
        mxjbExaminee.setChooseCount(0);
        mxjbExaminee.setSubjectiveCount(0);

        List<MxjbPaperAnswers> answerList = mxjbPaperAnswersMapper.selectMxjbAnswersByPaperExamineeId(examinee.getExamineeId());
        Map<Long, MxjbPaperAnswers> questionAnswersMap = answerList.stream().collect(Collectors.toMap(MxjbPaperAnswers::getQuestionId, Function.identity()));

        Set<Long> questionIdSet = Sets.newHashSet();
        for(QuestionDto stdQuestion : standPaper.getQuestions()) {
            MxjbPaperAnswers answers = questionAnswersMap.get(stdQuestion.getQuestionId());
            if (null == answers || null == answers.getScore()) {
                questionIdSet.add(stdQuestion.getQuestionId());
            }
        }
        if (questionIdSet.size() > 0) {
            throw new RuntimeException(examinee.getExamineeId() + "有题未打分: " + StringUtils.join(questionIdSet, ","));
        }
        if (buildWrong && standPaper.getQuestions().size() > 0) {
            adminExaminationService.calculateWrongAnswers(standPaper, answerList, examinee.getExamineeId(), examinee.getCustomerCode(), paper.getPaperType());
        }
        answerList.forEach(t -> {
            if (ObjectUtils.equals(t.getState(), 1L)) {
                if (MxjbContants.QuestionTypeChoose.equals(cacheService.getQuestionType(t.getQuestionType()))) {
                    mxjbExaminee.setChooseCount(mxjbExaminee.getChooseCount() + 1);
                } else {
                    mxjbExaminee.setSubjectiveCount(mxjbExaminee.getSubjectiveCount() + 1);
                }
            }
            if (null != t.getScore()) {
                mxjbExaminee.setScore(mxjbExaminee.getScore() + t.getScore());
            }
        });

        mxjbExaminee.setScoreRate(totalStandScore > 0 ? Math.round(mxjbExaminee.getScore() * 100.0 / totalStandScore) / 100.0 : 0);
        mxjbExaminee.setScoreLevel(mxjbExaminee.getScoreRate() >= MxjbContants.ScoreRateLimit ? MxjbContants.ScoreLevelRight : MxjbContants.ScoreLevelWrong);
        mxjbExaminee.setExamineeId(examinee.getExamineeId());
        mxjbExaminee.setState(MxjbContants.ExamineeStatusPublish);
        mxjbPaperExamineeMapper.updateMxjbPaperExaminee(mxjbExaminee);
    }


    /**
     * 加载已有试卷
     * @param paperId
     * @return
     */
    public PaperDto loadPaperById(Long paperId) {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        MxjbPaperExaminee mxjbPaperExamineeCond = new MxjbPaperExaminee();
        mxjbPaperExamineeCond.setPaperId(paperId);
        mxjbPaperExamineeCond.setCustomerCode(user.getCode());
        mxjbPaperExamineeCond.setState(MxjbContants.ExamineeStatusExamine);
        List<MxjbPaperExaminee> mxjbPaperExamineeList = mxjbPaperExamineeMapper.selectMxjbPaperExamineeList(mxjbPaperExamineeCond);
        if (CollectionUtils.isEmpty(mxjbPaperExamineeList)) {
            mxjbPaperExamineeCond.setState(MxjbContants.ExamineeStatusReview);
            mxjbPaperExamineeList = mxjbPaperExamineeMapper.selectMxjbPaperExamineeList(mxjbPaperExamineeCond);
        }
        Papers paper = papersMapper.selectPapersById(paperId);
        if (CollectionUtils.isEmpty(mxjbPaperExamineeList)) {
            return initNewExaminee(user, paper, true);
        }
        MxjbPaperExaminee mxjbPaperExaminee = mxjbPaperExamineeList.get(0);
        PaperDto paperDto = loadPaperDto(paper, mxjbPaperExaminee, mxjbPaperExaminee.getState() >= MxjbContants.ExamineeStatusPublish, true);
        paperDto.setPaperType(MxjbContants.PaperTypeStandard);
        if (MxjbContants.ExamineeStatusExamine.equals(mxjbPaperExaminee.getState())) { // 正在考试时再次进入需要合并答案
            adminExaminationService.updateTempAnswerAndSave(paperDto, mxjbPaperExaminee.getExamineeId(), MxjbContants.ExamineeTypeIe);
        }
        return paperDto;
    }

    public PaperDto loadPaperByExamineeId(Long examineeId) {
        MxjbPaperExaminee mxjbPaperExaminee = mxjbPaperExamineeMapper.selectMxjbPaperExamineeById(examineeId);
        Papers paper = papersMapper.selectPapersById(mxjbPaperExaminee.getPaperId());
        PaperDto paperDto = loadPaperDto(paper, mxjbPaperExaminee, mxjbPaperExaminee.getState() >= MxjbContants.ExamineeStatusPublish, true);
        paperDto.setPaperType(MxjbContants.PaperTypeStandard);
        return paperDto;
    }

    public String getPaperKnowledgeKey(String typeName, Long subjectId, Long paperId) {
        return typeName + (null != paperId ? "_" + paperId : "") + (null != subjectId ? "_" + subjectId : "");
    }
    // 实时生成或加载正在做的试卷
    public PaperDto loadPaperByType(SysUser sysUser, Long subjectId, String typeName, String type, Long paperId, boolean keepTitleNotChange) {
        MxjbPaperTest mxjbPaperTest = new MxjbPaperTest();
        mxjbPaperTest.setKnownledges(getPaperKnowledgeKey(typeName, paperId, subjectId));
        mxjbPaperTest.setCreatorId(sysUser.getUserId());
        mxjbPaperTest.setState(MxjbContants.ExamineeStatusExamine);
        MxjbPaperTest exist = mxjbPaperTestMapper.selectTopMxjbPaperTest(mxjbPaperTest);
        if (null == exist) { // 无此类练习
            return initNewKnowledgePaper(mxjbPaperTest.getKnownledges(), sysUser, subjectId, typeName, type, paperId, 0L, 0L, keepTitleNotChange);
        }
        MxjbPaperExaminee cond = new MxjbPaperExaminee();
        cond.setPaperId(exist.getPaperId());
        List<MxjbPaperExaminee> mxjbPaperExamineeList = mxjbPaperExamineeMapper.selectMxjbPaperExamineeList(cond);
        if (1 != mxjbPaperExamineeList.size()) {
            throw new RuntimeException("异常AI卷: " + exist.getPaperId());
        }
        MxjbPaperExaminee mxjbAiExaminee = mxjbPaperExamineeList.get(0);
        if (MxjbContants.ExamineeStatusPublish.equals(mxjbAiExaminee.getState())) { // 无此类练习
            MxjbPaperTest updPaperTest = new MxjbPaperTest();
            updPaperTest.setPaperId(mxjbAiExaminee.getPaperId());
            updPaperTest.setState(MxjbContants.ExamineeStatusCommit);
            mxjbPaperTestMapper.updateMxjbPaperTest(updPaperTest);
            return initNewKnowledgePaper(mxjbPaperTest.getKnownledges(), sysUser, subjectId, typeName, type, paperId, 0L, 0L, keepTitleNotChange);
        }
        exist.setState(mxjbAiExaminee.getState());
        boolean loadParse = exist.getState() >= MxjbContants.ExamineeStatusReview;
        Papers papers = papersMapper.selectPapersById(exist.getPaperId());
        PaperDto paperDto = loadPaperDto(papers, mxjbAiExaminee, loadParse, keepTitleNotChange);
        if (MxjbContants.ExamineeStatusExamine.equals(exist.getState())) { // 正在考试时再次进入需要合并答案
            adminExaminationService.updateTempAnswerAndSave(paperDto, mxjbAiExaminee.getExamineeId(), MxjbContants.ExamineeTypeIe);
        }
        return paperDto;
    }

    private void updatePaperAnswers(PaperDto standPaper, List<QuestionDto> questions, Long examineeId) {
        List<MxjbPaperAnswers> mxjbAnswersList = mxjbPaperAnswersMapper.selectMxjbAnswersByPaperExamineeId(examineeId);
        Map<Long, MxjbPaperAnswers> oldQuestionAnswersMap = mxjbAnswersList.stream().collect(Collectors.toMap(MxjbPaperAnswers::getQuestionId, Function.identity()));
        Map<Long, QuestionDto> newQuestionAnswerMap = questions.stream().collect(Collectors.toMap(QuestionDto::getQuestionId, Function.identity()));
        MxjbPaperAnswers mxjbAnswersCond = new MxjbPaperAnswers();
        standPaper.getQuestions().forEach(t -> {
            MxjbPaperAnswers oldAnswer = oldQuestionAnswersMap.remove(t.getQuestionId());
            QuestionDto newAnswer = newQuestionAnswerMap.get(t.getQuestionId());
            if (null != newAnswer) {
                mxjbAnswersCond.setAnswer(newAnswer.getAnswer() + "$@" + StringUtils.join(newAnswer.getAttachments(), "$@"));
                mxjbAnswersCond.setDuration(newAnswer.getDuration());
            } else {
                mxjbAnswersCond.setAnswer(null);
                mxjbAnswersCond.setDuration(-1L);
            }
//            if (MxjbContants.QuestionTypeChoose.equals(t.getTypeId())) {
//                Boolean right = (newAnswer != null && CollectionUtils.isNotEmpty(t.getAnswers())
//                        && ObjectUtils.equals(MxjbContants.getNormalAnswer(mxjbAnswersCond.getAnswer(), ""), t.getAnswers().get(0)));
//                mxjbAnswersCond.setScore(right ? t.getScore() : 0L);
//                mxjbAnswersCond.setScoreRate(Math.round(mxjbAnswersCond.getScore() * 100.0 / t.getScoreTotal()) / 100.0);
//                mxjbAnswersCond.setScoreLevel(mxjbAnswersCond.getScoreRate() >= MxjbContants.ScoreRateLimit ? MxjbContants.ScoreLevelRight : MxjbContants.ScoreLevelWrong);
//                mxjbAnswersCond.setState(MxjbContants.InvalidScoreLevelSet.contains(mxjbAnswersCond.getScoreLevel()) ? 0L : 1L);
//            }
            if (null != oldAnswer) {
                mxjbAnswersCond.setAnswerId(oldAnswer.getAnswerId());
                mxjbAnswersCond.setExamineeId(null);
                mxjbAnswersCond.setExamineeType(null);
                mxjbAnswersCond.setPaperType(null);
                mxjbAnswersCond.setQuestionId(null);
                mxjbAnswersCond.setSeq(null);
                mxjbAnswersCond.setKnowledgeId(null);
                mxjbPaperAnswersMapper.updateMxjbPaperAnswers(mxjbAnswersCond);
            } else {
                mxjbAnswersCond.setAnswerId(null);
                mxjbAnswersCond.setExamineeId(examineeId);
                mxjbAnswersCond.setExamineeType(MxjbContants.ExamineeTypeIe);
                mxjbAnswersCond.setPaperType("ai");
                mxjbAnswersCond.setQuestionId(t.getQuestionId());
                mxjbAnswersCond.setSeq(t.getSeq());
                mxjbAnswersCond.setKnowledgeId(t.getKnowledgeId());
                mxjbPaperAnswersMapper.insertMxjbPaperAnswers(mxjbAnswersCond);
            }
        });
        if (oldQuestionAnswersMap.size() > 0) {
            List<Long> oldIds = oldQuestionAnswersMap.values().stream().map(MxjbPaperAnswers::getAnswerId).collect(Collectors.toList());
            mxjbPaperAnswersMapper.deleteMxjbPaperAnswersByIds(oldIds.toArray(new Long[oldIds.size()]));
        }
    }

    // 试卷考生处理
    public QuestionDto openQuestion(Long examineeId, Long userId, Long questionId) {
        MxjbPaperExaminee mxjbAiExaminee = mxjbPaperExamineeMapper.selectMxjbPaperExamineeById(examineeId);
        Papers paper = papersMapper.selectPapersById(mxjbAiExaminee.getPaperId());
        PaperDto paperDto = loadPaperDto(paper, mxjbAiExaminee, mxjbAiExaminee.getState() >= MxjbContants.ExamineeStatusPublish, true);
        Map<Long, QuestionDto> questionMap = paperDto.getQuestions().stream().collect(Collectors.toMap(QuestionDto::getQuestionId, Function.identity()));
        QuestionDto standard = questionMap.get(questionId);
        return standard;
    }

    private PaperDto loadPaperDto(Papers paper, MxjbPaperExaminee mxjbPaperExaminee, boolean loadParse, boolean keepTitleNotChange) {
        PaperDto paperDto = new PaperDto();
        List<Questions> questionsList;
        Map map = Maps.newHashMap();
        map.put("paperId", paper.getId());
        map.put("userId", SecurityUtils.getLoginUser().getUserId());
        questionsList = questionsMapper.selectQuestionsByPaperId(map);

        questionsService.changeQuestionList(questionsList, true);

        paperDto.setExamineeId(mxjbPaperExaminee.getExamineeId());
        paperDto.setExamineeType(MxjbContants.ExamineeTypeIeValue);
        paperDto.setName(paper.getPaperName());
        paperDto.setPaperId(paper.getId());
        paperDto.setScoringType(MxjbContants.ScoringTypeSelf);
        paperDto.setPublish(true);
        paperDto.setMode(120L); // TODO 竞赛要设置时间
        Date now = new Date();
        paperDto.setEvaluationState(MxjbContants.EvaluationStatusOpen);

        paperDto.setState(mxjbPaperExaminee.getState());
        paperDto.setBeginTime(mxjbPaperExaminee.getBeginTime());
        paperDto.setEndTime(DateUtils.addMinutes(paperDto.getBeginTime(), paperDto.getMode().intValue()));
        paperDto.setRemaining(0L);
        if (MxjbContants.ExamineeStatusExamine.equals(mxjbPaperExaminee.getState())) {
            paperDto.setRemaining(Duration.between(now.toInstant(), paperDto.getEndTime().toInstant()).getSeconds());
        }
        Map<Long, MxjbPaperAnswers> questionAnswersMap = paperDto.getState() >= MxjbContants.ExamineeStatusExamine
                ? mxjbPaperAnswersMapper.selectMxjbAnswersByPaperExamineeId(mxjbPaperExaminee.getExamineeId()).stream().collect(Collectors.toMap(MxjbPaperAnswers::getQuestionId, Function.identity()))
                : null;

        Boolean fillParseAndAnswer = loadParse || MxjbContants.ExamineeStatusPublish.equals(paperDto.getState()) // 自阅卷或发布时可显示成绩
                || MxjbContants.ExamineeStatusReview.equals(paperDto.getState());

        List<QuestionDto> questionDtoList = paperDto.getQuestions();
        questionsList.forEach(t -> {
            QuestionDto dto = adminExaminationService.toQuestionDto(t, fillParseAndAnswer, questionAnswersMap);
            dto.setSeq(questionDtoList.size() + 1L);
            questionDtoList.add(dto);
        });
        // adminExaminationService.fillSubQuestions(subQuestionDtoMap, fillParseAndAnswer);
        paperDto.setMinutes(paperDto.getRemaining() / 60);
        return paperDto;
    }

    private PaperDto initNewKnowledgePaper(String knowledgeKey, SysUser sysUser, Long subjectId, String typeName, String type, Long paperId, Long round, Long step, boolean keepTitleNotChange) {
        String name;
        if (type.equals("know_test") && null != paperId) {
            KnowledgeTree knowledgeTree = knowledgeTreeMapper.selectKnowledgeTreeById(paperId);
            name = typeName + "_" + knowledgeTree.getName();
        } else {
            name = typeName;
        }

        List<Questions> questionsList = buildQuestions(type, subjectId, paperId);
        if (CollectionUtils.isEmpty(questionsList)) {
            throw new RuntimeException("无满足要求的试题: " + knowledgeKey);
        }
        Subject subject;
        Papers paper = new Papers();
        paper.setPaperName(name);
        paper.setLocation(sysUser.getProvinceName());
        paper.setExamineeType(sysUser.getExamType());
        paper.setSubjectId(questionsList.get(0).getSubjectId());
        if(null != paper.getSubjectId() && null != (subject = subjectMapper.selectSubjectById(paper.getSubjectId().longValue()))) {
            paper.setSubjectName(subject.getSubjectName());
        }
        // paper.setParseName("");
        paper.setNumber(String.valueOf(questionsList.size()));
        // paper.setTiid("");
        // paper.setYear();
        // paper.setArea();
        // paper.setGradeName("");
        paper.setPaperType(type);
        paper.setMark(0L);
        // paper.setIsExist(1);
        paper.setFenshu(0L);
        paper.setPaperSource(0);
        // paper.setOsspath();
        // paper.setFilename();
        // paper.setAttachments();
        paper.setPaperTypeId(3);
        papersMapper.insertPapers(paper);
//        papersMapper.insertMxjbPapers(paper);

        MxjbPaperTest mxjbPaper = new MxjbPaperTest();
        mxjbPaper.setPaperId(paper.getId());
        mxjbPaper.setCreatorId(sysUser.getUserId());
        mxjbPaper.setKnownledges(knowledgeKey);
        mxjbPaper.setState(MxjbContants.ExamineeStatusExamine);
        mxjbPaper.setRound(round);
        mxjbPaper.setName(name);
        mxjbPaper.setCreatedTime(new Date());
        mxjbPaper.setStep(step);
        mxjbPaper.setSubjectid(null != paper.getSubjectId() ? paper.getSubjectId().longValue() : null);
        mxjbPaperTestMapper.insertMxjbPaperTest(mxjbPaper);

        List<Paperques> paperquesList = Lists.newArrayList();
        String examType = Constant.EXAM_TYPE_ZG.equals(sysUser.getExamType()) ? "zg" : "ie";
        for(Questions question : questionsList) {
            Paperques pq = new Paperques();
            pq.setPaperId(paper.getId());
            pq.setQuestionId(question.getId());
            pq.setType(examType);
            pq.setSorces(BigDecimal.ONE);
            paperquesList.add(pq);
        }
        paperquesMapper.batchInsert(paperquesList);

        return initNewExaminee(sysUser, paper, keepTitleNotChange);
    }

    private PaperDto initNewExaminee(SysUser sysUser, Papers paper, boolean keepTitleNotChange) {
        MxjbPaperExaminee mxjbPaperExaminee = new MxjbPaperExaminee();
        mxjbPaperExaminee.setCustomerCode(sysUser.getCode());
        mxjbPaperExaminee.setPaperId(paper.getId());
        mxjbPaperExaminee.setBeginTime(new Date());
        mxjbPaperExaminee.setState(MxjbContants.ExamineeStatusExamine);
        mxjbPaperExaminee.setScoreLevel("W");
        mxjbPaperExaminee.setScore(0L);
        mxjbPaperExaminee.setScoreRate(0.0);
        mxjbPaperExaminee.setRanking(0L);
        mxjbPaperExaminee.setChooseCount(0);
        mxjbPaperExaminee.setChooseTotal(0);
        mxjbPaperExaminee.setSubjectiveCount(0);
        mxjbPaperExaminee.setSubjectiveTotal(0);
        mxjbPaperExamineeMapper.insertMxjbPaperExaminee(mxjbPaperExaminee);

        PaperDto paperDto = loadPaperDto(paper, mxjbPaperExaminee, false, keepTitleNotChange);
        paperDto.setPaperType(MxjbContants.PaperTypeCustom);
        return paperDto;
    }

    private List<Questions> buildQuestions(String type, Long subjectId, Long paperId) {
        Map cond = Maps.newHashMap();

        cond.put("paperId", paperId);
        cond.put("subjectId", subjectId);
        cond.put("userId", SecurityUtils.getLoginUser().getUserId());

        if("必刷题".equals(type)) {
            cond.put("type", "know_test");
            cond.put("incTypes", cacheService.ChooseQuestionTypeSet);
        } else {
            cond.put("type", type);
        }
        Set<Long> existIdSet = Sets.newHashSet();
        List<Questions> finalList = Lists.newArrayList();
        // 单选10 多选5 判断3 主观2
//        cond.put("incTypes", cacheService.ChooseItemQuestionTypeSet);
//        appendQuestions(finalList, cond, 10);
//        cond.put("incTypes", cacheService.MultiQuestionTypeSet);
//        appendQuestions(finalList, cond, 5);
//        cond.put("incTypes", cacheService.ChooseRightQuestionTypeSet);
//        appendQuestions(finalList, cond, 3);
//        cond.clear();
//        cond.put("type", type);
//        cond.put("paperId", paperId);
//        cond.put("subjectId", subjectId);
//        cond.put("excTypes", CollectionUtils.union(cacheService.ChooseQuestionTypeSet, cacheService.MultiQuestionTypeSet));
        appendQuestions(existIdSet, finalList, cond, 20);
        if (finalList.size() == 0) {
            cond.put("userId", 0);
            appendQuestions(existIdSet, finalList, cond, 20);
        }
        Collections.sort(finalList, new Comparator<Questions>() {
            @Override
            public int compare(Questions o1, Questions o2) {
                Boolean s1 = cacheService.ChooseQuestionTypeSet.contains(o1.getType());
                Boolean s2 = cacheService.ChooseQuestionTypeSet.contains(o2.getType());
                int iRet;
                if (0 != (iRet = s1.compareTo(s1))) {
                    return iRet;
                }
                return o1.getId().compareTo(o2.getId());
            }
        });
        return finalList;
    }

    private List<Questions> appendQuestions(Set<Long> existIdSet, List<Questions> finalList, Map cond, Integer count) {
        Integer maxCount = questionsMapper.selectTestQuestionsByMapCount(cond);
        Random random = new Random();
        Integer startPos = maxCount > 100 ? random.nextInt(maxCount - 100) : 0;
        cond.put("startPos", startPos);
        List<Questions> questionsList = questionsMapper.selectTestQuestionsByMap(cond);
        int maxCnt = Math.min(questionsList.size(), count);
        for (int i = maxCnt; i > 0; i--) {
            int next = random.nextInt(questionsList.size());
            Questions q = questionsList.remove(next);
            if (existIdSet.add(q.getId())) {
                finalList.add(q);
            }
        }
        return questionsList;
    }

}
