package com.zhongzheng.modules.bank.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.zhongzheng.common.constant.Constants;
import com.zhongzheng.common.core.domain.entity.SysUser;
import com.zhongzheng.common.core.redis.RedisCache;
import com.zhongzheng.common.exception.CustomException;
import com.zhongzheng.common.utils.DateUtils;
import com.zhongzheng.common.utils.ServletUtils;
import com.zhongzheng.modules.bank.bo.*;
import com.zhongzheng.modules.bank.domain.ExamQuestion;
import com.zhongzheng.modules.bank.domain.Question;
import com.zhongzheng.modules.bank.domain.QuestionBusiness;
import com.zhongzheng.modules.bank.domain.QuestionModuleChapter;
import com.zhongzheng.modules.bank.mapper.QuestionMapper;
import com.zhongzheng.modules.bank.service.IExamQuestionService;
import com.zhongzheng.modules.bank.service.IQuestionBusinessService;
import com.zhongzheng.modules.bank.service.IQuestionService;
import com.zhongzheng.modules.bank.vo.QuestionBusinessImport;
import com.zhongzheng.modules.bank.vo.QuestionImport;
import com.zhongzheng.modules.bank.vo.QuestionVo;
import com.zhongzheng.modules.base.domain.ApplyAreas;
import com.zhongzheng.modules.course.bo.CourseChapterSectionListAddBo;
import com.zhongzheng.modules.course.domain.*;
import com.zhongzheng.modules.course.service.*;
import com.zhongzheng.modules.exam.domain.ExamKnowledge;
import com.zhongzheng.modules.exam.service.IExamKnowledgeService;
import com.zhongzheng.modules.goods.bo.GoodsQueryBo;
import com.zhongzheng.modules.goods.vo.GoodsUserQuestionVo;
import io.micrometer.core.lang.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.Page;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * 题库题目Service业务层处理
 *
 * @author hjl
 * @date 2021-10-21
 */
@Service
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements IQuestionService {

    @Autowired
    private IQuestionBusinessService iQuestionBusinessService;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    private ICourseEducationTypeService iCourseEducationTypeService;

    @Autowired
    private ICourseProjectTypeService iCourseProjectTypeService;

    @Autowired
    private ICourseBusinessService iCourseBusinessService;

    @Autowired
    private ICourseSubjectService iCourseSubjectService;

    @Autowired
    private IExamKnowledgeService iExamKnowledgeService;

    @Autowired
    private IExamQuestionService iExamQuestionService;

    @Autowired
    private RedisCache redisCache;


    @Override
    public QuestionVo queryById(Long questionId){
        Question db = this.baseMapper.selectById(questionId);
        return BeanUtil.toBean(db, QuestionVo.class);
    }

    @Override
    public List<QuestionVo> selectList(QuestionQueryBo bo) {
        return questionMapper.selectList(bo);
    }



    @Override
    public List<QuestionVo> queryList(QuestionQueryBo bo) {
        LambdaQueryWrapper<Question> lqw = Wrappers.lambdaQuery();
        lqw.eq(StrUtil.isNotBlank(bo.getContent()), Question::getContent, bo.getContent());
        lqw.eq(bo.getAnswerQuestion() != null, Question::getAnswerQuestion, bo.getAnswerQuestion());
        lqw.in(bo.getStatus() != null, Question::getStatus, bo.getStatus());
        lqw.eq(StrUtil.isNotBlank(bo.getImgUrl()), Question::getImgUrl, bo.getImgUrl());
        lqw.eq(StrUtil.isNotBlank(bo.getAnalysisContent()), Question::getAnalysisContent, bo.getAnalysisContent());
        lqw.eq(StrUtil.isNotBlank(bo.getJsonStr()), Question::getJsonStr, bo.getJsonStr());
        lqw.like(StrUtil.isNotBlank(bo.getPrefixName()), Question::getPrefixName, bo.getPrefixName());
        lqw.eq(StrUtil.isNotBlank(bo.getKnowledgeIds()), Question::getKnowledgeIds, bo.getKnowledgeIds());
        lqw.eq(bo.getPublishStatus() != null, Question::getPublishStatus, bo.getPublishStatus());
        lqw.eq(StrUtil.isNotBlank(bo.getCode()), Question::getCode, bo.getCode());
        return entity2Vo(this.list(lqw));
    }

    /**
    * 实体类转化成视图对象
    *
    * @param collection 实体类集合
    * @return
    */
    private List<QuestionVo> entity2Vo(Collection<Question> collection) {
        List<QuestionVo> voList = collection.stream()
                .map(any -> BeanUtil.toBean(any, QuestionVo.class))
                .collect(Collectors.toList());
        if (collection instanceof Page) {
            Page<Question> page = (Page<Question>)collection;
            Page<QuestionVo> pageVo = new Page<>();
            BeanUtil.copyProperties(page,pageVo);
            pageVo.addAll(voList);
            voList = pageVo;
        }
        return voList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insertByAddBo(QuestionAddBo bo) {
        Question add = BeanUtil.toBean(bo, Question.class);
        add.setCode(ServletUtils.getEncoded("TM"));
        if(bo.getOptionsList()!=null){
            add.setJsonStr(JSON.toJSONString(bo.getOptionsList()));
        }
        validEntityBeforeSave(add);
        add.setCreateTime(DateUtils.getNowTime());
        add.setUpdateTime(DateUtils.getNowTime());
        boolean result = this.save(add);
        if(bo.getBusinessList()!=null){
            Collection<QuestionBusiness> coll = new HashSet<>();
            for(int i=0;i<bo.getBusinessList().size();i++){
                QuestionBusinessAddBo item = bo.getBusinessList().get(i);
                QuestionBusiness addItem = BeanUtil.toBean(item, QuestionBusiness.class);
                addItem.setMajorId(add.getQuestionId());
                addItem.setType(QuestionBusiness.TYPE_QUESTION);
                coll.add(addItem);
            }
            iQuestionBusinessService.saveBatch(coll);
        }
        return add.getQuestionId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByEditBo(QuestionEditBo bo) {
        Question update = BeanUtil.toBean(bo, Question.class);
        if(bo.getOptionsList()!=null){
            update.setJsonStr(JSON.toJSONString(bo.getOptionsList()));
        }
        validEntityBeforeSave(update);
        update.setUpdateTime(DateUtils.getNowTime());
        if(bo.getBusinessList()!=null){
            iQuestionBusinessService.remove(new LambdaQueryWrapper<QuestionBusiness>()
                    .eq(QuestionBusiness::getMajorId, bo.getQuestionId())
            .eq(QuestionBusiness::getType,QuestionBusiness.TYPE_QUESTION));
            Collection<QuestionBusiness> coll = new HashSet<>();
            for(int i=0;i<bo.getBusinessList().size();i++){
                QuestionBusinessEditBo item = bo.getBusinessList().get(i);
                QuestionBusiness addItem = BeanUtil.toBean(item, QuestionBusiness.class);
                addItem.setMajorId(bo.getQuestionId());
                addItem.setType(QuestionBusiness.TYPE_QUESTION);
                coll.add(addItem);
            }
            iQuestionBusinessService.saveBatch(coll);
        }
        return this.updateById(update);
    }

    /**
     * 保存前的数据校验
     *
     * @param entity 实体类数据
     */
    private void validEntityBeforeSave(Question entity){
        //TODO 做一些数据校验,如唯一约束
       /* if(checkNameUnique(entity)&&Validator.isEmpty(entity.getQuestionId())){
            throw new CustomException("名称重复");
        }*/
        if(entity.getQuestionId()!=null&&entity.getPublishStatus()!=null&&entity.getPublishStatus()==0){
            if(iExamQuestionService.getOne(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getQuestionId,entity.getQuestionId()).last("limit 1"))!=null){
                throw new CustomException("该题目已被绑定，不允许修改发布状态为未发布");
            }
        }
    }

    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return this.removeByIds(ids);
    }

    @Override
    public String importQuestion(List<QuestionImport> questionList, Boolean isUpdateSupport, String operName) {
        if (Validator.isNull(questionList) || questionList.size() == 0) {
            throw new CustomException("导入数据不能为空！");
        }

        String errorLog = "";
        String importNo = ServletUtils.getEncoded("IMPORT");
        int i=10;
        for (QuestionImport question : questionList) {
            QuestionAddBo bo = new QuestionAddBo();
            if(question.getKnowledge()!=null){
                List<String> knowledgeList = Arrays.asList(question.getKnowledge().split(","));
                List<Long> kIdList = new ArrayList<>();
                for (String knowledge : knowledgeList) {
                    Long knowledgeId = findKnowledgeId(knowledge);
                    if(!Validator.isNotEmpty(knowledgeId)){
                        errorLog+=knowledge+"-该知识点不存在\n";
                        continue;
                    }
                    kIdList.add(knowledgeId);
                }
                //知识点ID拼接
                bo.setKnowledgeIds(join(",", kIdList));
            }
            if(!Validator.isNotEmpty(question.getContent())){
                errorLog+="题目内容空白\n";
                continue;
            }
            Integer type = findType(question.getType());
            if(type==null){
                errorLog+=question.getType()+"-该题目类型错误\n";
                continue;
            }
           /* if(!Validator.isNotEmpty(question.getPrefixName())){
                errorLog+=question.getContent()+"-前缀名称空白\n";
                continue;
            }*/
            bo.setContent(question.getContent());
            bo.setType(type);
            bo.setStatus(1);
            bo.setPrefixName(question.getPrefixName());
            bo.setAnswerQuestion(question.getAnswerQuestion().replace(" ", ""));
            bo.setAnalysisContent(question.getAnalysisContent());
            //业务层次,导入每条题目只有一条业务层次
            List<QuestionBusinessAddBo> businessList = new ArrayList<>();
            QuestionBusinessAddBo questionBusinessAddBo = new QuestionBusinessAddBo();
            Long eduId = findEduId(question.getEducationType());
            if(!Validator.isNotEmpty(eduId)){
                errorLog+=question.getEducationType()+"-该业务层次不存在\n";
                continue;
            }
            Long projectId = findProjectId(question.getProject());
            if(!Validator.isNotEmpty(projectId)){
                errorLog+=question.getProject()+"-该项目类型不存在\n";
                continue;
            }
            Long businessId = findBusinessId(question.getBusiness());
            if(!Validator.isNotEmpty(businessId)){
                errorLog+=question.getBusiness()+"-该业务层次不存在\n";
                continue;
            }
            Long subjectId = findSubjectId(question.getSubject());
            if(!Validator.isNotEmpty(subjectId)){
                errorLog+=question.getSubject()+"-该科目不存在\n";
                continue;
            }
            questionBusinessAddBo.setEducationTypeId(eduId);
            questionBusinessAddBo.setProjectId(projectId);
            questionBusinessAddBo.setBusinessId(businessId);
            questionBusinessAddBo.setSubjectId(subjectId);
            businessList.add(questionBusinessAddBo);
            bo.setBusinessList(businessList);
            //选项列表
            bo.setOptionsList(dealOptionsList(question));
            bo.setCreateBy(operName);
            bo.setImportNo(importNo);
            bo.setPublishStatus(0);
            errorLog = insertByAddBoImport(bo,errorLog,i);
            i++;
        }
        return errorLog;
    }

    @Override
    public List<GoodsUserQuestionVo> listGoodsUserQuestionVo(GoodsQueryBo bo) {
        return baseMapper.listGoodsUserQuestionVo(bo);
    }

    @Transactional(rollbackFor = Exception.class)
    public String insertByAddBoImport(QuestionAddBo bo,String errorLog,Integer no) {
        Question add = BeanUtil.toBean(bo, Question.class);
        add.setCode(ServletUtils.getImportEncoded("TM")+no);
        if(bo.getOptionsList()!=null){
            add.setJsonStr(JSON.toJSONString(bo.getOptionsList()));
        }
        /*if(checkNameUnique(add)){
            errorLog+=bo.getContent()+"-名称重复\n";
           return errorLog;
        }*/
        add.setCreateTime(DateUtils.getNowTime());
        add.setUpdateTime(DateUtils.getNowTime());
        boolean result = this.save(add);
        if(bo.getBusinessList()!=null){
            Collection<QuestionBusiness> coll = new HashSet<>();
            for(int i=0;i<bo.getBusinessList().size();i++){
                QuestionBusinessAddBo item = bo.getBusinessList().get(i);
                QuestionBusiness addItem = BeanUtil.toBean(item, QuestionBusiness.class);
                addItem.setMajorId(add.getQuestionId());
                addItem.setType(QuestionBusiness.TYPE_QUESTION);
                coll.add(addItem);
            }
            iQuestionBusinessService.saveBatch(coll);
        }
        return errorLog;
    }

    private boolean checkNameUnique(Question entity) {
        Question info = getOne(new LambdaQueryWrapper<Question>()
                .eq(Question::getPrefixName,entity.getPrefixName()).eq(Question::getContent,entity.getContent()).ne(Question::getStatus,-1).last("limit 1"));
        if (Validator.isNotNull(info)) {
            if(Validator.isNotEmpty(entity.getQuestionId())){
                if(entity.getQuestionId() != info.getQuestionId()){
                    return true;
                }
            }else{
                return true;
            }
        }
        return false;
    }

    //合并选项列表
    public List<QuestionChildAddBo> dealOptionsList(QuestionImport question){
        List<QuestionChildAddBo> optionsList =new ArrayList<>();
        if(Validator.isNotEmpty(question.getV1())){
            QuestionChildAddBo bo1 = new QuestionChildAddBo();
            bo1.setOptionsId(1L);
            bo1.setContent(question.getV1());
            optionsList.add(bo1);
        }
        if(Validator.isNotEmpty(question.getV2())){
            QuestionChildAddBo bo2 = new QuestionChildAddBo();
            bo2.setOptionsId(2L);
            bo2.setContent(question.getV2());
            optionsList.add(bo2);
        }
        if(Validator.isNotEmpty(question.getV3())){
            QuestionChildAddBo bo3 = new QuestionChildAddBo();
            bo3.setOptionsId(3L);
            bo3.setContent(question.getV3());
            optionsList.add(bo3);
        }
        if(Validator.isNotEmpty(question.getV4())){
            QuestionChildAddBo bo4 = new QuestionChildAddBo();
            bo4.setOptionsId(4L);
            bo4.setContent(question.getV4());
            optionsList.add(bo4);
        }
        if(Validator.isNotEmpty(question.getV5())){
            QuestionChildAddBo bo5 = new QuestionChildAddBo();
            bo5.setOptionsId(5L);
            bo5.setContent(question.getV5());
            optionsList.add(bo5);
        }
        if(Validator.isNotEmpty(question.getV6())){
            QuestionChildAddBo bo6 = new QuestionChildAddBo();
            bo6.setOptionsId(6L);
            bo6.setContent(question.getV6());
            optionsList.add(bo6);
        }
        return optionsList;
    }

    public Long findEduId(String edu){
        if(edu!=null){
            String key = "EDU_"+edu;
            Long value = redisCache.getCacheObject(key);
            if(value!=null){
                if(value==0L){
                    return null;
                }
                return value;
            }
            CourseEducationType info = iCourseEducationTypeService.getOne(new LambdaQueryWrapper<CourseEducationType>()
                    .eq(CourseEducationType::getEducationName,edu));
            if(info!=null){
                redisCache.setCacheObject(key,info.getId(),3, TimeUnit.MINUTES);//3分钟
                return info.getId();
            }else{
                redisCache.setCacheObject(key,0L,3, TimeUnit.MINUTES);//3分钟
            }
        }
        return null;
    }

    public Long findProjectId(String project){
        if(project!=null){
            String key = "PROJ_"+project;
            Long value = redisCache.getCacheObject(key);
            if(value!=null){
                if(value==0L){
                    return null;
                }
                return value;
            }
            CourseProjectType info = iCourseProjectTypeService.getOne(new LambdaQueryWrapper<CourseProjectType>()
                    .eq(CourseProjectType::getProjectName,project).eq(CourseProjectType::getStatus,1).last("limit 1"));
            if(info!=null){
                redisCache.setCacheObject(key,info.getId(),3, TimeUnit.MINUTES);//3分钟
                return info.getId();
            }else{
                redisCache.setCacheObject(key,0L,3, TimeUnit.MINUTES);//3分钟
            }
        }
        return null;
    }

    public Long findBusinessId(String business){
        if(business!=null){
            String key = "BUS_"+business;
            Long value = redisCache.getCacheObject(key);
            if(value!=null){
                if(value==0L){
                    return null;
                }
                return value;
            }
            CourseBusiness info = iCourseBusinessService.getOne(new LambdaQueryWrapper<CourseBusiness>()
                    .eq(CourseBusiness::getBusinessName,business).eq(CourseBusiness::getStatus,1).last("limit 1"));
            if(info!=null){
                redisCache.setCacheObject(key,info.getId(),3, TimeUnit.MINUTES);//3分钟
                return info.getId();
            }else{
                redisCache.setCacheObject(key,0L,3, TimeUnit.MINUTES);//3分钟
            }
        }
        return null;
    }

    public Long findSubjectId(String subject){
        if(subject!=null){
            String key = "SUB_"+subject;
            Long value = redisCache.getCacheObject(key);
            if(value!=null){
                if(value==0L){
                    return null;
                }
                return value;
            }
            CourseSubject info = iCourseSubjectService.getOne(new LambdaQueryWrapper<CourseSubject>()
                    .eq(CourseSubject::getSubjectName,subject).eq(CourseSubject::getStatus,1).last("limit 1"));
            if(info!=null){
                redisCache.setCacheObject(key,info.getId(),3, TimeUnit.MINUTES);//3分钟
                return info.getId();
            }else{
                redisCache.setCacheObject(key,0L,3, TimeUnit.MINUTES);//3分钟
            }
        }
        return null;
    }

    public Long findKnowledgeId(String knowledge){

        if(knowledge!=null){
            String key = "KNO_"+knowledge;
            Long value = redisCache.getCacheObject(key);
            if(value!=null){
                if(value==0L){
                    return null;
                }
                return value;
            }
            ExamKnowledge info = iExamKnowledgeService.getOne(new LambdaQueryWrapper<ExamKnowledge>()
                    .eq(ExamKnowledge::getKnowledgeName,knowledge).eq(ExamKnowledge::getStatus,1).last("limit 1"));
            if(info!=null){
                redisCache.setCacheObject(key,info.getKnowledgeId(),3, TimeUnit.MINUTES);//3分钟
                return info.getKnowledgeId();
            }else{
                redisCache.setCacheObject(key,0L,3, TimeUnit.MINUTES);//3分钟
            }
        }
        return null;
    }

    public Integer findType(String type){
        if("单选题".equals(type)){
            return 1;
        }
        else if("多选题".equals(type)){
            return 2;
        }
        else if("判断题".equals(type)){
            return 3;
        }
        else if("简答题".equals(type)){
            return 4;
        }
        else if("案例题".equals(type)){
            return 5;
        }
        return null;
    }

    public  String join(@NonNull CharSequence delimiter, @NonNull Iterable tokens) {
        final Iterator<?> it = tokens.iterator();
        if (!it.hasNext()) {
            return "";
        }
        final StringBuilder sb = new StringBuilder();
        sb.append(it.next());
        while (it.hasNext()) {
            sb.append(delimiter);
            sb.append(it.next());
        }
        return sb.toString();
    }
}
