package com.artfess.examine.manager.impl;

import com.artfess.base.enums.PaperStatusEnum;
import com.artfess.base.enums.PaperTypeEnum;
import com.artfess.base.enums.PaperWayTypeEnum;
import com.artfess.base.enums.QuestionStateEnum;
import com.artfess.base.enums.QuestionTypeEnum;
import com.artfess.base.manager.impl.BaseManagerImpl;
import com.artfess.base.query.PageBean;
import com.artfess.base.query.PageList;
import com.artfess.base.query.QueryFilter;
import com.artfess.base.util.time.DateUtil;
import com.artfess.examine.dao.ExamPaperBaseDao;
import com.artfess.examine.dao.ExamPaperQuestionSettingDao;
import com.artfess.examine.dao.ExamPaperSettingDao;
import com.artfess.examine.dao.ExamQuestionsOptionDao;
import com.artfess.examine.dao.ExamSubjectInfoDao;
import com.artfess.examine.manager.ExamImitateRecordManager;
import com.artfess.examine.manager.ExamPaperBaseManager;
import com.artfess.examine.manager.ExamQuestionsInfoManager;
import com.artfess.examine.manager.ExamUserRecordDetailManager;
import com.artfess.examine.manager.ExamUserRecordManager;
import com.artfess.examine.model.ExamPaperBase;
import com.artfess.examine.model.ExamPaperQuestionSetting;
import com.artfess.examine.model.ExamPaperSetting;
import com.artfess.examine.model.ExamQuestionsInfo;
import com.artfess.examine.model.ExamQuestionsOption;
import com.artfess.examine.model.ExamSubjectInfo;
import com.artfess.examine.model.ExamUserRecord;
import com.artfess.examine.model.ExamUserRecordDetail;
import com.artfess.examine.vo.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 试卷基础信息 服务实现类
 *
 * @author min.wu
 * @company 阿特菲斯信息技术有限公司
 * @since 2022-10-19
 */
@Service
public class ExamPaperBaseManagerImpl extends BaseManagerImpl<ExamPaperBaseDao, ExamPaperBase> implements ExamPaperBaseManager {

    @Resource
    private ExamPaperSettingDao paperSettingDao;

    @Resource
    private ExamPaperQuestionSettingDao paperQuestionSettingDao;

    @Autowired
    private ExamQuestionsInfoManager questionsInfoManager;

    @Resource
    private ExamQuestionsOptionDao questionsOptionDao;

    @Autowired
    private ExamUserRecordManager userRecordManager;

    @Autowired
    private ExamUserRecordDetailManager userRecordDetailManager;

    @Resource
    private ExamSubjectInfoDao subjectInfoDao;

    @Resource
    private ExamImitateRecordManager imitateRecordManager;

    @Resource(name = "bmpExecutorService")
    private ExecutorService executorService;

    /**
     * 年度考核逻辑 todo 一次只能一个课目吧 ？ 年底进行计算 还是考一科就计算？
     * @param t
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createInfo(ExamPaperBase t) {
        if (PaperTypeEnum.formal.getType().equals(t.getType())) {
            Assert.notNull(t.getPaperType(), "请选择考核类型");
        }
        QueryWrapper<ExamSubjectInfo> queryWrapper = new QueryWrapper<>();
        List<String> subIds = Arrays.asList(t.getSubjectId().split(","));
        queryWrapper.in("id_", subIds);
        List<ExamSubjectInfo> examSubjectInfos = subjectInfoDao.selectList(queryWrapper);
        String subjectName = examSubjectInfos.stream()
                .map(ExamSubjectInfo::getName)
                .collect(Collectors.joining("," , "", ""));
        t.setSubjectName(subjectName);

        //保存试卷基础信息
        int insert = this.baseMapper.insert(t);
        if (insert < 0) {
            return null;
        }
        //试卷分数配置
        processScoreSetting(t);
        //试卷配置信息
        processSetting(t);

        if(PaperTypeEnum.simulation.getType().equals(t.getType())) {
            imitateRecordManager.createImitate(t);
        }else {
            executorService.execute(() -> {
                try {
                    processUserRecord(t);
                } catch (Exception e) {
                    log.error("创建考生记录失败:{}", e);
                }
            });

        }

        this.baseMapper.updateById(t);

        return t.getId();
    }

    private void processUserRecord(ExamPaperBase examPaperBase) {
        QueryWrapper<ExamUserRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("paper_id_", examPaperBase.getId());
        userRecordManager.getBaseMapper().delete(queryWrapper);

        examPaperBase.setTestUserType(examPaperBase.getPaperSetting().getTestUserType());

        List<UserInfoVo> userInfoVos = Lists.newArrayList();

        if("1".equals(examPaperBase.getTestUserType())) {
            userInfoVos = this.baseMapper.getAllUser();
        }else {
            userInfoVos = examPaperBase.getUserInfoVos();
        }

        if (CollectionUtils.isEmpty(userInfoVos)) {
            return;
        }

        //随机出卷每个考生试卷都是根据规则随机生成
        List<ExamUserRecord> records = Lists.newArrayList();
        userInfoVos.forEach(vo -> {

            ExamUserRecord examUserRecord = new ExamUserRecord();
            examUserRecord.setUserId(vo.getUserId());
            examUserRecord.setUserName(vo.getUserName());
            examUserRecord.setAccount(vo.getAccount());
            examUserRecord.setPositionId(vo.getPositionId());
            examUserRecord.setPaperId(examPaperBase.getId());
            examUserRecord.setStatus(QuestionStateEnum.notTo.getType());
            records.add(examUserRecord);

        });
        userRecordManager.saveBatch(records);

    }

    private void processSetting(ExamPaperBase t) {

        QueryWrapper<ExamPaperSetting> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("paper_id_", t.getId());
        this.paperSettingDao.delete(queryWrapper);
        if (PaperTypeEnum.formal.getType().equals(t.getType())) {
            Assert.notNull(t.getPaperSetting().getPlanStartTime(), "请选择考试开始时间");
            Assert.notNull(t.getPaperSetting().getPlanEndTime(), "请选择考试结束时间");
        }
        ExamPaperSetting paperSetting = t.getPaperSetting();
        Assert.notNull(t.getPaperSetting().getTimeLength(), "请填写考试时长");
        Assert.notNull(t.getTotalScore(), "请填写总分数");
        paperSetting.setPaperId(t.getId());
        paperSetting.setStatus(PaperStatusEnum.notRelease.getType());
        paperSetting.setPaperId(t.getId());
        paperSetting.setTrainModel(t.getTrainModel());
        paperSettingDao.insert(paperSetting);
    }

    private void processScoreSetting(ExamPaperBase t) {
        QueryWrapper<ExamPaperQuestionSetting> questionSettingQueryWrapper = new QueryWrapper<>();
        questionSettingQueryWrapper.eq("paper_base_id_", t.getId());
        this.paperQuestionSettingDao.delete(questionSettingQueryWrapper);
        Assert.notEmpty(t.getPaperQuestionSettings(), "请配置题目信息");
        AtomicReference<Double> totalScore = new AtomicReference<>(0.0);
        AtomicReference<Integer> totalNumber = new AtomicReference<>(0);
        AtomicReference<Integer> trainModel = new AtomicReference<>(1);
        t.getPaperQuestionSettings().forEach(question -> {
            if ("2".equals(t.getWayType())) {
                //固定试卷则需要传入题目id
                Assert.hasText(question.getQuestionId(), "请选择问题");
                question.setNumuber(1);
            }
            Assert.notNull(question.getScore(), "请填写分数");
            Assert.hasText(question.getQuestionType(), "请填写题目类型");
            question.setPaperBaseId(t.getId());
            question.setSubjectId(question.getSubjectId());
            totalNumber.updateAndGet(v -> v + question.getNumuber());
            totalScore.set(totalScore.get() + (question.getScore().doubleValue() * question.getNumuber()));
            this.paperQuestionSettingDao.insert(question);
            if (QuestionTypeEnum.radio.getType().equals(question.getQuestionType())
                    || QuestionTypeEnum.multi.getType().equals(question.getQuestionType())
                    || QuestionTypeEnum.judge.getType().equals(question.getQuestionType())) {
                trainModel.set(2);
            }
        });
        t.setTotalScore(new BigDecimal(totalScore.get()));
        t.setTotalNumber(totalNumber.get());
        t.setTrainModel(trainModel.get());

    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String updateInfo(ExamPaperBase t) {
        if (PaperTypeEnum.formal.getType().equals(t.getType())) {
            Assert.notNull(t.getPaperType(), "请选择考核类型");
        }

        ExamPaperBase examPaperBase = this.baseMapper.selectById(t.getId());
        Assert.notNull(examPaperBase, "试卷不存在");
        QueryWrapper<ExamPaperSetting> query = new QueryWrapper<>();
        query.eq("paper_id_", t.getId());
        ExamPaperSetting examPaperSetting = paperSettingDao.selectOne(query);
        Assert.notNull(examPaperSetting, "关联的试卷不存在");
        if(!"0".equals(examPaperSetting.getStatus())) {
            throw new RuntimeException("任务已发布，不能进行编辑");
        }
        QueryWrapper<ExamSubjectInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id_", Arrays.asList(t.getSubjectId().split(",")));
        List<ExamSubjectInfo> examSubjectInfos = subjectInfoDao.selectList(queryWrapper);
        String subjectName = examSubjectInfos.stream()
                .map(ExamSubjectInfo::getName)
                .collect(Collectors.joining("," , "", ""));
        t.setSubjectName(subjectName);
        //修改试卷基础信息
        int count = this.baseMapper.updateById(t);
        if (count < 0) {
            return null;
        }
        //试卷分数配置
        processScoreSetting(t);
        //试卷配置信息
        processSetting(t);

        if(PaperTypeEnum.simulation.getType().equals(t.getType())) {
            imitateRecordManager.createImitate(t);
        }else {
            executorService.execute(() -> {
                try {
                    processUserRecord(t);
                } catch (Exception e) {
                    log.error("创建考生记录失败:{}", e);
                }
            });
        }
        this.baseMapper.updateById(t);

        return t.getId();
    }

    @Override
    public ExamPaperBase findById(String id) {
        Assert.hasText(id, "请选择要查看的试卷");
        ExamPaperBase examPaperBase = this.baseMapper.selectById(id);

        QueryWrapper<ExamPaperSetting> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("paper_id_", id);
        ExamPaperSetting examPaperSetting = this.paperSettingDao.selectOne(queryWrapper);
        examPaperBase.setPaperSetting(examPaperSetting);

        QueryWrapper<ExamPaperQuestionSetting> questionSettingQueryWrapper = new QueryWrapper<>();
        questionSettingQueryWrapper.eq("paper_base_id_", id);
        List<ExamPaperQuestionSetting> examPaperQuestionSettings = this.paperQuestionSettingDao.selectList(questionSettingQueryWrapper);
        examPaperBase.setPaperQuestionSettings(examPaperQuestionSettings);

        if (PaperWayTypeEnum.pzcj.getType().equals(examPaperBase.getWayType())) {
            QueryWrapper<ExamQuestionsInfo> questionQuery = new QueryWrapper<>();
            List<String> questionIds = examPaperQuestionSettings.stream().map(ExamPaperQuestionSetting::getQuestionId).collect(Collectors.toList());
            questionQuery.in("id_", questionIds);
            List<ExamQuestionsInfo> questionsInfos = questionsInfoManager.getBaseMapper().selectList(questionQuery);

            Map<String, ExamQuestionsInfo> questionsInfoMap = questionsInfos.stream().collect(Collectors.toMap(item -> item.getId(), item -> item));

            QueryWrapper<ExamQuestionsOption> optionQueryWrapper = new QueryWrapper<>();
            optionQueryWrapper.in("question_id_", questionIds);
            optionQueryWrapper.orderByAsc("option_key_");
            List<ExamQuestionsOption> examQuestionsOptions = questionsOptionDao.selectList(optionQueryWrapper);

            Map<String, List<ExamQuestionsOption>> map = examQuestionsOptions.stream().collect(Collectors.groupingBy(ExamQuestionsOption::getQuestionId));

            examPaperQuestionSettings.forEach(examPaperQuestionSetting -> {
                ExamQuestionsInfo examQuestionsInfo = questionsInfoMap.get(examPaperQuestionSetting.getQuestionId());
                examQuestionsInfo.setOptions(map.get(examPaperQuestionSetting.getQuestionId()));
                examPaperQuestionSetting.setQuestionsInfo(examQuestionsInfo);
            });

        }

        //选择的人员信息
        List<UserInfoVo> userInfoVoList = userRecordManager.findByPaperId(id);
        examPaperBase.setUserInfoVos(userInfoVoList);

        QueryWrapper<ExamSubjectInfo> subjectInfoQueryWrapper = new QueryWrapper<>();
        subjectInfoQueryWrapper.in("id_", Arrays.asList(examPaperBase.getSubjectId().split(",")));
        List<ExamSubjectInfo> examSubjectInfos = subjectInfoDao.selectList(subjectInfoQueryWrapper);
        examPaperBase.setSubjectInfos(examSubjectInfos);
        return examPaperBase;
    }

    @Override
    public ExamPaperBase viewPaper(String id) {
        Assert.hasText(id, "请选择要预览的试卷");
        ExamPaperBase examPaperBase = this.baseMapper.selectById(id);
        Assert.notNull(examPaperBase, "试卷信息不存在");
        List<ExamQuestionsInfo> questionList = processQuestionList(examPaperBase);
        QueryWrapper<ExamPaperSetting> query = new QueryWrapper<>();
        query.eq("paper_id_", id);
        ExamPaperSetting examPaperSetting = paperSettingDao.selectOne(query);
        Assert.notNull(examPaperSetting, "关联的试卷不存在");
        examPaperBase.setTimeLength(examPaperSetting.getTimeLength());
        examPaperBase.setExamQuestionsInfos(questionList);
        return examPaperBase;
    }

    @Override
    public List<ExamQuestionsInfo> processQuestionList(ExamPaperBase examPaperBase) {
        //获取试卷组卷信息
        QueryWrapper<ExamPaperQuestionSetting> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("paper_base_id_", examPaperBase.getId());
        queryWrapper.orderByAsc("sn_");
        List<ExamPaperQuestionSetting> settingList = this.paperQuestionSettingDao.selectList(queryWrapper);

        //获取所有题目信息
        List<ExamQuestionsInfo> questionList = Lists.newArrayList();
        List<String> questionIds = Lists.newArrayList();
        if (PaperWayTypeEnum.pzcj.getType().equals(examPaperBase.getWayType())) {
            questionIds = settingList.stream().map(ExamPaperQuestionSetting::getQuestionId).collect(Collectors.toList());
            Map<String, ExamPaperQuestionSetting> settingMap = settingList.stream().collect(Collectors.toMap(item -> item.getQuestionId(), item -> item));
            QueryWrapper<ExamQuestionsInfo> questionQuery = new QueryWrapper<>();
            questionQuery.in("id_", questionIds);
            List<ExamQuestionsInfo> examQuestionsInfos = questionsInfoManager.getBaseMapper().selectList(questionQuery);
            examQuestionsInfos.forEach(question -> {
                question.setScore(settingMap.get(question.getId()).getScore());
            });
            questionList.addAll(examQuestionsInfos);
        } else {
            settingList.forEach(setting -> {
                List<ExamQuestionsInfo> examQuestionsInfos = questionsInfoManager.randomQuestion(setting.getSubjectId(), setting.getQuestionType(), setting.getNumuber());
                examQuestionsInfos.forEach(question -> {
                    question.setScore(setting.getScore());
                });
                questionList.addAll(examQuestionsInfos);
            });
            questionIds = questionList.stream().map(ExamQuestionsInfo::getId).collect(Collectors.toList());
        }

        if(CollectionUtils.isEmpty(questionIds)) {
            return Lists.newArrayList();
        }
        QueryWrapper<ExamQuestionsOption> optionQueryWrapper = new QueryWrapper<>();
        optionQueryWrapper.in("question_id_", questionIds);
        optionQueryWrapper.orderByAsc("option_key_");
        List<ExamQuestionsOption> examQuestionsOptions = questionsOptionDao.selectList(optionQueryWrapper);

        Map<String, List<ExamQuestionsOption>> map = examQuestionsOptions.stream().collect(Collectors.groupingBy(ExamQuestionsOption::getQuestionId));
        questionList.forEach(question -> {
            if (!map.containsKey(question.getId())) {
                return;
            }
            question.setOptions(map.get(question.getId()));
        });
        questionList.sort((o1, o2) -> o1.getType().compareTo(o2.getType()));

        return questionList;
    }

    @Override
    public void startPaper(String id) {
        Assert.hasText(id, "请选择要发布的考试");
        ExamPaperBase examPaperBase = this.baseMapper.selectById(id);
        QueryWrapper<ExamPaperSetting> query = new QueryWrapper<>();
        query.eq("paper_id_", id);
        ExamPaperSetting examPaperSetting = paperSettingDao.selectOne(query);
        Assert.notNull(examPaperSetting, "关联的试卷不存在");
        if(!"0".equals(examPaperSetting.getStatus())) {
            throw new RuntimeException("试卷已发布，不能再次发布");
        }
        //时间验证逻辑
        boolean dateLittle = DateUtil.isDateLittle(LocalDateTime.now(), examPaperSetting.getPlanStartTime());
        if (dateLittle) {
            throw new RuntimeException("未到试卷开始时间，不能进行发布！");
        }

        boolean dateEquals = DateUtil.isDateLittle(LocalDateTime.now(), examPaperSetting.getPlanEndTime());
        if(!dateEquals) {
            throw new RuntimeException("试卷结束时间已过，不能进行发布！");
        }
        examPaperSetting.setStatus("1");

        this.paperSettingDao.updateById(examPaperSetting);

        QueryWrapper<ExamUserRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("paper_id_", id);
        List<ExamUserRecord> records = userRecordManager.getBaseMapper().selectList(queryWrapper);
        executorService.execute(() -> {
            try {
                //发布成功生成试卷
                processUserRecordDetail(records, examPaperBase);
            } catch (Exception e) {
                log.error("创建试卷失败:{}", e);
            }
        });

    }

    @Override
    public void addUser(ExamPaperBase examPaperBase) {
        if (CollectionUtils.isEmpty(examPaperBase.getUserInfoVos())) {
            return;
        }

        //随机出卷每个考生试卷都是根据规则随机生成
        List<ExamUserRecord> records = Lists.newArrayList();
        examPaperBase.getUserInfoVos().forEach(vo -> {
            QueryWrapper<ExamUserRecord> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id_", vo.getUserId());
            queryWrapper.eq("paper_id_", examPaperBase.getId());
            List<ExamUserRecord> list = userRecordManager.getBaseMapper().selectList(queryWrapper);
            Assert.isTrue(CollectionUtils.isEmpty(list), "");
            if(!CollectionUtils.isEmpty(list)) {
                throw new RuntimeException(vo.getUserName() + "已加入试卷中，不能重复添加");
            }
            ExamUserRecord examUserRecord = new ExamUserRecord();
            examUserRecord.setUserId(vo.getUserId());
            examUserRecord.setPositionId(vo.getPositionId());
            examUserRecord.setAccount(vo.getAccount());
            examUserRecord.setUserName(vo.getUserName());
            examUserRecord.setPaperId(examPaperBase.getId());
            examUserRecord.setStatus(QuestionStateEnum.toBeAnswer.getType());
            records.add(examUserRecord);

        });
        userRecordManager.saveBatch(records);

        processUserRecordDetail(records, examPaperBase);
    }

    @Override
    public PageList<ExamPaperBase> findByPage(QueryFilter<ExamPaperBase> queryFilter) {
        PageBean pageBean = queryFilter.getPageBean();
        IPage<ExamPaperBase> result = baseMapper.findByPage(convert2IPage(pageBean), convert2Wrapper(queryFilter, currentModelClass()));

        result.getRecords().forEach(paperBase -> {
            boolean dateEquals = DateUtil.isDateLittle(LocalDateTime.now(), paperBase.getPlanEndTime());
            if (dateEquals && !PaperStatusEnum.notRelease.getType().equals(paperBase.getStatus())) {
                paperBase.setStatus(PaperStatusEnum.inTets.getType());
            } else if(!dateEquals && !PaperStatusEnum.notRelease.getType().equals(paperBase.getStatus())){
                paperBase.setStatus(PaperStatusEnum.finish.getType());
            }

        });
        return new PageList<ExamPaperBase>(result);
    }

    @Override
    public Long getEndStatus(String subjectId) {
        ExamPaperBase examPaperBase = new ExamPaperBase();
        examPaperBase.setSubjectId(subjectId);
        Long count = this.baseMapper.getYearPaperCount(examPaperBase);
        return count;
    }

    private void processUserRecordDetail(List<ExamUserRecord> records, ExamPaperBase examPaperBase) {
        //随机出卷每个考生试卷都是根据规则随机生成
        if(PaperWayTypeEnum.sjcj.getType().equals(examPaperBase.getWayType())) {
            records.forEach(record -> {
                record.setStatus(QuestionStateEnum.toBeAnswer.getType());
                List<ExamQuestionsInfo> examQuestionsInfos = this.processQuestionList(examPaperBase);
                createUserRecordDetail(record, examQuestionsInfos, examPaperBase);
            });

        } else {
            List<ExamQuestionsInfo> examQuestionsInfos = this.processQuestionList(examPaperBase);

            records.forEach(record -> {
                record.setStatus(QuestionStateEnum.toBeAnswer.getType());
                createUserRecordDetail(record, examQuestionsInfos, examPaperBase);
            });
        }

        userRecordManager.updateBatchById(records);

    }

    private void createUserRecordDetail(ExamUserRecord record, List<ExamQuestionsInfo> examQuestionsInfos, ExamPaperBase examPaperBase) {
        //保存考生试卷信息
        //保存考生题目信息
        List<ExamUserRecordDetail> details = Lists.newArrayList();
        examQuestionsInfos.forEach(question->{
            ExamUserRecordDetail userRecordDetail = new ExamUserRecordDetail();
            userRecordDetail.setRecordId(record.getId());
            userRecordDetail.setQuestionId(question.getId());
            userRecordDetail.setQuestionType(question.getType());
            userRecordDetail.setRightKey(question.getRightOption());
            userRecordDetail.setScore(question.getScore());
            details.add(userRecordDetail);
        });

        if(!CollectionUtils.isEmpty(details)) {
            userRecordDetailManager.saveBatch(details);
        }
    }

}
