package com.ynfy.buss.exam.question.service.impl;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ynfy.buss.exam.paper.enmus.LevelType;
import com.ynfy.buss.exam.question.dto.QuestionConditionDTO;
import com.ynfy.buss.exam.question.dto.QuestionDTO;
import com.ynfy.buss.exam.question.entity.Question;
import com.ynfy.buss.exam.question.enums.QuestionType;
import com.ynfy.buss.exam.question.mapper.QuestionMapper;
import com.ynfy.buss.exam.question.service.IQuestionService;
import com.ynfy.buss.exam.questionanswer.entity.QuestionAnswer;
import com.ynfy.buss.exam.questionanswer.service.IQuestionAnswerService;
import com.ynfy.buss.exam.repository.entity.Repository;
import com.ynfy.buss.exam.repository.service.IRepositoryService;
import com.ynfy.buss.practice.userpractice.enums.PracticeMode;
import com.ynfy.common.utils.CharUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: question
 * @Author: jeecg-boot
 * @Date: 2023-02-22
 * @Version: V1.0
 */
@Slf4j
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements IQuestionService {

    @Autowired
    private IQuestionAnswerService questionAnswerService;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private IRepositoryService repositoryService;

    /**
     * 保存题目
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveQuestion(Question question) {
        // 校验数据
        this.checkData(question);
        // 更新
        this.saveOrUpdate(question);
        // 保存题目答案选项
        questionAnswerService.saveAll(question.getId(), question.getType(), question.getAnswerList());
    }

    /**
     * 校验题目信息
     *
     * @throws Exception
     */
    public void checkData(Question question) {
        if (StringUtils.isEmpty(question.getContent())) {
            throw new JeecgBootException("题目内容不能为空！");
        }
        if (StringUtils.isEmpty(question.getRepositoryId())) {
            throw new JeecgBootException("题库不能为空！");
        }
        //单选，多选，判断题的答案选项校验
        if (QuestionType.RADIO.getCode().equals(question.getType()) || QuestionType.MULTI.getCode().equals(question.getType())
                || QuestionType.JUDGE.getCode().equals(question.getType())) {
            List<QuestionAnswer> answers = question.getAnswerList();
            if (CollectionUtils.isEmpty(answers)) {
                throw new JeecgBootException("客观题至少要包含一个备选答案！");
            }
            int trueCount = 0;
            for (QuestionAnswer a : answers) {
                if (StringUtils.isEmpty(a.getContent())) {
                    throw new JeecgBootException("答案选项内容不为空！");
                }
                if (!Objects.isNull(a.getIsRight()) && a.getIsRight()) {
                    trueCount += 1;
                }
            }
            if (trueCount == 0) {
                throw new JeecgBootException("至少要包含一个正确项！");
            }
            //单选题
            if (QuestionType.RADIO.getCode().equals(question.getType()) && trueCount > 1) {
                throw new JeecgBootException("单选题不能包含多个正确项！");
            }
        }
    }

    /**
     * 根据规则生成试题
     *
     * @param repositoryId
     * @param questionType
     * @param num
     * @return
     */
    @Override
    public List<Question> generateQuestionsByRule(String repositoryId, Integer questionType, Integer level,
                                                  Integer num, List<Question> ruleQuestions) {
        List<Question> questionList = ruleQuestions.stream().filter(o -> o.getRepositoryId().equals(repositoryId)
                && o.getType().equals(questionType) && o.getLevel().equals(level)).collect(Collectors.toList());
        return getRandom(questionList, num);
    }

    /**
     * 统计资源库题型的数量
     */
    public long count(String repositoryId, Integer questionType) {
        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Question::getRepositoryId, repositoryId).eq(Question::getType, questionType);
        return this.count(queryWrapper);
    }

    public List<Question> listQuestion(String repositoryId, Integer questionType, Integer level) {
        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Question::getRepositoryId, repositoryId).eq(Question::getType, questionType).eq(Question::getLevel, level);
        return this.list(queryWrapper);
    }

    /**
     * 抽取试题
     */
    private List<Question> getRandom(List<Question> questionList, int num) {
        List backList = new ArrayList<Question>();
        Random random = new Random();
        int backSum = 0;
        if (questionList.size() >= num) {
            backSum = num;
        } else {
            backSum = questionList.size();
        }
        for (int i = 0; i < backSum; i++) {
            // 随机数的范围为0-list.size0-1
            int target = random.nextInt(questionList.size());
            backList.add(questionList.get(target));
            questionList.remove(target);
        }
        return backList;
    }


    /**
     * 导入题目
     *
     * @param file
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importExcel(MultipartFile file) throws Exception {
        List<Repository> repositoryList = repositoryService.list();
        Map<String, Repository> repositoryMap = repositoryList.stream().collect(Collectors.toMap(Repository::getCode, a -> a, (k1, k2) -> k1));
        long beginTime = System.currentTimeMillis();
        ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
        List<Map<String, Object>> dataList = reader.readAll();

        //校验数据的合法性
        checkData(dataList);

        //导入数据
        importData(beginTime, dataList, repositoryMap);
    }

    /**
     * 导入前校验数据
     */
    public void checkData(List<Map<String, Object>> dataList) {
        if (CollectionUtils.isEmpty(dataList)) {
            throw new JeecgBootException("导入的数据不能为空!");
        }
        int index = 2;
        for (Map<String, Object> map : dataList) {
            if (StringUtils.isBlank(String.valueOf(map.get("题型")))) {
                throw new JeecgBootException("第【" + index + "】行题型不能为空!");
            }
            if (!"单选题".equals(map.get("题型")) && !"多选题".equals(map.get("题型")) && !"判断题".equals(map.get("题型"))
                    && !"简答题".equals(map.get("题型")) && !"填空题".equals(map.get("题型"))) {
                throw new JeecgBootException("第【" + index + "】行题型不符合要求!");
            }
            if (StringUtils.isBlank(String.valueOf(map.get("所属题库名称")))) {
                throw new JeecgBootException("第【" + index + "】行所属题库名称不能为空!");
            }
            if (StringUtils.isBlank(String.valueOf(map.get("所属题库编码")))) {
                throw new JeecgBootException("第【" + index + "】行所属题库编码不能为空!");
            }
            if (StringUtils.isBlank(String.valueOf(map.get("题干")))) {
                throw new JeecgBootException("第【" + index + "】行题干不能为空!");
            }
            //单选题和多选题检查选项
            if ("单选题".equals(map.get("题型")) || "多选题".equals(map.get("题型")) || "填空题".equals(map.get("题型"))) {
                List<String> optionList = new ArrayList<>();
                for (Map.Entry<String, Object> entity : map.entrySet()) {
                    String key = entity.getKey();
                    Object value = entity.getValue();
                    if (key.contains("选项")) {
                        if (StringUtils.isNotBlank(String.valueOf(value))) {
                            optionList.add(String.valueOf(value));
                        }
                    }
                }
                if (CollectionUtils.isEmpty(optionList)) {
                    throw new JeecgBootException("第【" + index + "】行至少包含一个选项!");
                }
            }
            if (!("简答题".equals(map.get("题型")) || "填空题".equals(map.get("题型")))) {//填空题和简答题不检查答案
                if (StringUtils.isBlank(String.valueOf(map.get("答案")))) {
                    throw new JeecgBootException("第【" + index + "】行答案不能为空!");
                }
            }
            index++;
        }
    }

    /**
     * 导入数据
     *
     * @param beginTime
     * @param dataList
     * @param repositoryMap
     */
    public void importData(long beginTime, List<Map<String, Object>> dataList, Map<String, Repository> repositoryMap) {
        if (!CollectionUtils.isEmpty(dataList)) {
            List<Question> questionList = new ArrayList<>();
            List<QuestionAnswer> answerList = new ArrayList<>();
            List<Repository> repositoryList = new ArrayList<>();
            for (Map<String, Object> map : dataList) {
                Question question = new Question();
                question.setId(IdUtil.getSnowflake(1, 1).nextIdStr());
                List<QuestionAnswer> answers = new ArrayList<>();
                for (Map.Entry<String, Object> entity : map.entrySet()) {
                    String key = entity.getKey();
                    Object value = entity.getValue();
                    if (key.contains("选项")) {
                        if (("单选题".equals(map.get("题型")) || "多选题".equals(map.get("题型"))) ||
                                "填空题".equals(map.get("题型")) && StringUtils.isNotBlank(value.toString())) {
                            if (!Objects.isNull(value) && StringUtils.isNotBlank(String.valueOf(value).trim())) {
                                QuestionAnswer answer = new QuestionAnswer();
                                answer.setContent(String.valueOf(value));
                                if ("填空题".equals(map.get("题型"))) {
                                    answer.setTag(String.valueOf(CharUtil.getZmIndex(key.replaceAll("选项", ""))));
                                } else {
                                    answer.setTag(key.replaceAll("选项", ""));
                                }
                                answers.add(answer);
                            }
                        } else if ("判断题".equals(map.get("题型")) && CollectionUtils.isEmpty(answers)) {
                            QuestionAnswer answerYes = new QuestionAnswer();
                            answerYes.setContent("正确");
                            answerYes.setTag("A");
                            QuestionAnswer answerNo = new QuestionAnswer();
                            answerNo.setContent("错误");
                            answerNo.setTag("B");
                            answers.add(answerYes);
                            answers.add(answerNo);
                        }
                    } else {
                        switch (key) {
                            case "题型":
                                if ("单选题".equals(value)) {
                                    question.setType(QuestionType.RADIO.getCode());
                                } else if ("多选题".equals(value)) {
                                    question.setType(QuestionType.MULTI.getCode());
                                } else if ("判断题".equals(value)) {
                                    question.setType(QuestionType.JUDGE.getCode());
                                } else if ("简答题".equals(value)) {
                                    question.setType(QuestionType.SIMPLE.getCode());
                                } else if ("填空题".equals(value)) {
                                    question.setType(QuestionType.BLANK.getCode());
                                }
                                break;
                            case "难易程度":
                                if ("简单".equals(value)) {
                                    question.setLevel(1);
                                } else if ("一般".equals(value)) {
                                    question.setLevel(2);
                                } else if ("较难".equals(value)) {
                                    question.setLevel(3);
                                }
                                break;
                            case "题干":
                                question.setContent(String.valueOf(value));
                                break;
                            case "解析":
                                question.setAnalysis(String.valueOf(value));
                                break;
                            case "所属题库编码":
                                //设置所属题库
                                //先从现有的题库找
                                Repository repository = repositoryMap.get(String.valueOf(value));
                                if (!Objects.isNull(repository)) {
                                    question.setRepositoryId(repository.getId());
                                } else {
                                    //没找到题库,从新建的题库去找
                                    Repository r = null;
                                    if (!CollectionUtils.isEmpty(repositoryList)) {
                                        List<Repository> tmpList = repositoryList.stream().filter(o -> !Objects.isNull(o.getCode())
                                                && o.getCode().equals(String.valueOf(value))).collect(Collectors.toList());
                                        if (!CollectionUtils.isEmpty(tmpList)) {
                                            r = tmpList.get(0);
                                        } else {
                                            //新建
                                            r = createRepository(value, map, repositoryList);
                                        }
                                    } else {
                                        //新建
                                        r = createRepository(value, map, repositoryList);
                                    }
                                    question.setRepositoryId(r.getId());
                                }
                                break;
                        }
                    }
                }
                if (QuestionType.BLANK.getCode().equals(question.getType())) {
                    //填空题重新设置内容中的填空，替换题目中的括号
                    question.setContent(replaceBlankContent(question.getContent()));
                }
                questionList.add(question);
                int sort = 1;
                List<QuestionAnswer> answerGroupList = new ArrayList<>();
                for (QuestionAnswer answer : answers) {
                    answer.setQuestionId(question.getId());
                    if (QuestionType.RADIO.getCode().equals(question.getType()) || QuestionType.MULTI.getCode().equals(question.getType())) {
                        answer.setIsRight(String.valueOf(map.get("答案")).contains(answer.getTag()) ? true : false);
                    } else if (QuestionType.JUDGE.getCode().equals(question.getType())) {
                        answer.setIsRight(String.valueOf(map.get("答案")).contains(answer.getContent()) ? true : false);
                    } else if (QuestionType.BLANK.getCode().equals(question.getType())) {
                        answer.setIsRight(true);
                    }
                    answer.setSort(sort);
                    answerGroupList.add(answer);
                    sort++;
                }
                answerList.addAll(answerGroupList);
            }
            saveBatch(questionList);
            questionAnswerService.saveBatch(answerList);
            if (!CollectionUtils.isEmpty(repositoryList)) {
                repositoryService.saveBatch(repositoryList);
            }
        }
        log.info("成功导入：{}个题目，耗时：{}", dataList.size(), (System.currentTimeMillis() - beginTime) / 1000 + "秒");
    }

    /**
     * 导入的填空题重新设置内容
     *
     * @param s
     */
    public String replaceBlankContent(String s) {
        StringBuilder sb = new StringBuilder();
        int index = 1;
        int preIndex = 0;
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {//若为左括号，则向右寻找右括号的位置
                int j = i;
                while (s.charAt(j) != ')') {
                    j++;
                }
                sb.append(s, preIndex, i).append("<span contenteditable=\"false\" class=\"xe-blanknumber\" " +
                        "data-name=\"blanknumber\" data-content=\"{&quot;content&quot;:&quot;" + index + "&quot;}\">" + index + "</span>");
                i = j;
                preIndex = j + 1;
                index++;
            }
        }
        if (preIndex > 0 && s.length() > preIndex) {
            sb.append(s.substring(preIndex));
        }
        return sb.toString();
    }

    /**
     * 新建题库
     *
     * @param value
     * @param map
     * @param repositoryList
     * @return
     */
    public Repository createRepository(Object value, Map<String, Object> map, List<Repository> repositoryList) {
        Repository r = new Repository();
        r.setId(IdUtil.getSnowflake(1, 1).nextIdStr());
        r.setCode(String.valueOf(value));
        r.setTitle(String.valueOf(map.get("所属题库名称")));
        if (!repositoryList.contains(r)) {
            repositoryList.add(r);
        }
        return r;
    }

    /**
     * 删除试题及试题选项
     *
     * @param questionId
     */
    @Override
    @Transactional
    public void removeQuestionAnswer(String questionId) {
        removeById(questionId);
        questionAnswerService.remove(questionId);
    }

    @Override
    @Transactional
    public void removeBatch(String ids) {
        removeByIds(Arrays.asList(ids.split(",")));
        questionAnswerService.removeByQuestionIds(ids.split(","));
    }

    /**
     * 下载导入模板
     *
     * @throws Exception
     */
    @Override
    public void downloadTemplate(HttpServletResponse response) throws Exception {
        response.reset();
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "*");
        response.setHeader("Access-Control-Allow-Headers", "*");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment; filename=question_template.xlsx");
        byte[] buff = new byte[1024];
        BufferedInputStream bis = null;
        OutputStream outputStream = null;
        InputStream inputStream = null;
        try {
            outputStream = response.getOutputStream();
            //得到输入流
            inputStream = ResourceUtil.getStream("excelTemplate/question_template.xlsx");
            bis = new BufferedInputStream(inputStream);
            int len;
            while ((len = bis.read(buff)) > 0) {
                outputStream.write(buff, 0, len);
                outputStream.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                bis.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    /**
     * 根据条件分页查询
     */
    @Override
    public List<Question> selectQuestionList(List<String> idList, boolean needAnswerFlag, boolean needAnalysis) {
        return questionMapper.selectQuestionList(idList, needAnswerFlag, needAnalysis);
    }

    @Override
    public List<QuestionDTO> groupLevelById(List<String> repositoryIds, Integer type, List<String> questionIds) {
        return questionMapper.groupLevelById(repositoryIds, type, questionIds);
    }

    @Override
    public List<Question> listByPaperId(String paperId) {
        return questionMapper.listByPaperId(paperId);
    }

    /**
     * 查询题库，对应的题型数量
     *
     * @param repositoryIds
     * @param type
     * @return
     */
    @Override
    public List<Question> groupCount(List<String> repositoryIds, Integer type) {
        return questionMapper.groupCount(repositoryIds, type);
    }

    /**
     * 根据题库，题型，难度的集合获取题目
     *
     * @param dtoList
     * @return
     */
    @Override
    public List<Question> listQuestionByCondition(List<QuestionConditionDTO> dtoList) {
        return questionMapper.listQuestionByCondition(dtoList);
    }

    /**
     * 查询用户的考试题目和答案（此时查询的答案可能是乱序的）
     *
     * @param needAnswerFlag 是否需要标记答案
     * @param needAnalysis   是否需要解析
     * @return
     */
    @Override
    public List<Question> listUserExamQuestionAnswer(String userExamId, List<String> objectiveList,
                                                     List<String> subjectiveList, boolean needAnswerFlag,
                                                     boolean needAnalysis, boolean needPathScore, boolean needSubjective) {
        return questionMapper.listUserExamQuestionAnswer(userExamId, objectiveList, subjectiveList, needAnswerFlag,
                needAnalysis, needPathScore, needSubjective);
    }

    /**
     * 根据条件分页查询
     */
    @Override
    public IPage<Question> selectPageList(IPage<Question> page, Question question) {
        return questionMapper.selectPageList(page, question);
    }

    /**
     * 统计题库题目数量
     *
     * @param repositoryIds
     * @return
     */
    @Override
    public List<QuestionDTO> countQuestionByRepository(List<String> repositoryIds) {
        return questionMapper.countQuestionByRepository(repositoryIds);
    }

    /**
     * 计算指定题库的题目题型数量
     *
     * @param repositoryId
     * @return
     */
    @Override
    public List<QuestionDTO> countQuestionNumByType(String repositoryId) {
        return questionMapper.countQuestionNumByType(repositoryId);
    }

    @Override
    public IPage<Question> listQuestionByPracticeMode(Integer pageNo, Integer pageSize, String repositoryId,
                                                      Integer questionType, Integer mode) {
        Page<Question> page = new Page<Question>(pageNo, pageSize);
        LambdaQueryWrapper<Question> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Question::getRepositoryId, repositoryId);

        IPage<Question> pageList = null;
        switch (PracticeMode.getByCode(mode)) {
            case SX://顺序练习
                pageList = page(page, queryWrapper);
                break;
            case SJ://随机练习
                pageList = page(page, queryWrapper);
                if (!CollectionUtils.isEmpty(pageList.getRecords())) {
                    Collections.shuffle(pageList.getRecords());
                }
                break;
            case TX://题型练习
                if (Objects.isNull(questionType)) {
                    throw new JeecgBootException("题型缺失异常");
                }
                queryWrapper.eq(Question::getType, questionType);
                pageList = page(page, queryWrapper);
                break;
        }

        //设置题目的答案
        setQuestionAnswer(pageList.getRecords());
        return pageList;
    }

    /**
     * 设置题目的答案
     *
     * @param questionList
     */
    @Override
    public void setQuestionAnswer(List<Question> questionList) {
        List<String> questionIds = questionList.stream().map(Question::getId).collect(Collectors.toList());

        //获取题目答案
        List<QuestionAnswer> answerList = questionAnswerService.listAnswerByQuestionIds(questionIds);
        if (!CollectionUtils.isEmpty(answerList)) {
            Map<String, List<QuestionAnswer>> answerMap = answerList.stream().collect(Collectors
                    .groupingBy(QuestionAnswer::getQuestionId, Collectors.toList()));
            questionList.forEach(item -> {
                List<QuestionAnswer> answers = answerMap.get(item.getId());
                if (!CollectionUtils.isEmpty(answers)) {
                    //答案排序
                    item.setAnswerList(answers.stream().sorted(Comparator.comparing(QuestionAnswer::getSort)).collect(Collectors.toList()));
                }
            });
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void castExcel(MultipartFile file) throws Exception {
        ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
        List<List<Object>> dataList = reader.read();
        if (!CollectionUtils.isEmpty(dataList)) {
            Iterator<List<Object>> iterator = dataList.iterator();
            List<Question> questionList = new ArrayList<>();
            while (iterator.hasNext()) {
                List<Object> objectList = iterator.next();
                String content = String.valueOf(objectList.get(0));
                char c = content.charAt(0);
                if (Character.isDigit(c)) {//字符串以数字开头（题目）
                    Question question = new Question();
                    String str = content.substring(content.indexOf("：") + 1, content.lastIndexOf("：")).replace("答案", "").trim();
                    question.setContent(str.startsWith("．") ? str.replace("．", "") : str);
                    question.setRemark(content.substring(content.lastIndexOf("：") + 1).trim());//正确答案
                    question.setType(QuestionType.RADIO.getCode());
                    question.setLevel(LevelType.YB.getCode());
                    question.setId(IdUtil.getSnowflake(1, 1).nextIdStr());
                    question.setAnswerList(new ArrayList<>());
                    questionList.add(question);
                } else if (Character.isLetter(c)) {//字符串以字母开头（答案）
                    Question relateQuestion = questionList.get(questionList.size() - 1);
                    List<QuestionAnswer> answerList = relateQuestion.getAnswerList();
                    QuestionAnswer answer = new QuestionAnswer();
                    answer.setId(IdUtil.getSnowflake(2, 2).nextIdStr());
                    answer.setQuestionId(relateQuestion.getId());
                    answer.setTag(String.valueOf(c));
                    answer.setContent(content.substring(content.indexOf("．") + 1).trim());
                    answer.setIsRight(relateQuestion.getRemark().equals(answer.getTag()));
                    answer.setSort(answerList.size() + 1);
                    answerList.add(answer);
                }
                iterator.remove();
            }
            saveBatch(questionList);
            List<QuestionAnswer> questionAnswerList = new ArrayList<>();
            questionList.forEach(question -> questionAnswerList.addAll(question.getAnswerList()));
            questionAnswerService.saveBatch(questionAnswerList);
        }
    }

    /**
     * 统计题库、题型的题目数量
     *
     * @param repositoryIds
     * @return
     */
    @Override
    public List<QuestionDTO> countQuestionType(List<String> repositoryIds) {
        return questionMapper.countQuestionType(repositoryIds);
    }
}