package com.ruoyi.school.paper.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.dynamic.datasource.annotation.Slave;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.school.paper.domain.*;
import com.ruoyi.school.paper.domain.bo.DbTestPaperBo;
import com.ruoyi.school.paper.domain.vo.DbTestPaperVo;
import com.ruoyi.school.paper.mapper.DbQuestionBankAnswerMapper;
import com.ruoyi.school.paper.mapper.DbQuestionBankAnswerTestPaperMapper;
import com.ruoyi.school.paper.mapper.DbQuestionBankTestPaperMapper;
import com.ruoyi.school.paper.mapper.DbTestPaperMapper;
import com.ruoyi.school.paper.service.IDbTestPaperService;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 试卷Service业务层处理
 *
 * @author zyf
 * @date 2023-07-20
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class DbTestPaperServiceImpl implements IDbTestPaperService {

    private final DbTestPaperMapper baseMapper;
    private final DbQuestionBankTestPaperMapper questionBankTestPaperMapper;
    private final DbQuestionBankAnswerMapper questionBankAnswerMapper;
    private final DbQuestionBankAnswerTestPaperMapper questionBankAnswerTestPaperMapper;
    private final ISysUserService userService;
    private final ISysDeptService deptService;
    private String DEFAULT_QUESTIOBN_BANK_KEY = "question_bank:default_question_bank";

    /**
     * 查询试卷列表
     */
    @Override
    public TableDataInfo<DbTestPaperVo> queryPageList(DbTestPaperBo bo, PageQuery pageQuery) {
        Long userId = LoginHelper.getUserId();
        SysUser sysUser = userService.selectUserById(userId);
        if (sysUser.getSex().equals("0")) {
            List<String> colList = CollUtil.newArrayList(sysUser.getDeptList().split(","));
            List<SysDept> sysDepts = deptService.selectDeptListByList(colList.stream().mapToLong(t -> Long.parseLong(t.trim())).boxed().collect(Collectors.toList()));
            SysDept dept = sysDepts.stream().filter(depts -> !depts.getParentId().equals(0L)).findAny().orElse(null);
            if (dept != null) {
                bo.setPhaseId(dept.getParentId());
            }
            if (dept != null) {
                bo.setClassId(dept.getDeptId());
            }
        }
        LambdaQueryWrapper<DbTestPaper> lqw = buildQueryWrapper(bo);
        Page<DbTestPaperVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        result.getRecords().forEach(vo -> {
            SysDept phase = deptService.selectDeptById(vo.getPhaseId());
            SysDept clas = deptService.selectDeptById(vo.getClassId());
            vo.setPhaseClass(phase.getDeptName() + clas.getDeptName());
        });
        return TableDataInfo.build(result);
    }

    /**
     * 查询试卷列表
     */
    @Override
    public List<DbTestPaperVo> queryList(DbTestPaperBo bo) {
        LambdaQueryWrapper<DbTestPaper> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<DbTestPaper> buildQueryWrapper(DbTestPaperBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<DbTestPaper> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getTestName()), DbTestPaper::getTestName, bo.getTestName());
        lqw.eq(bo.getCategoryId() != null, DbTestPaper::getCategoryId, bo.getCategoryId());
        lqw.eq(bo.getPhaseId() != null, DbTestPaper::getPhaseId, bo.getPhaseId());
        lqw.eq(bo.getClassId() != null, DbTestPaper::getClassId, bo.getClassId());
        lqw.eq(bo.getTotalPoints() != null, DbTestPaper::getTotalPoints, bo.getTotalPoints());
        lqw.eq(bo.getStatus() != null, DbTestPaper::getStatus, bo.getStatus());
        lqw.last(StringUtils.isNotEmpty(bo.getIntensity()), "and intensity like '%" + bo.getIntensity() + "'");
        return lqw;
    }

    /**
     * 新增试卷
     */
    @Override
    public Boolean insertByBo(DbTestPaperBo bo) {
        DbTestPaper add = BeanUtil.toBean(bo, DbTestPaper.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改试卷
     */
    @Override
    public Boolean updateByBo(DbTestPaperBo bo) {
        DbTestPaper update = BeanUtil.toBean(bo, DbTestPaper.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 查询试卷
     */
    @Override
    public DbTestPaperVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(DbTestPaper entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除试卷
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 随机生成试卷
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean randomGeneration(DbTestPaperBo bo) {
        DbTestPaper add = BeanUtil.toBean(bo, DbTestPaper.class);
        List<String> stringList = bo.getIntensitys().stream().map(String::valueOf).collect(Collectors.toList());
        add.setIntensity(String.join(",", stringList));
        // 根据班级id获取阶段信息
        SysDept phase = deptService.getParent(bo.getClassId());
        add.setPhaseId(phase.getDeptId());
        bo.setPhaseId(phase.getDeptId());
        baseMapper.insert(add);
        // 从redis中获取所有题目
        List<DbQuestionBank> dbQuestionBanks = JsonUtils.parseArray(RedisUtils.getCacheObject(DEFAULT_QUESTIOBN_BANK_KEY), DbQuestionBank.class);
        // 筛选符合条件的题目
        Map<Integer, List<DbQuestionBank>> collect = dbQuestionBanks.stream()
            .filter(bank -> bank.getCategoryId().equals(bo.getCategoryId())
                && bank.getPhaseId().equals(bo.getPhaseId())
                && bo.getIntensitys().contains(bank.getIntensity())
            ).collect(Collectors.groupingBy(DbQuestionBank::getSpecies));
        if (CollectionUtil.isEmpty(collect)) {
            throw new ServiceException("符合条件的题目数量不足");
        }
        // 根据习题种类分组
        // 选择题
        List<DbQuestionBank> select = collect.get(2);
        // 填空题
        List<DbQuestionBank> gapFilling = collect.get(1);
        // 解答题
        List<DbQuestionBank> resolve = collect.get(3);
        // 随机筛选题目
        List<DbQuestionBankTestPaper> papers = new ArrayList<>();
        List<DbQuestionBankTestPaper> selectList = null;
        if (bo.getTotalPoints().equals(100)) {
            if (CollectionUtil.isEmpty(select) || select.size() < 4) {
                throw new ServiceException("符合条件的题目数量不足");
            }
            if (CollectionUtil.isEmpty(gapFilling) || gapFilling.size() < 10) {
                throw new ServiceException("符合条件的题目数量不足");
            }
            if (CollectionUtil.isEmpty(resolve) || resolve.size() < 2) {
                throw new ServiceException("符合条件的题目数量不足");
            }
            selectList = generatePaper(select, 4, bo.getTotalPoints(), add.getId());
            List<DbQuestionBankTestPaper> gapFillingList = generatePaper(gapFilling, 10, bo.getTotalPoints(), add.getId());
            List<DbQuestionBankTestPaper> resolveList = generatePaper(resolve, 2, bo.getTotalPoints(), add.getId());
            papers.addAll(selectList);
            papers.addAll(gapFillingList);
            papers.addAll(resolveList);
        } else {
            if (CollectionUtil.isEmpty(select) || select.size() < 4) {
                throw new ServiceException("符合条件的题目数量不足");
            }
            if (CollectionUtil.isEmpty(gapFilling) || gapFilling.size() < 12) {
                throw new ServiceException("符合条件的题目数量不足");
            }
            if (CollectionUtil.isEmpty(resolve) || resolve.size() < 5) {
                throw new ServiceException("符合条件的题目数量不足");
            }
            selectList = generatePaper(select, 4, bo.getTotalPoints(), add.getId());
            List<DbQuestionBankTestPaper> gapFillingList = generatePaper(gapFilling, 12, bo.getTotalPoints(), add.getId());
            List<DbQuestionBankTestPaper> resolveList = generatePaper(resolve, 5, bo.getTotalPoints(), add.getId());
            papers.addAll(selectList);
            papers.addAll(gapFillingList);
            papers.addAll(resolveList);
        }
        questionBankTestPaperMapper.insertBatch(papers);
        this.saveSelectListOptions(add.getId());
        return true;
    }

    /**
     * 保存选择题的选项
     *
     * @param paperId 试卷id
     */
    private void saveSelectListOptions(Long paperId) {
        List<DbQuestionBankTestPaper> papers = questionBankTestPaperMapper.selectList(Wrappers.<DbQuestionBankTestPaper>lambdaQuery()
            .eq(DbQuestionBankTestPaper::getTestPaperId, paperId)
            .eq(DbQuestionBankTestPaper::getSpecies, 2));
        papers.forEach(questionBank -> {
            List<DbQuestionBankAnswer> dbQuestionBankAnswers = questionBankAnswerMapper.selectList(Wrappers.<DbQuestionBankAnswer>lambdaQuery()
                .select(DbQuestionBankAnswer::getOptionContent, DbQuestionBankAnswer::getOptionTag)
                .eq(DbQuestionBankAnswer::getQuestionBankId, questionBank.getQuestionBankId())
                .eq(DbQuestionBankAnswer::getStatus, 0)
            );
            dbQuestionBankAnswers.forEach(questionBankAnswer -> {
                questionBankAnswer.setQuestionBankId(questionBank.getId());
            });
            List<DbQuestionBankAnswerTestPaper> answerTestPapers = BeanUtil.copyToList(dbQuestionBankAnswers, DbQuestionBankAnswerTestPaper.class);
            questionBankAnswerTestPaperMapper.insertBatch(answerTestPapers);
        });
    }

    /**
     * 随机筛选题目
     *
     * @param list        习题列表
     * @param num         习题数量
     * @param totalPoints 总分
     * @return
     */
    private List<DbQuestionBankTestPaper> generatePaper(List<DbQuestionBank> list, int num, Integer totalPoints, Long paperId) {
        log.info("系统正在随机抽取题目生成试卷，这可能需要一段时间，请耐心等候···");
        List<DbQuestionBankTestPaper> paper = new ArrayList<>();//用来存储题目，set集合去重复
        //这个while循环用来生成不重复的一套题目
        while (paper.size() != num) {
            DbQuestionBank question = list.get(new Random().nextInt(list.size()));
            DbQuestionBankTestPaper questionBankTestPaper = BeanUtil.copyProperties(question, DbQuestionBankTestPaper.class);
            if (totalPoints.equals(100)) {
                switch (questionBankTestPaper.getSpecies()) {
                    case 1:
                    case 2:
                        questionBankTestPaper.setScore(5);
                        break;
                    case 3:
                        questionBankTestPaper.setScore(15);
                        break;
                }
            } else {
                switch (questionBankTestPaper.getSpecies()) {
                    case 1:
                        if (paper.size() <= 6) {
                            questionBankTestPaper.setScore(4);
                        } else {
                            questionBankTestPaper.setScore(5);
                        }
                        break;
                    case 2:
                        if (paper.size() <= 2) {
                            questionBankTestPaper.setScore(4);
                        } else {
                            questionBankTestPaper.setScore(5);
                        }
                        break;
                    case 3:
                        if (paper.size() <= 3) {
                            questionBankTestPaper.setScore(14);
                        } else {
                            questionBankTestPaper.setScore(18);
                        }
                        break;
                }
            }
            questionBankTestPaper.setId(null);
            questionBankTestPaper.setTestPaperId(paperId);
            questionBankTestPaper.setQuestionBankId(question.getId());
            paper.add(questionBankTestPaper);
            paper = paper.stream()
                .collect(Collectors.collectingAndThen(Collectors.toCollection(() ->
                    new TreeSet<>(Comparator.comparing(DbQuestionBankTestPaper::getQuestionBankId))), ArrayList::new));
        }
        return paper;
    }
}
