package com.ruoyi.web.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.ruoyi.common.core.content.VistorContextHolder;
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.enums.ExamType;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.mxjb.domain.MxjbContants;
import com.ruoyi.sy.domain.*;
import com.ruoyi.sy.mapper.*;
import com.ruoyi.web.domain.CategoryDto;
import com.ruoyi.web.domain.PaperDto;
import com.ruoyi.web.domain.QuestionDto;
import com.ruoyi.web.domain.TestMarjorDto;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.stereotype.Service;

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

@Service
public class SyTestMajorService {
    private final SyMajorMapper majorMapper;
    private final SyKnowledgeInterstTypeMapper syKnowledgeInterstTypeMapper;
    private final SyMajorCategoryMapper syMajorCategoryMapper;
    private final SyMajorMajorMapper syMajorMajorMapper;
    private final SyOccupationInterstMajorMapper syOccupationInterstMajorMapper;
    private final SyOccupationInterstTypeMapper syOccupationInterstTypeMapper;
    private final SyPhilosophyViewMajorMapper syPhilosophyViewMajorMapper;
    private final SyPhilosophyViewTypeMapper syPhilosophyViewTypeMapper;
    private final SyTestAnswersMapper syTestAnswersMapper;
    private final SyTestExamineeMapper syTestExamineeMapper;
    private final SyTestPaperMapper syTestPaperMapper;
    private final SyTestQuestionMapper syTestQuestionMapper;
    private final SyTestSelectCategoryMapper syTestSelectCategoryMapper;
    private final SyTestSelectMajorMapper syTestSelectMajorMapper;
    private final ExamUtilService examUtilService;
    private final RedisCache redisCache;
    private Set<String> marjorCodeSet;
    private Set<String> marjorNameSet;

    private ObjectMapper om = new ObjectMapper();
    // key1 category, key2 value(RIA...)
    // key1 category, key2 value(喜欢, 擅长)
    private TypeReference<Map<String, Map<String, Integer>>> keyCatValueTypeReference = new TypeReference<Map<String, Map<String, Integer>>>() {};
    private TypeReference<Map<String, Integer>> keyValueTypeReference = new TypeReference<Map<String, Integer>>() {};

    public SyTestMajorService(SyMajorMapper majorMapper, SyKnowledgeInterstTypeMapper syKnowledgeInterstTypeMapper, SyMajorCategoryMapper syMajorCategoryMapper,
                              SyMajorMajorMapper syMajorMajorMapper, SyOccupationInterstMajorMapper syOccupationInterstMajorMapper,
                              SyOccupationInterstTypeMapper syOccupationInterstTypeMapper, SyPhilosophyViewMajorMapper syPhilosophyViewMajorMapper, SyPhilosophyViewTypeMapper syPhilosophyViewTypeMapper,
                              SyTestAnswersMapper syTestAnswersMapper, SyTestExamineeMapper syTestExamineeMapper, SyTestPaperMapper syTestPaperMapper, SyTestQuestionMapper syTestQuestionMapper, SyTestSelectCategoryMapper syTestSelectCategoryMapper,
                              SyTestSelectMajorMapper syTestSelectMajorMapper, ExamUtilService examUtilService, RedisCache redisCache) {
        this.majorMapper = majorMapper;
        this.syKnowledgeInterstTypeMapper = syKnowledgeInterstTypeMapper;
        this.syMajorCategoryMapper = syMajorCategoryMapper;
        this.syMajorMajorMapper = syMajorMajorMapper;
        this.syOccupationInterstMajorMapper = syOccupationInterstMajorMapper;
        this.syOccupationInterstTypeMapper = syOccupationInterstTypeMapper;
        this.syPhilosophyViewMajorMapper = syPhilosophyViewMajorMapper;
        this.syPhilosophyViewTypeMapper = syPhilosophyViewTypeMapper;
        this.syTestAnswersMapper = syTestAnswersMapper;
        this.syTestExamineeMapper = syTestExamineeMapper;
        this.syTestPaperMapper = syTestPaperMapper;
        this.syTestQuestionMapper = syTestQuestionMapper;
        this.syTestSelectCategoryMapper = syTestSelectCategoryMapper;
        this.syTestSelectMajorMapper = syTestSelectMajorMapper;
        this.examUtilService = examUtilService;
        this.redisCache = redisCache;
    }

    public Set<String> loadMarjorCodes() {
        if(null == marjorCodeSet) {
            ExamType examType = VistorContextHolder.getExamType();
//            marjorCodeSet = syMajorMajorMapper.selectSyMajorMajorList(new SyMajorMajor()).stream().map( t -> t.getMajorCode()).collect(Collectors.toSet());
            marjorCodeSet = majorMapper.selectSyMajorList(new SyMajor()).stream()
                    .filter(t -> examType != null && examType.title().equals(t.getExamType()))
                    .map( t -> t.getCode()).collect(Collectors.toSet());
        }
        return marjorCodeSet;
    }

    public Set<String> loadMarjorNames() {
        if(null == marjorNameSet) {
            ExamType examType = VistorContextHolder.getExamType();
            marjorNameSet = majorMapper.selectSyMajorList(new SyMajor()).stream()
                    .filter(t -> examType != null && examType.title().equals(t.getExamType()))
                    .map( t -> t.getName())
                    .collect(Collectors.toSet());
        }
        return marjorNameSet;
    }
    public List<SyTestExaminee> getTestListWithStatus(Integer testType, Integer[] states) {
        Map cond = Maps.newHashMap();
        cond.put("paperId", testType.longValue());
        cond.put("customerCode", SecurityUtils.getLoginUser().getUser().getUserId().toString());
        cond.put("states", states);
        return syTestExamineeMapper.selectSyTestExamineeLatest(cond);
    }

    public List<SyTestExaminee> getTestList(Integer testType) {
        SyTestExaminee cond = new SyTestExaminee();
        cond.setPaperId(testType.longValue());
        cond.setCustomerCode(SecurityUtils.getLoginUser().getUser().getUserId().toString());
        return syTestExamineeMapper.selectSyTestExamineeList(cond);
    }

    public SyTestExaminee getTestExaminee(Integer testType, Long examineeId) {
        SyTestExaminee examinee = syTestExamineeMapper.selectSyTestExamineeById(examineeId);
        return examinee;
    }

    public List<TestMarjorDto.TestMatchStat> toTestMatchStats(SyTestExaminee examinee) {
        if (StringUtils.isNotBlank(examinee.getStats())) {
            TestMarjorDto.TestStats testStat = null;
            try {
                testStat = om.readValue(examinee.getStats(), TestMarjorDto.TestStats.class);
                return testStat.getMatchStats();
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return Lists.newArrayList();
    }

    public AjaxResult saveUserSelectedCategory(CategoryDto.UserCategory params) {
        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        SyTestExaminee examinee = null != params.getExamineeId() ? syTestExamineeMapper.selectSyTestExamineeById(params.getExamineeId()) : getLatestExaminee(params.getTestType(), sysUser.getCode());

        SyTestSelectCategory categoryCond = new SyTestSelectCategory();
        categoryCond.setExamineeId(examinee.getExamineeId());
        List<SyTestSelectCategory> categoryList = syTestSelectCategoryMapper.selectSyTestSelectCategoryList(categoryCond);
        Map<String, SyTestSelectCategory> oldCategoryMap = categoryList.stream().collect(Collectors.toMap(SyTestSelectCategory::getMajorCategoryCode, Function.identity()));

        SyTestSelectCategory mxjbCategoryCond = new SyTestSelectCategory();
        for(String code : Sets.newHashSet(params.getCodes())) {
            SyTestSelectCategory oldCategory = oldCategoryMap.remove(code);
            if (null != oldCategory) {
                mxjbCategoryCond.setId(oldCategory.getId());
                mxjbCategoryCond.setState(1L);
                syTestSelectCategoryMapper.updateSyTestSelectCategory(mxjbCategoryCond);
            } else {
                mxjbCategoryCond.setId(null);
                mxjbCategoryCond.setState(1L);
                mxjbCategoryCond.setExamineeId(examinee.getExamineeId());
                mxjbCategoryCond.setCustomerCode(examinee.getCustomerCode());
                mxjbCategoryCond.setMajorCategoryCode(code);
                syTestSelectCategoryMapper.insertSyTestSelectCategory(mxjbCategoryCond);
            }
        }
        if (oldCategoryMap.size() > 0) {
            List<Long> oldIds = oldCategoryMap.values().stream().map(SyTestSelectCategory::getId).collect(Collectors.toList());
            syTestSelectCategoryMapper.deleteSyTestSelectCategoryByIds(oldIds.toArray(new Long[oldIds.size()]));
        }
        return AjaxResult.success();
    }

    public AjaxResult getUserSelectedMajor() {
        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        Map categoryCond = Maps.newHashMap();
        categoryCond.put("customerCode", sysUser.getCode());
        List<SyMajorCategory> categoryList = syMajorCategoryMapper.selectUserSyMajorMajor(categoryCond);
        return AjaxResult.success(categoryList);
    }


    public AjaxResult saveUserSelectedMajor(CategoryDto.UserMajor param) {
        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        SyTestSelectMajor categoryCond = new SyTestSelectMajor();
        categoryCond.setCustomerCode(sysUser.getCode());
        Map<String, SyTestSelectMajor> oldCategoryMap = Maps.newHashMap();
        Set<String> existCodeSet = Sets.newHashSet();
        List<Long> deleteIdList = Lists.newArrayList();
        for(SyTestSelectMajor major : syTestSelectMajorMapper.selectSyTestSelectMajorList(categoryCond)) {
            if (existCodeSet.add(major.getMajorCode())) {
                oldCategoryMap.put(major.getMajorCode(), major);
            } else {
                deleteIdList.add(major.getId());
            }
        }
        if (deleteIdList.size() > 0) {
            syTestSelectMajorMapper.deleteSyTestSelectMajorByIds(deleteIdList.toArray(new Long[deleteIdList.size()]));
        }
        SyTestSelectMajor mxjbCategoryCond = new SyTestSelectMajor();
        for(String code : Sets.newHashSet(param.getCodes())) {
            SyTestSelectMajor oldCategory = oldCategoryMap.remove(code);
            if (null != oldCategory) {
                mxjbCategoryCond.setId(oldCategory.getId());
                mxjbCategoryCond.setState(1L);
                syTestSelectMajorMapper.updateSyTestSelectMajor(mxjbCategoryCond);
            } else {
                mxjbCategoryCond.setId(null);
                mxjbCategoryCond.setState(1L);
                mxjbCategoryCond.setCustomerCode(sysUser.getCode());
                mxjbCategoryCond.setUserId(sysUser.getUserId());
                mxjbCategoryCond.setMajorCode(code);
                syTestSelectMajorMapper.insertSyTestSelectMajor(mxjbCategoryCond);
            }
        }
        if (oldCategoryMap.size() > 0) {
            List<Long> oldIds = oldCategoryMap.values().stream().map(SyTestSelectMajor::getId).collect(Collectors.toList());
            syTestSelectMajorMapper.deleteSyTestSelectMajorByIds(oldIds.toArray(new Long[oldIds.size()]));
        }
        return AjaxResult.success();
    }

    public AjaxResult loadAllMajorSubjectTree() {
        Map cond = new HashMap();
        List<SyMajorCategory> majorCategoryList = syMajorCategoryMapper.selectAllMajorList(cond);;
        return AjaxResult.success(buildTree(majorCategoryList));
    }

    public AjaxResult loadSelectMajorSubjectTree() {
        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        SyTestExaminee examineeCond = new SyTestExaminee();
        examineeCond.setCustomerCode(sysUser.getCode());
        examineeCond.setState(MxjbContants.ExamineeStatusPublish);
        List<SyTestExaminee> existList = syTestExamineeMapper.selectSyTestExamineeList(examineeCond);
        List<Long> examineeIds = existList.stream().map(t -> t.getExamineeId()).collect(Collectors.toList());

        if (CollectionUtils.isEmpty(examineeIds)) {
            return AjaxResult.success(examineeIds);
        }

        Map cond = new HashMap();
        cond.put("examineeIds", examineeIds);
        List<SyMajorCategory> majorCategoryList = syMajorCategoryMapper.selectSelectedMajorList(cond);;
        return AjaxResult.success(buildTree(majorCategoryList));
    }

    private List<TestMarjorDto.CategoryNode> buildTree(List<SyMajorCategory> majorCategoryList) {
        List<TestMarjorDto.CategoryNode> categoryNodeList = Lists.newArrayList();
        if (CollectionUtils.isEmpty(majorCategoryList)) {
            return categoryNodeList;
        }
        Map<String, TestMarjorDto.CategoryNode> categoryNodeMap = Maps.newHashMap();
        for(SyMajorCategory mc : majorCategoryList) {
            TestMarjorDto.CategoryNode n = categoryNodeMap.get(mc.getMajorSubjectCode());
            if (null == n) {
                n = new TestMarjorDto.CategoryNode(mc.getMajorSubjectCode(), mc.getMajorSubjectName(), Lists.newArrayList());
                categoryNodeMap.put(mc.getMajorSubjectCode(), n);
                categoryNodeList.add(n);
            }
            String snKey = mc.getMajorSubjectCode() + "_" + mc.getMajorCategoryCode();
            TestMarjorDto.CategoryNode sn = categoryNodeMap.get(snKey);
            if (null == sn) {
                sn = new TestMarjorDto.CategoryNode(mc.getMajorCategoryCode(), mc.getMajorCategoryName(), Lists.newArrayList());
                categoryNodeMap.put(snKey, sn);
                n.getNodes().add(sn);
            }
            TestMarjorDto.CategoryNode ssn = new TestMarjorDto.CategoryNode(mc.getMajorCode(), mc.getMajorName(), null);
            sn.getNodes().add(ssn);
        }
        return categoryNodeList.size() != 1 ? categoryNodeList : categoryNodeList.get(0).getNodes();
    }

    private List<SyMajorCategory> findMatchCategoryList(Integer testType, String matchCode) {
        if (StringUtils.isBlank(matchCode)) {
            return Lists.newArrayList();
        }
        Map cond = new HashMap();
        List<SyMajorCategory> majorCategoryList = null;
        switch (testType) {
            case 1:
                cond.put("matchCodes", StringUtils.split(matchCode, ","));
                majorCategoryList = syMajorCategoryMapper.selectSyMajorCategoryListByView(cond);
                break;
            case 2:
                cond.put("matchCodes", Lists.newArrayList(matchCode));
                majorCategoryList = syMajorCategoryMapper.selectSyMajorCategoryListByOccupation(cond);
                break;
            case 3:
                cond.put("matchCodes", StringUtils.split(matchCode, ","));
                majorCategoryList = syMajorCategoryMapper.selectSyMajorCategoryListByKnowledge(cond);
                break;
            default:
                majorCategoryList = Lists.newArrayList();
        }
        return majorCategoryList;
    }
    public AjaxResult loadMatchCategoryTree(Integer testType, String matchCode, Integer level) {
        List<SyMajorCategory> majorCategoryList = findMatchCategoryList(testType, matchCode);
        Map<String, TestMarjorDto.CategoryNode> categoryNodeMap = Maps.newHashMap();
        List<TestMarjorDto.CategoryNode> categoryNodeList = Lists.newArrayList();
        for(SyMajorCategory mc : majorCategoryList) {
            TestMarjorDto.CategoryNode n = categoryNodeMap.get(mc.getMajorSubjectCode());
            if (null == n) {
                n = new TestMarjorDto.CategoryNode(mc.getMajorSubjectCode(), mc.getMajorSubjectName(), Lists.newArrayList());
                categoryNodeMap.put(mc.getMajorSubjectCode(), n);
                categoryNodeList.add(n);
            }
            TestMarjorDto.CategoryNode sn = new TestMarjorDto.CategoryNode(mc.getMajorCategoryCode(), mc.getMajorCategoryName(), null);
            n.getNodes().add(sn);
        }
        return AjaxResult.success(categoryNodeList);
    }


    public AjaxResult getAllTestMajorResult() throws JsonProcessingException {
        Map allResult = new HashMap();
        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        Map categoryCond = Maps.newHashMap();
        categoryCond.put("customerCode", sysUser.getCode());
        List<SyMajorCategory> categoryList = syMajorCategoryMapper.selectUserSyMajorMajor(categoryCond);
        allResult.put("selectedList", categoryList);
        allResult.put("philosophy", getTestResult(getLatestExaminee(1, sysUser.getCode())));
        allResult.put("occupation", getTestResult(getLatestExaminee(2, sysUser.getCode())));
        allResult.put("knowledge", getTestResult(getLatestExaminee(3, sysUser.getCode())));
        return AjaxResult.success(allResult);
    }

    public AjaxResult loadTestMajorResult(Integer testType, String customerCode) throws JsonProcessingException {
        SyTestExaminee examinee = getLatestExaminee(testType, customerCode);
        return loadTestMajorResult(examinee);
    }
    public AjaxResult loadTestMajorResult(Long examineeId) throws JsonProcessingException {
        SyTestExaminee examinee = syTestExamineeMapper.selectSyTestExamineeById(examineeId);
        return loadTestMajorResult(examinee);
    }
    public AjaxResult loadTestMajorResult(SyTestExaminee examinee) throws JsonProcessingException {
        TestMarjorDto.TestResultDto dto;
        return null == (dto = getTestResult(examinee)) ? AjaxResult.success() : AjaxResult.success(dto);
    }

    private TestMarjorDto.TestResultDto getTestResult(SyTestExaminee examinee) throws JsonProcessingException {
        if (null == examinee) {
            return null;
        }
        boolean testCompleted = examinee.getState() >= MxjbContants.ExamineeStatusPublish;

        SyTestPaper paper = syTestPaperMapper.selectSyTestPaperById(examinee.getPaperId());
        TestMarjorDto.TestStats testStat = null;
        Map<String, TestMarjorDto.TestMatchStat> categoryCountMap = Maps.newHashMap();
        Map<String, Map<String, Integer>> catKeyValueMap = null;
        String matchCode = null;
        boolean noMatch = false;
        if (StringUtils.isNotBlank(examinee.getStats())) {
            testStat = om.readValue(examinee.getStats(), TestMarjorDto.TestStats.class);
            if (CollectionUtils.isNotEmpty(testStat.getMatchStats())) {
                for (TestMarjorDto.TestMatchStat ts : testStat.getMatchStats()) {
                    categoryCountMap.put(ts.getCode(), ts);
                }
            }
            catKeyValueMap = testStat.getCatKeyValueMap();
            matchCode = testStat.getMatchCode();
            noMatch = testStat.isNoMatch();
        }
        boolean selectCompleted = noMatch || isSelectCompleted(examinee.getExamineeId());

        Map categoryCond = Maps.newHashMap();
        categoryCond.put("examineeId", examinee.getExamineeId());
        List<SyMajorCategory> selectedList = syMajorCategoryMapper.selectUserSyMajorCategory(categoryCond);

        switch (paper.getCategory()) {
            case 1:
                Map<String, SyPhilosophyViewType> viewTypeMap = Maps.newHashMap();
                List<SyPhilosophyViewType> viewTypeList = syPhilosophyViewTypeMapper.selectSyPhilosophyViewTypeList(new SyPhilosophyViewType());

                for (SyPhilosophyViewType type : viewTypeList) {
                    type.setViewMajorList(Lists.newArrayList());
                    viewTypeMap.put(type.getCode(), type);
                    TestMarjorDto.TestMatchStat ts = categoryCountMap.get(type.getCode());
                    if (null != ts) {
                        Integer total = ts.getCount();
                        type.setTotal(null != total ? total : 0);
                        type.setMatched(ts.isMatched());
                    } else {
                        type.setTotal(0);
                        type.setMatched(false);
                    }
                    if (null != catKeyValueMap) {
                        type.setStats(catKeyValueMap.get(type.getCode()));
                    }
                }
                for (SyPhilosophyViewMajor viewMajor : syPhilosophyViewMajorMapper.selectSyPhilosophyViewMajorList(new SyPhilosophyViewMajor())) {
                    viewTypeMap.get(viewMajor.getMatchCode()).getViewMajorList().add(viewMajor);
                }
                Collections.sort(viewTypeList, new Comparator<SyPhilosophyViewType>() {
                    @Override
                    public int compare(SyPhilosophyViewType o1, SyPhilosophyViewType o2) {
                        int iRet = -o1.getTotal().compareTo(o2.getTotal());
                        if (0 == iRet) {
                            iRet = o1.getCode().compareTo(o2.getCode());
                        }
                        return iRet;
                    }
                });
                return new TestMarjorDto.TestResultDto(examinee.getExamineeId(), selectedList, viewTypeList, matchCode, testCompleted, selectCompleted, noMatch);
            case 2:
                List<SyOccupationInterstType> occupationTypeList = syOccupationInterstTypeMapper.selectSyOccupationInterstTypeList(new SyOccupationInterstType());
                for (SyOccupationInterstType type : occupationTypeList) {
                    TestMarjorDto.TestMatchStat ts = categoryCountMap.get(type.getCode());
                    if (null != ts) {
                        Integer total = ts.getCount();
                        type.setTotal(null != total ? total : 0);
                        type.setMatched(ts.isMatched());
                    } else {
                        type.setTotal(0);
                        type.setMatched(false);
                    }
                    if (null != catKeyValueMap) {
                        type.setStats(catKeyValueMap.get(type.getCode()));
                    }
                }
                Collections.sort(occupationTypeList, new Comparator<SyOccupationInterstType>() {
                    @Override
                    public int compare(SyOccupationInterstType o1, SyOccupationInterstType o2) {
                        int iRet = -o1.getTotal().compareTo(o2.getTotal());
                        if (0 == iRet) {
                            iRet = o1.getCode().compareTo(o2.getCode());
                        }
                        return iRet;
                    }
                });
                return new TestMarjorDto.TestResultDto(examinee.getExamineeId(), selectedList, occupationTypeList, matchCode, testCompleted, selectCompleted, noMatch);
            case 3:
                List<SyKnowledgeInterstType> knowledgeTypeList = syKnowledgeInterstTypeMapper.selectSyKnowledgeInterstTypeList(new SyKnowledgeInterstType());
                for (SyKnowledgeInterstType type : knowledgeTypeList) {
                    TestMarjorDto.TestMatchStat ts = categoryCountMap.get(type.getId().toString());
                    if (null != ts) {
                        Integer total = ts.getCount();
                        type.setTotal(null != total ? total : 0);
                        type.setMatched(ts.isMatched());
                    } else {
                        type.setTotal(0);
                        type.setMatched(false);
                    }
                }
                Collections.sort(knowledgeTypeList, new Comparator<SyKnowledgeInterstType>() {
                    @Override
                    public int compare(SyKnowledgeInterstType o1, SyKnowledgeInterstType o2) {
                        int iRet = -o1.getTotal().compareTo(o2.getTotal());
                        if (0 == iRet) {
                            iRet = o1.getCode().compareTo(o2.getCode());
                        }
                        return iRet;
                    }
                });
                return new TestMarjorDto.TestResultDto(examinee.getExamineeId(), selectedList, knowledgeTypeList, matchCode, testCompleted, selectCompleted, noMatch);
        }
        return null;
    }

    private TestMarjorDto.TestStats getStats(Integer testType, Long paperId, List<QuestionDto> questionlist) {
        SyTestQuestion cond = new SyTestQuestion();
        cond.setPaperId(paperId);
        Map<Long, SyTestQuestion> stdQuestionMap = syTestQuestionMapper.selectSyTestQuestionList(cond).stream().collect(Collectors.toMap(SyTestQuestion::getQuestionId, Function.identity()));

        TestMarjorDto.TestStats testStats = new TestMarjorDto.TestStats();
        // key1=category, key2=value, value=score
        Map<String, Map<String, Integer>> catKeyValueMap = null;
        List<TestMarjorDto.TestMatchStat> matchStats = null;
        String matchedCode = null;
        boolean noMatch = false;
        switch (testType) {
            case 1:
                // k1=value(A/B/D/F), k2=questionCategory, value=按answer求和, 复选总分(限制总分不重复, 3), 复选评价(限制总分不重复, 6)
                catKeyValueMap = Maps.newHashMap();
                matchStats = Lists.newArrayList();
                for (QuestionDto ques : questionlist) {
                    String normalAnswer = MxjbContants.getNormalAnswer(ques.getAnswer(), "");
                    if (StringUtils.isBlank(normalAnswer)) {
                        continue;
                    }
                    SyTestQuestion stdQuestion = stdQuestionMap.get(ques.getQuestionId());
                    if(StringUtils.isNotBlank(normalAnswer)) {
                        String[] answerScores = normalAnswer.split(",");
                        for (int i = 0; i < answerScores.length; i++) {
                            Integer cnt = Integer.parseInt(answerScores[i]);
                            String newValue = getAnswerValue(stdQuestion, i);
                            if (StringUtils.isNotBlank(newValue)) {
                                Map<String, Integer> valueMap = catKeyValueMap.get(newValue);
                                if (null == valueMap) {
                                    valueMap = Maps.newHashMap();
                                    catKeyValueMap.put(newValue, valueMap);
                                }
                                Integer oldCount = valueMap.get(stdQuestion.getQuesitonCateogry());
                                valueMap.put(stdQuestion.getQuesitonCateogry(), null != oldCount ? oldCount + cnt : cnt);
                            }
                        }
                    }
                }
                for (String category : catKeyValueMap.keySet()) {
                    TestMarjorDto.TestMatchStat testMatchStat = new TestMarjorDto.TestMatchStat();
                    testMatchStat.setCode(category);
                    Integer sum = 0;
                    for (Integer v : catKeyValueMap.get(category).values()) {
                        sum += v;
                    }
                    testMatchStat.setCount(sum);
                    testMatchStat.setMatched(false);
                    matchStats.add(testMatchStat);
                }
                Collections.sort(matchStats, new Comparator<TestMarjorDto.TestMatchStat>() {
                    @Override
                    public int compare(TestMarjorDto.TestMatchStat o1, TestMarjorDto.TestMatchStat o2) {
                        return -o1.getCount().compareTo(o2.getCount());
                    }
                });
                matchedCode = getMatchedCodeByTop(matchStats, 3);
                break;
            case 2:
                // k1=questionCategory, k2=value(Like/Familiar), value=对应个数
                catKeyValueMap = Maps.newHashMap();
                matchStats = Lists.newArrayList();
                String newValue;
                for (QuestionDto ques : questionlist) {
                    SyTestQuestion stdQuestion = stdQuestionMap.get(ques.getQuestionId());
                    String normalAnswer = MxjbContants.getNormalAnswer(ques.getAnswer(), "");
                    if (StringUtils.isNotBlank(normalAnswer) && StringUtils.isNotBlank((newValue = getAnswerValue(stdQuestion, normalAnswer.charAt(0) - 'A')))) {;
                        Map<String, Integer> valueMap = catKeyValueMap.get(stdQuestion.getQuesitonCateogry());
                        if (null == valueMap) {
                            valueMap = Maps.newHashMap();
                            catKeyValueMap.put(stdQuestion.getQuesitonCateogry(), valueMap);
                        }
                        String valueName = "Like".equals(newValue) ? "喜欢" : "擅长"; // 最多7个
                        Integer oldCount = valueMap.get(valueName);
                        valueMap.put(valueName, null != oldCount ? Math.min(oldCount + 1, 7) : 1);
                    }
                }
                for (String category : catKeyValueMap.keySet()) {
                    TestMarjorDto.TestMatchStat testMatchStat = new TestMarjorDto.TestMatchStat();
                    testMatchStat.setCode(category);
                    Integer sum = 0;
                    for (Integer v : catKeyValueMap.get(category).values()) {
                        sum += v;
                    }
                    testMatchStat.setCount(sum);
                    testMatchStat.setMatched(false);
                    matchStats.add(testMatchStat);
                }
                Collections.sort(matchStats, new Comparator<TestMarjorDto.TestMatchStat>() {
                    @Override
                    public int compare(TestMarjorDto.TestMatchStat o1, TestMarjorDto.TestMatchStat o2) {
                        return -o1.getCount().compareTo(o2.getCount());
                    }
                });
                List<String> matchCodeList = Lists.newArrayList();
                noMatch = getMatchedCode(matchStats, matchCodeList);
                matchedCode = StringUtils.join(matchCodeList, ",");
                break;
            case 3:
                // id 对应 questionCategory, value 对应分值
                Map<String, Integer> keyValueMap = Maps.newHashMap();
                for (QuestionDto ques : questionlist) {
                    SyTestQuestion stdQuestion = stdQuestionMap.get(ques.getQuestionId());
                    Integer oldValue = keyValueMap.get(stdQuestion.getQuesitonCateogry());
                    String normalAnswer = MxjbContants.getNormalAnswer(ques.getAnswer(), "");
                    if (StringUtils.isNotBlank(normalAnswer)) {
                        keyValueMap.put(stdQuestion.getQuesitonCateogry(), Integer.parseInt(getAnswerValue(stdQuestion, normalAnswer.charAt(0) - 'A')) + (null != oldValue ? oldValue : 0));
                    }
                }
                matchStats = Lists.newArrayList();
                for (String category : keyValueMap.keySet()) {
                    TestMarjorDto.TestMatchStat testMatchStat = new TestMarjorDto.TestMatchStat();
                    testMatchStat.setCode(category);
                    testMatchStat.setCount(keyValueMap.get(category));
                    testMatchStat.setMatched(false);
                    matchStats.add(testMatchStat);
                }
                Collections.sort(matchStats, new Comparator<TestMarjorDto.TestMatchStat>() {
                    @Override
                    public int compare(TestMarjorDto.TestMatchStat o1, TestMarjorDto.TestMatchStat o2) {
                        return -o1.getCount().compareTo(o2.getCount());
                    }
                });
                matchedCode = getMatchedCodeByTop(matchStats, 3);
                break;
        }
        testStats.setCatKeyValueMap(catKeyValueMap);
        testStats.setMatchStats(matchStats);
        testStats.setMatchCode(matchedCode);
        testStats.setNoMatch(StringUtils.isNotBlank(matchedCode) && (noMatch || CollectionUtils.isEmpty(findMatchCategoryList(testType, matchedCode))));
        return testStats;
    }

    /**
     * 根据用户选择查询对应的值
     * @param stdQuestion
     * @param optionIdx
     * @return
     */
    private String getAnswerValue(SyTestQuestion stdQuestion, int optionIdx) {
        switch(optionIdx) {
            case 0:
                return stdQuestion.getValueA();
            case 1:
                return stdQuestion.getValueB();
            case 2:
                return stdQuestion.getValueC();
            case 3:
                return stdQuestion.getValueD();
            case 4:
                return stdQuestion.getValueE();
        }
        return "";
    }

    public List<SyTestQuestion> getPaperQuestions(Long paperId) {
        SyTestQuestion cond = new SyTestQuestion();
        cond.setPaperId(paperId);
        List<SyTestQuestion> questionsList = syTestQuestionMapper.selectSyTestQuestionList(cond);
        return questionsList;
    }

    public AjaxResult saveTestPaper(PaperDto paperDto) {
        Long examineeId = paperDto.getExamineeId();
        SyTestExaminee examinee = syTestExamineeMapper.selectSyTestExamineeById(examineeId);
        if (examinee.getState() >= MxjbContants.ExamineeStatusReview) {
            return AjaxResult.error(examineeId + " 已经在批阅的不可保存答案");
        }
        String key = examUtilService.getExamineeAnswersKey(MxjbContants.ExamineeTypeTestKey, paperDto.getExamineeId());
        if (CollectionUtils.isEmpty(paperDto.getQuestions())) {
            Map<String, QuestionDto> questionDtoMap = redisCache.getCacheMap(key);
            questionDtoMap.remove("0");
            paperDto.setQuestions(Lists.newArrayList(questionDtoMap.values()));
        }

        if (CollectionUtils.isEmpty(paperDto.getQuestions()) || getPaperQuestions(examinee.getPaperId()).size() != paperDto.getQuestions().size()) {
            return AjaxResult.error(examineeId + "未答完题时不能保存");
        }
        SyTestPaper paper = syTestPaperMapper.selectSyTestPaperById(examinee.getPaperId());
        updateTestAnswers(paperDto.getQuestions(), paperDto.getExamineeId());
        SyTestExaminee examineeCond = new SyTestExaminee();
        examineeCond.setExamineeId(paperDto.getExamineeId());
        examineeCond.setState(MxjbContants.ExamineeStatusPublish);
        examineeCond.setEndTime(new Date());

        TestMarjorDto.TestStats testStats = examinee.getPaperId().equals(4L) ? getMentalHealth(paperDto.getQuestions()) : getStats(paper.getCategory(), paper.getPaperId(), paperDto.getQuestions());
        try {
            examineeCond.setStats(om.writeValueAsString(testStats));
        } catch (JsonProcessingException e) {
            return AjaxResult.error(examineeId + " 已经在批阅的不可保存答案");
        }
        syTestExamineeMapper.updateSyTestExaminee(examineeCond);
        redisCache.deleteObject(key);
        return AjaxResult.success("成功");
    }

    public AjaxResult updateExamineeStats(Long examineeId) {
        SyTestExaminee examinee = syTestExamineeMapper.selectSyTestExamineeById(examineeId);
        SyTestPaper paper = syTestPaperMapper.selectSyTestPaperById(examinee.getPaperId());

        SyTestAnswers answerCond = new SyTestAnswers();
        answerCond.setExamineeId(examineeId);

        List<QuestionDto> questionlist = Lists.newArrayList();
        List<SyTestAnswers> answersList = syTestAnswersMapper.selectSyTestAnswersList(answerCond);
        for(SyTestAnswers a : answersList) {
            QuestionDto dto = new QuestionDto();
            dto.setQuestionId(a.getQuestionId());
            dto.setAnswer(a.getAnswer());
            questionlist.add(dto);
        }

        SyTestExaminee examineeCond = new SyTestExaminee();
        examineeCond.setExamineeId(examinee.getExamineeId());
        TestMarjorDto.TestStats testStats = examinee.getPaperId().equals(4L) ? getMentalHealth(questionlist) : getStats(paper.getCategory(), paper.getPaperId(), questionlist);
        try {
            examineeCond.setStats(om.writeValueAsString(testStats));
        } catch (JsonProcessingException e) {
            return AjaxResult.error(examineeId + " 统计序列化异常");
        }
        syTestExamineeMapper.updateSyTestExaminee(examineeCond);
        return AjaxResult.success();
    }

    private TestMarjorDto.TestStats getMentalHealth(List<QuestionDto> questionlist) {
        SyTestQuestion cond = new SyTestQuestion();
        cond.setPaperId(4L);
        Map<Long, SyTestQuestion> stdQuestionMap = Maps.newHashMap();
        Map<String, Integer> categoryValueMap = Maps.newLinkedHashMap();
        for (SyTestQuestion q : syTestQuestionMapper.selectSyTestQuestionList(cond)) {
            stdQuestionMap.put(q.getQuestionId(), q);
            categoryValueMap.putIfAbsent(q.getQuesitonCateogry(), 0);
        }
        for (QuestionDto ques : questionlist) {
            String normalAnswer = MxjbContants.getNormalAnswer(ques.getAnswer(), "");
            if (StringUtils.isBlank(normalAnswer)) {
                continue;
            }
            SyTestQuestion stdQuestion = stdQuestionMap.get(ques.getQuestionId());
            if(StringUtils.isNotBlank(normalAnswer)) {
                String value = normalAnswer.equals("A") ? stdQuestion.getValueA() : stdQuestion.getValueB();
                Integer v = NumberUtils.toInt(value, 0);
                String c = stdQuestion.getQuesitonCateogry();
                Integer ov = categoryValueMap.get(c);
                categoryValueMap.put(c, null != ov ? ov + v : v);
            }
        }
        TestMarjorDto.TestStats testStats = new TestMarjorDto.TestStats();
        List<TestMarjorDto.TestMatchStat> matchStats = Lists.newArrayList();
        for (String name : categoryValueMap.keySet()) {
            matchStats.add(new TestMarjorDto.TestMatchStat(name, categoryValueMap.get(name), false));
        }
        testStats.setMatchStats(matchStats);
        return testStats;
    }

    private String getMatchedCodeByTop(List<TestMarjorDto.TestMatchStat> matchStats, Integer top) {
        if (CollectionUtils.isEmpty(matchStats)) {
            return "";
        }
        List<String> matchCodeList = Lists.newArrayList();
        for (TestMarjorDto.TestMatchStat ts : matchStats) {
            matchCodeList.add(ts.getCode());
            ts.setMatched(true);
            if (matchCodeList.size() == top) {
                break;
            }
        }
        return StringUtils.join(matchCodeList, ",");
    }

    private boolean getMatchedCode(List<TestMarjorDto.TestMatchStat> matchStats, List<String> codeList) {
        if (CollectionUtils.isEmpty(matchStats)) {
            return true;
        }
        Set<String> validCodeSet = syOccupationInterstMajorMapper.selectSyOccupationInterstMajorList(new SyOccupationInterstMajor()).stream().map(t -> t.getMatchCode()).collect(Collectors.toSet());
        TestMarjorDto.TestMatchStat ts1 = matchStats.get(0);
        TestMarjorDto.TestMatchStat ts2 = null ;
        String code;
        if (matchStats.size() == 1 || (ts1.getCount() - (ts2 = matchStats.get(1)).getCount()) > 4) { // 只有一个或可忽略第二个时
            ts1.setMatched(true);
            code = ts1.getCode() + "**";
        } else {
            TestMarjorDto.TestMatchStat ts3 = null ;
            String code2;
            if (matchStats.size() == 2 || (ts2.getCount() - (ts3 = matchStats.get(2)).getCount()) > 4) { // 只有两个或忽略第3个时
                ts1.setMatched(true);
                ts2.setMatched(true);
                code = ts1.getCode() + ts2.getCode() + "*";
                if (!validCodeSet.contains(code)) {
                    if (ts1.getCount() == ts2.getCount() && validCodeSet.contains((code2 = ts2.getCode() + ts1.getCode() + "*"))) {
                        matchStats.add(0, matchStats.remove(1)); // 交换 12位置
                        code = code2;
                    } else if (validCodeSet.contains((code2 = ts1.getCode() + "**"))) { // 降级
                        code = code2;
                    } else if (validCodeSet.contains((code2 = ts2.getCode() + "**"))) {
                        matchStats.add(0, matchStats.remove(1));
                        code = code2;
                    }
                }
            } else {
                code = ts1.getCode() + ts2.getCode() + ts3.getCode();
                List<TestMarjorDto.TestMatchStat> needMatchStats = Lists.newArrayList();
                List<TestMarjorDto.TestMatchStat> remainMatchStats;
                if (ts2.getCount() > ts3.getCount()) { // ts2 固定
                    remainMatchStats = splitStats(matchStats, 2, ts3.getCount(), needMatchStats);
                    if (null != (code2 = matchStatsTry(validCodeSet, matchStats, needMatchStats))) {
                        code = code2;
                    } else {
                        matchStats.addAll(needMatchStats);
                        if (validCodeSet.contains((code2 = ts1.getCode() + ts2.getCode() + "*"))) {
                            code = code2;
                        } else if (validCodeSet.contains((code2 = ts1.getCode() + "**"))) {
                            code = code2;
                        }
                    }
                    matchStats.addAll(remainMatchStats);
                } else { // ts2 == ts3
                    remainMatchStats = splitStats(matchStats, 1, ts2.getCount(), needMatchStats);
                    if (ts1.getCount() == ts2.getCount()) { // 3 of needMatchStats
                        needMatchStats.add(0, matchStats.remove(0));
                    }  // t1 + 2 of needMatchStats
                    if (null != (code2 = matchStatsTry(validCodeSet, matchStats, needMatchStats))) {
                        code = code2;
                    }  else {
                        if (matchStats.size() == 0) {
                            code = needMatchStats.get(0).getCode() + "**";
                        } else {
                            code = matchStats.get(0).getCode() + "**";
                        }
                        matchStats.addAll(needMatchStats);
                    }
                    matchStats.addAll(remainMatchStats);
                }
            }
        }
        codeList.add(code);
        return !validCodeSet.contains(code);
    }

    private String matchStatsTry(Set<String> validCodeSet, List<TestMarjorDto.TestMatchStat> matchStats, List<TestMarjorDto.TestMatchStat> needMatchStats) {
        String code = null;
        if (matchStats.size() == 0) { // 任意组合3个
            List<List<TestMarjorDto.TestMatchStat>> permutaionList = Lists.newArrayList();
            buildPermutation(needMatchStats.toArray(new TestMarjorDto.TestMatchStat[needMatchStats.size()]), 3, Lists.newArrayList(), Sets.newHashSet(), permutaionList);
            for (List<TestMarjorDto.TestMatchStat> tsList : permutaionList) {
                TestMarjorDto.TestMatchStat ts1 = tsList.get(0);
                TestMarjorDto.TestMatchStat ts2 = tsList.get(1);
                TestMarjorDto.TestMatchStat ts3 = tsList.get(2);
                if (validCodeSet.contains((code = ts1.getCode() + ts2.getCode() + ts3.getCode()))) {
                    matchStats.addAll(tsList);
                    return combineMatch(code, ts1, ts2, ts3);
                }
            }
        } else if(matchStats.size() == 1) { // 任意组合2个
            TestMarjorDto.TestMatchStat ts1 = matchStats.get(0);
            List<List<TestMarjorDto.TestMatchStat>> permutaionList = Lists.newArrayList();
            buildPermutation(needMatchStats.toArray(new TestMarjorDto.TestMatchStat[needMatchStats.size()]), 2, Lists.newArrayList(), Sets.newHashSet(), permutaionList);
            for (List<TestMarjorDto.TestMatchStat> matchStatList : permutaionList) {
                TestMarjorDto.TestMatchStat ts2 = matchStatList.get(0);
                TestMarjorDto.TestMatchStat ts3 = matchStatList.get(1);
                if (validCodeSet.contains((code = ts1.getCode() + ts2.getCode() + ts3.getCode()))) {
                    matchStats.addAll(matchStatList);
                    return combineMatch(code, ts1, ts2, ts3);
                }
            }
        } else { // 前两个可能交换
            TestMarjorDto.TestMatchStat ts1 = matchStats.get(0);
            TestMarjorDto.TestMatchStat ts2 = matchStats.get(1);
            for (TestMarjorDto.TestMatchStat ts3 : needMatchStats) {
                if (validCodeSet.contains((code = ts1.getCode() + ts2.getCode() + ts3.getCode()))) {
                    matchStats.add(ts3);
                    return combineMatch(code, ts1, ts2, ts3);
                } else if (ts1.getCount() == ts2.getCount()) {
                    if (validCodeSet.contains((code = ts1.getCode() + ts2.getCode() + ts3.getCode()))) {
                        matchStats.add(0, matchStats.remove(1)); // 交换 12位置
                        matchStats.add(ts3);
                        return combineMatch(code, ts1, ts2, ts3);
                    }
                }
            }
        }
        return null;
    }

    private String combineMatch(String code, TestMarjorDto.TestMatchStat ts1, TestMarjorDto.TestMatchStat ts2, TestMarjorDto.TestMatchStat ts3) {
        ts1.setMatched(true);
        ts2.setMatched(true);
        ts3.setMatched(true);
        return code;
    }

    private void buildPermutation(TestMarjorDto.TestMatchStat[] matchStats, Integer matchCount, List<TestMarjorDto.TestMatchStat> baseStats,
                                  Set<Integer> usedIndexSet, List<List<TestMarjorDto.TestMatchStat>> permutaionList) {
        boolean lastCount = matchCount == baseStats.size() + 1;
        for (int i = 0; i < matchStats.length; i++) {
            if (usedIndexSet.add(i)) {
                List<TestMarjorDto.TestMatchStat> foundList = Lists.newArrayList(baseStats);
                foundList.add(matchStats[i]);
                if (lastCount) {
                    permutaionList.add(foundList);
                } else {
                    buildPermutation(matchStats, matchCount, foundList, usedIndexSet, permutaionList);
                }
                usedIndexSet.remove(i);
            }
        }
    }

    /**
     * 从除列中删除指定位置后的，并根据count分成两组
     * @param matchStats
     * @param index
     * @param matchCount
     * @param needMatchStats
     * @return
     */
    private List<TestMarjorDto.TestMatchStat> splitStats(List<TestMarjorDto.TestMatchStat> matchStats, Integer index, Integer matchCount, List<TestMarjorDto.TestMatchStat> needMatchStats) {
        List<TestMarjorDto.TestMatchStat> remainMatchStats = Lists.newArrayList();
        for (int i = index; i < matchStats.size(); ) {
            TestMarjorDto.TestMatchStat ts = matchStats.remove(i);
            if (ts.getCount().equals(matchCount)) {
                needMatchStats.add(ts);
            } else {
                remainMatchStats.add(ts);
            }
        }
        return remainMatchStats;
    }

    public void updateTestAnswers(List<QuestionDto> questions, Long examineeId) {
        SyTestExaminee examinee = syTestExamineeMapper.selectSyTestExamineeById(examineeId);

        SyTestAnswers answerCond = new SyTestAnswers();
        answerCond.setExamineeId(examineeId);
        List<SyTestAnswers> answersList = syTestAnswersMapper.selectSyTestAnswersList(answerCond);
        Map<Long, SyTestAnswers> oldQuestionAnswersMap = answersList.stream().collect(Collectors.toMap(SyTestAnswers::getQuestionId, Function.identity()));
        Map<Long, QuestionDto> newQuestionAnswerMap = questions.stream().collect(Collectors.toMap(QuestionDto::getQuestionId, Function.identity()));

        SyTestQuestion questionCond = new SyTestQuestion();
        questionCond.setPaperId(examinee.getPaperId());
        List<SyTestQuestion> stdQuestionList = syTestQuestionMapper.selectSyTestQuestionList(questionCond);

        SyTestAnswers mxjbAnswersCond = new SyTestAnswers();
        stdQuestionList.forEach(t -> {
            SyTestAnswers 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 (null != oldAnswer) {
                mxjbAnswersCond.setAnswerId(oldAnswer.getAnswerId());
                mxjbAnswersCond.setExamineeId(null);
                mxjbAnswersCond.setExamineeType(null);
                mxjbAnswersCond.setQuestionId(null);
                mxjbAnswersCond.setSeq(null);
                syTestAnswersMapper.updateSyTestAnswers(mxjbAnswersCond);
            } else {
                mxjbAnswersCond.setAnswerId(null);
                mxjbAnswersCond.setExamineeId(examineeId);
                mxjbAnswersCond.setExamineeType(MxjbContants.ExamineeTypeTest);
                mxjbAnswersCond.setQuestionId(t.getQuestionId());
                mxjbAnswersCond.setSeq(t.getSeq());
                syTestAnswersMapper.insertSyTestAnswers(mxjbAnswersCond);
            }
        });
        if (oldQuestionAnswersMap.size() > 0) {
            List<Long> oldIds = oldQuestionAnswersMap.values().stream().map(SyTestAnswers::getAnswerId).collect(Collectors.toList());
            syTestAnswersMapper.deleteSyTestAnswersByIds(oldIds.toArray(new Long[oldIds.size()]));
        }
    }

    public PaperDto loadPaperByTestMajorTypeId(Integer testType) {
        Long paperId = testType.longValue();

        SysUser sysUser = SecurityUtils.getLoginUser().getUser();
        SyTestExaminee mxjbExaminee = getLatestExaminee(testType, sysUser.getCode());
        SyTestPaper syTestPaper = syTestPaperMapper.selectSyTestPaperById(paperId.longValue());
        if (null != mxjbExaminee && (mxjbExaminee.getState() < MxjbContants.ExamineeStatusPublish)) { // 未测试完成的先继续测试
            PaperDto paperDto = loadPaperDto(syTestPaper, mxjbExaminee, true);
            if (MxjbContants.ExamineeStatusExamine.equals(mxjbExaminee.getState())) {
                examUtilService.updateTempAnswerAndSave(paperDto, mxjbExaminee.getExamineeId(), MxjbContants.ExamineeTypeTest);
            }
            return paperDto;
        }
        if (null != mxjbExaminee) {
            if (4 != testType && !isSelectCompleted(mxjbExaminee)) { // 测试完成但未选择的先选择
                throw new RuntimeException("请选选择专业类，完成本轮测试, 再进行下一轮");
            }
            // 关闭上一轮的测试，进入下一轮
            SyTestExaminee examineeUpdate = new SyTestExaminee();
            examineeUpdate.setExamineeId(mxjbExaminee.getExamineeId());
            examineeUpdate.setState(MxjbContants.ExamineeStatusClose);
            syTestExamineeMapper.updateSyTestExaminee(examineeUpdate);
        }
        SyTestExaminee examineeCond = new SyTestExaminee();
        PaperDto paperDto = loadPaperDto(syTestPaper, null, false);
        if (MxjbContants.ExamineeStatusSign.equals(paperDto.getState())) {
            paperDto.setRemaining(paperDto.getMode() * 60);
            paperDto.setMinutes(paperDto.getRemaining() / 60);
            paperDto.setState(MxjbContants.ExamineeStatusExamine);
            examineeCond.setPaperId(paperId);
            examineeCond.setCustomerCode(sysUser.getCode());
            examineeCond.setState(paperDto.getState());
            examineeCond.setBeginTime(paperDto.getBeginTime());
            examineeCond.setEndTime(paperDto.getEndTime());
            examineeCond.setScoreLevel("W");
            examineeCond.setScore(0L);
            examineeCond.setScoreRate(0.0);
            examineeCond.setRanking(0L);
            syTestExamineeMapper.insertSyTestExaminee(examineeCond);
            paperDto.setExamineeId(examineeCond.getExamineeId());
        }
        paperDto.getQuestions().get(0).setCurrent(true);
        return paperDto;
    }


    public PaperDto loadPaperByExaminerId(Long examineeId) {
        SyTestExaminee mxjbExaminee = syTestExamineeMapper.selectSyTestExamineeById(examineeId);
        if (null == mxjbExaminee) {
            throw new RuntimeException("无测试人员");
        }
        SyTestPaper syTestPaper = syTestPaperMapper.selectSyTestPaperById(mxjbExaminee.getPaperId());
        PaperDto paperDto = loadPaperDto(syTestPaper, mxjbExaminee, true);
        return paperDto;
    }

    private PaperDto loadPaperDto(SyTestPaper syTestPaper, SyTestExaminee examinee, boolean loadParse) {
        PaperDto paperDto = new PaperDto();
        SyTestQuestion cond = new SyTestQuestion();
        cond.setPaperId(syTestPaper.getPaperId());
        List<SyTestQuestion> questionsList = syTestQuestionMapper.selectSyTestQuestionList(cond);
        paperDto.setExamineeType(MxjbContants.ExamineeTypeTestValue);
        paperDto.setName(syTestPaper.getName());
        paperDto.setPaperId(syTestPaper.getPaperId());
        paperDto.setScoringType(MxjbContants.ScoringTypeAuto);
        paperDto.setPublish(true);
        paperDto.setMode(99*60L); // TODO 竞赛要设置时间
        paperDto.setEvaluationState(MxjbContants.EvaluationStatusOpen);
        paperDto.setBeginTime(new Date());
        paperDto.setEndTime(DateUtils.addMinutes(paperDto.getBeginTime(), paperDto.getMode().intValue()));
        if (null == examinee) { // 没有考生
            paperDto.setMinutes(0L);
            paperDto.setRemaining(0L);
            paperDto.setState(MxjbContants.ExamineeStatusSign);
        } else {
            paperDto.setState(examinee.getState());
            if (examinee.getState() >= MxjbContants.ExamineeStatusPublish) {
                paperDto.setBeginTime(examinee.getBeginTime());
                paperDto.setEndTime(examinee.getEndTime());
                paperDto.setRemaining(0L);
            } else {
                paperDto.setRemaining(paperDto.getMode() * 60);
            }
            paperDto.setMinutes(paperDto.getRemaining() / 60);
            paperDto.setExamineeId(examinee.getExamineeId());
        }
        final Map<Long, SyTestAnswers> questionAnswersMap;
        if(paperDto.getState() >= MxjbContants.ExamineeStatusExamine) {
            SyTestAnswers answerCond = new SyTestAnswers();
            answerCond.setExamineeId(examinee.getExamineeId());
            answerCond.setExamineeType(MxjbContants.ExamineeTypeTest);
            questionAnswersMap = syTestAnswersMapper.selectSyTestAnswersList(answerCond).stream().collect(Collectors.toMap(SyTestAnswers::getQuestionId, Function.identity()));
        } else {
            questionAnswersMap = null;
        }

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

        List<QuestionDto> questionDtoList = paperDto.getQuestions();
        questionsList.forEach(t -> {
            QuestionDto dto = examUtilService.toQuestionDto(t, fillParseAndAnswer, questionAnswersMap);
            dto.setSeq(questionDtoList.size() + 1L);
            questionDtoList.add(dto);
        });
        return paperDto;
    }

    public boolean isSelectCompleted(SyTestExaminee mxjbExaminee) {
        boolean noMatch = false;
        if (StringUtils.isNotBlank(mxjbExaminee.getStats())) {
            TestMarjorDto.TestStats testStat = null;
            try {
                testStat = om.readValue(mxjbExaminee.getStats(), TestMarjorDto.TestStats.class);
                noMatch = testStat.isNoMatch();
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return noMatch || isSelectCompleted(mxjbExaminee.getExamineeId());
    }

    private boolean isSelectCompleted(Long examineeId) {
        SyTestSelectCategory selectCategoryCond = new SyTestSelectCategory();
        selectCategoryCond.setExamineeId(examineeId);
        boolean selectCompleted = CollectionUtils.isNotEmpty(syTestSelectCategoryMapper.selectSyTestSelectCategoryList(selectCategoryCond));
        return selectCompleted;
    }

    private SyTestExaminee getLatestExaminee(Integer testType, String customerCode) {
        Long paperId = testType.longValue();
        Map cond = Maps.newHashMap();
        cond.put("paperId", paperId);
        cond.put("customerCode", customerCode);
        cond.put("cnt", 1);
        List<SyTestExaminee> existList = syTestExamineeMapper.selectSyTestExamineeLatest(cond);
        return CollectionUtils.isNotEmpty(existList) ? existList.get(0) : null;
    }

}
