package com.qxgmat.service.extend;

import com.alibaba.fastjson.JSONObject;
import com.nuliji.tools.Transform;
import com.nuliji.tools.exception.ParameterException;
import com.qxgmat.data.constants.enums.SettingKey;
import com.qxgmat.data.constants.enums.module.PaperModule;
import com.qxgmat.data.constants.enums.module.QuestionModule;
import com.qxgmat.data.dao.entity.*;
import com.qxgmat.data.relation.entity.QuestionNoRelation;
import com.qxgmat.data.relation.entity.UserReportRelation;
import com.qxgmat.service.*;
import com.qxgmat.service.annotation.*;
import com.qxgmat.service.inline.QuestionNoService;
import com.qxgmat.service.inline.QuestionService;
import com.qxgmat.service.inline.SentenceQuestionService;
import com.qxgmat.service.inline.UserReportService;
import com.qxgmat.util.annotation.Callback;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class QuestionFlowService {

    @Resource
    private ExercisePaperService exercisePaperService;

    @Resource
    private HomeworkPreviewService homeworkPreviewService;

    @Resource
    private ExaminationPaperService examinationPaperService;

    @Resource
    private QuestionNoService questionNoService;

    @Resource
    private QuestionService questionService;

    @Resource
    private SentenceQuestionService sentenceQuestionService;

    @Resource
    private SentencePaperService sentencePaperService;

    @Resource
    private UsersService usersService;

    @Resource
    private UserReportService userReportService;

    @Resource
    private UserPaperService userPaperService;

    @Resource
    private UserQuestionService userQuestionService;

    @Resource
    private ToolsService toolsService;

    // 初始化试卷的callback，根据试卷模块进行后续处理
    private Map<PaperModule, InitPaper> initPaperCallback = new HashMap<>();

    // 初始化报告的callback，根据试卷模块进行后续处理
    private Map<PaperModule, InitReport> initReportCallback = new HashMap<>();

    // 初始化题目，根据试卷模块进行选题处理
    private Map<PaperModule, InitQuestion> nextCallback = new HashMap<>();

    // 提交答题的callback，根据试题模块正确判断
    private Map<QuestionModule, SubmitQuestion> submitCallback = new HashMap<>();

    // 提交答题的callback，根据试题模块后续统计，会过滤满足剔除条件
    private Map<QuestionModule, SubmitAfterQuestion> submitAfterCallback = new HashMap<>();

    // 完成试卷的callback，根据试卷模块进行后续处理
    private Map<PaperModule, StatReport> finishCallback = new HashMap<>();

    public QuestionFlowService(){
        initPaperCallback.put(PaperModule.EXERCISE, (paper, id)->{
           exercisePaperService.initUserPaper(paper, id);
            // 获取考题时间
            List<QuestionNoRelation> relationList = questionNoService.listWithRelationByIds(paper.getQuestionNoIds());
            Integer time = toolsService.computerTime(SettingKey.EXERCISE_TIME, relationList);
            paper.setTime(time);
        });
        initPaperCallback.put(PaperModule.HOMEWORK_PREVIEW, (paper, id)->{
            // 后台主动修改，无需变更
        });
        initPaperCallback.put(PaperModule.ERROR, (paper, id)->{
            // 用户主动添加，无需变更
        });
        initPaperCallback.put(PaperModule.COLLECT, (paper, id)->{
            // 用户主动添加，无需变更
        });
        initPaperCallback.put(PaperModule.SENTENCE, (paper, id)->{
            sentencePaperService.initUserPaper(paper, id);
        });

        initReportCallback.put(PaperModule.EXERCISE, (report, paper)->{
            JSONObject setting = report.getSetting();
            if (setting.getBoolean("disorder")){
                // 随机试题
                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
            }
        });
        initReportCallback.put(PaperModule.HOMEWORK_PREVIEW, (report, paper)->{
            JSONObject setting = report.getSetting();
            if (setting.getBoolean("disorder")){
                // 随机试题
                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
            }
        });
        initReportCallback.put(PaperModule.COLLECT, (report, paper)->{
            JSONObject setting = report.getSetting();
            if (setting.getBoolean("disorder")){
                // 随机试题
                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
            }
        });
        initReportCallback.put(PaperModule.ERROR, (report, paper)->{
            JSONObject setting = report.getSetting();
            if (setting.getBoolean("disorder")){
                // 随机试题
                report.setQuestionNoIds(this.randomQuestionNoIds(paper.getQuestionNoIds()));
            }
        });
        initReportCallback.put(PaperModule.SENTENCE, (paper, report)->{
            // 无特殊设置
        });

        nextCallback.put(PaperModule.EXERCISE, (question, report, lastQuestion)->{
            Integer questionNoId = this.nextId(report.getQuestionNoIds(), lastQuestion!=null ? lastQuestion.getQuestionNoId():null);
            if (questionNoId == 0) return false;
            this.bindQuestionNo(question, questionNoId, SettingKey.EXERCISE_TIME);
            // 设定最后一次练习记录
            usersService.edit(User.builder().id(question.getUserId()).latestExercise(report.getPaperId()).build());
            return true;
        });
        nextCallback.put(PaperModule.HOMEWORK_PREVIEW, (question, report, lastQuestion)->{
            Integer questionNoId = this.nextId(report.getQuestionNoIds(), lastQuestion!=null ? lastQuestion.getQuestionNoId():null);
            if (questionNoId == 0) return false;
            this.bindQuestionNo(question, questionNoId, SettingKey.EXERCISE_TIME);
            return true;
        });
        nextCallback.put(PaperModule.COLLECT, (question, report, lastQuestion)->{
            Integer questionNoId = this.nextId(report.getQuestionNoIds(), lastQuestion!=null ? lastQuestion.getQuestionNoId():null);
            if (questionNoId == 0) return false;
            // 根据扩展确定exercise还是examination
            UserPaper paper = userPaperService.get(report.getPaperId());
            this.bindQuestionNo(question, questionNoId, SettingKey.getTimeByPaperModule(PaperModule.ValueOf(paper.getModuleExtend())));
            return true;
        });
        nextCallback.put(PaperModule.ERROR, (question, report, lastQuestion)->{
            Integer questionNoId = this.nextId(report.getQuestionNoIds(), lastQuestion!=null ? lastQuestion.getQuestionNoId():null);
            if (questionNoId == 0) return false;
            // 根据扩展确定exercise还是examination
            UserPaper paper = userPaperService.get(report.getPaperId());
            this.bindQuestionNo(question, questionNoId, SettingKey.getTimeByPaperModule(PaperModule.ValueOf(paper.getModuleExtend())));
            // 设定最后一次错题记录
            usersService.edit(User.builder().id(question.getUserId()).latestError(report.getPaperId()).build());
            return true;
        });
        nextCallback.put(PaperModule.SENTENCE, (question, report, lastQuestion)->{
            Integer questionNoId = this.nextId(report.getQuestionNoIds(), lastQuestion!=null ? lastQuestion.getQuestionNoId():null);
            if (questionNoId == 0) return false;
            SentenceQuestion relation = sentenceQuestionService.get(questionNoId);
            question.setQuestionNoId(relation.getId());
            question.setQuestionId(relation.getQuestionId());
            question.setTime(0);
            return true;
        });

        submitCallback.put(QuestionModule.BASE, (userQuestion)->{
            // 判断答题情况
            JSONObject answer = userQuestion.getUserAnswer();
            Question question = questionService.get(userQuestion.getQuestionId());

            return true;
        });
        submitCallback.put(QuestionModule.SENTENCE, (userQuestion)->{
            // 判断答题情况
            JSONObject answer = userQuestion.getUserAnswer();
            Question question = questionService.get(userQuestion.getQuestionId());

            return true;
        });

        submitAfterCallback.put(QuestionModule.BASE, (userQuestion)->{
            // 更新题目及题目编号统计
            questionNoService.accumulation(userQuestion);
            questionService.accumulation(userQuestion);
        });
        submitAfterCallback.put(QuestionModule.SENTENCE, (userQuestion)->{
            // 更新题目及题目编号统计
            questionService.accumulation(userQuestion);
            sentenceQuestionService.accumulation(userQuestion);
        });

        finishCallback.put(PaperModule.EXERCISE, (report, questionList)->{
            // report
        });
        finishCallback.put(PaperModule.HOMEWORK_PREVIEW, (report, questionList)->{

        });
        finishCallback.put(PaperModule.EXAMINATION, (report, questionList)->{

        });
    }


    /**
     * 开始新一轮做题
     * @param userId
     * @param module
     * @param paperId
     * @param setting
     * @return
     */
    @Transactional
    public UserReportRelation start(Integer userId, PaperModule module, Integer paperId, JSONObject setting){
        UserPaper paper = userPaperService.getByPaper(userId, module, paperId, initPaperCallback.get(module));
        // 查找对应的report是否有，如果没有或reset为1，则添加
        if (paper.getIsReset() > 0) {
            paper.setIsReset(0);
            userPaperService.edit(paper);
        }
        // 记录考试设置
        UserReport userReport = userReportService.addByPaper(paper, setting, initReportCallback.get(module));

        return relationReport(userReport);
    }

    /**
     * 继续做题：判断是否可以继续
     * @param userId
     * @param userReportId
     * @return
     */
    public UserReportRelation continueReport(Integer userId, Integer userReportId){
        UserReport userReport = userReportService.get(userReportId);
        if (!userReport.getUserId().equals(userId)){
            throw new ParameterException("试卷不存在");
        }
        if (userReport.getDetail() != null && userReport.getFinishTime() != null){
            throw new ParameterException("做题结束");
        }
        if (userReport.getUserNumber()>=userReport.getQuestionNumber()){
            throw new ParameterException("答题结束，请提交完成");
        }

        return relationReport(userReport);
    }

    /**
     * 获取单个report信息：基础
     * @param userId
     * @param userReportId
     * @return
     */
    public UserReportRelation baseReport(Integer userId, Integer userReportId){
        UserReport userReport = userReportService.get(userReportId);
        if (!userReport.getUserId().equals(userId)){
            throw new ParameterException("试卷不存在");
        }

        return relationReport(userReport);
    }

    /**
     * 获取report的下一道题
     * @param userId
     * @param userReportId
     * @return
     */
    public UserQuestion next(Integer userId, Integer userReportId){
        UserQuestion userQuestion = userQuestionService.getLastByReport(userId, userReportId);
        // 查找未完成的questionId
        if (userQuestion==null || userQuestion.getUserTime() >0){
            // 创建新的question
            UserReport report = userReportService.get(userReportId);
            if (!report.getUserId().equals(userId)){
                throw new ParameterException("试卷不存在");
            }
            userQuestion = userQuestionService.addByReport(report, userQuestion, nextCallback.get(PaperModule.ValueOf(report.getModule())));
        }

        return userQuestion;
    }

    /**
     * 提交试题
     * @param userId
     * @param userQuestionId
     * @param answer
     * @return
     */
    @Transactional
    public Boolean submit(Integer userId, Integer userQuestionId, Integer time, JSONObject answer){
        UserQuestion userQuestion = userQuestionService.get(userQuestionId);
        if (!userQuestion.getUserId().equals(userId)){
            throw new ParameterException("题目不存在");
        }
        userQuestion.setTime(time);
        userQuestion.setUserAnswer(answer);

        UserReport userReport = userReportService.get(userQuestion.getReportId());

        // 根据report模块获取试题所属模块
        QuestionModule module = QuestionModule.WithPaper(PaperModule.ValueOf(userReport.getModule()));
        SubmitQuestion callback = submitCallback.get(module);
        boolean result = callback.callback(userQuestion);
        userQuestion.setIsCorrect(result ? 1 : 0);
        userQuestionService.edit(userQuestion);

        // 根据剔除逻辑判断是否统计
        if (toolsService.filterTime(userQuestion)){
            SubmitAfterQuestion afterCallback = submitAfterCallback.get(module);
            afterCallback.callback(userQuestion);
        }

        // 更新对应report记录
        userReportService.accumulation(userQuestion);
        return true;
    }

    /**
     * 完成试卷，进行试卷分析
     * @param userId
     * @param userReportId
     * @return
     */
    @Transactional
    public Boolean finish(Integer userId, Integer userReportId){
        UserReport userReport = userReportService.get(userReportId);
        if (!userReport.getUserId().equals(userId)){
            throw new ParameterException("试卷不存在");
        }
        if (!userReport.getQuestionNumber().equals(userReport.getUserNumber())){
            throw new ParameterException("试卷未完成");
        }
        if (userReport.getDetail() != null && userReport.getFinishTime() != null){
            throw new ParameterException("做题结束");
        }
        userReport.setFinishTime(new Date());
        List<UserQuestion> userQuestionList = userQuestionService.listByReport(userId, userReportId);
        // 分析做题结果
        StatReport callback = finishCallback.get(PaperModule.ValueOf(userReport.getModule()));
        callback.callback(userReport, userQuestionList);

        // 统计: 更新对应paper记录
        userPaperService.accumulation(userReport);
        return true;
    }

    /**
     * 重新开始一份试卷：设定试卷reset=1，列表不绑定最后一次考试记录
     * @param paperId
     * @param userId
     * @return
     */
    public Boolean restart(Integer paperId, Integer userId){
        return userPaperService.reset(paperId, userId);
    }

    /**
     * 乱序生成题目列表
     * @param questionNoIds
     * @return
     */
    private Integer[] randomQuestionNoIds(Integer[] questionNoIds){
        Integer[] ran = new Integer[questionNoIds.length];
        List<Integer> base = Arrays.stream(questionNoIds).collect(Collectors.toList());
        int length = base.size();
        for(int i = 0; i < ran.length; i++){
            ran[i] = base.remove((int)(Math.random()*length));
            length -= 1;
        }
        return ran;
    }

    /**
     * 顺序获取下一题
     * @param ids
     * @param id
     * @return
     */
    private Integer nextId(Integer[] ids, Integer id){
        if (id == null) return ids[0];
        boolean flag = false;
        for(Integer a : ids){
            if (a.equals(id)){
                flag = true;
                continue;
            }
            if (flag){
                return a;
            }
        }
        return 0;
    }

    /**
     * 关联绑定questionNo类型试题
     * @param question
     * @param questionNoId
     * @param settingKey
     */
    private void bindQuestionNo(UserQuestion question, Integer questionNoId, SettingKey settingKey){
        QuestionNoRelation relation = questionNoService.getWithRelation(questionNoId);
        question.setQuestionNoId(relation.getId());
        question.setQuestionId(relation.getQuestionId());
        if (settingKey != null) {
            // 根据试题模块信息，获取对应时间key
            settingKey = SettingKey.getTimeByPaperModule(PaperModule.ValueOf(relation.getModule()));
        }

        Integer time = toolsService.computerTime(settingKey, relation);
        question.setTime(time);
    }

    /**
     * 获取报表关联试卷
     * @param p
     * @return
     */
    private UserReportRelation relationReport(UserReport p){
        UserReportRelation relation = Transform.convert(p, UserReportRelation.class);

        UserPaper paper = userPaperService.get(p.getPaperId());
        relation.setPaper(paper);
        return relation;
    }
}
