package com.qxgmat.controller.api;


import com.alibaba.fastjson.JSONObject;
import com.nuliji.tools.*;
import com.nuliji.tools.exception.ParameterException;
import com.qxgmat.data.constants.enums.QuestionType;
import com.qxgmat.data.constants.enums.ServiceKey;
import com.qxgmat.data.constants.enums.logic.ExerciseLogic;
import com.qxgmat.data.constants.enums.module.*;
import com.qxgmat.data.dao.entity.*;
import com.qxgmat.data.inline.UserQuestionStat;
import com.qxgmat.data.relation.entity.QuestionNoRelation;
import com.qxgmat.data.relation.entity.UserReportRelation;
import com.qxgmat.dto.extend.*;
import com.qxgmat.dto.request.*;
import com.qxgmat.dto.response.*;
import com.qxgmat.help.ShiroHelp;
import com.qxgmat.service.*;
import com.qxgmat.service.extend.*;
import com.qxgmat.service.inline.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpSession;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/question")
@Api(tags = "题目", description = "题目接口")
public class QuestionController {

    @Autowired
    private ShiroHelp shiroHelp;

    @Autowired
    private PreviewService previewService;

    @Autowired
    private ExercisePaperService exercisePaperService;

    @Autowired
    private ExerciseStructService exerciseStructService;

    @Autowired
    private ExerciseService exerciseService;

    @Autowired
    private ExaminationPaperService examinationPaperService;

    @Autowired
    private ExaminationStructService examinationStructService;

    @Autowired
    private ExaminationService examinationService;

    @Autowired
    private QuestionNoService questionNoService;

    @Autowired
    private QuestionService questionService;

    @Autowired
    private SentenceQuestionService sentenceQuestionService;

    @Autowired
    private TextbookQuestionService textbookQuestionService;

    @Autowired
    private SentencePaperService sentencePaperService;

    @Autowired
    private TextbookPaperService textbookPaperService;

    @Autowired
    private TextbookLibraryService textbookLibraryService;

    @Autowired
    private UserQuestionService userQuestionService;

    @Autowired
    private UserCollectQuestionService userCollectQuestionService;

    @Autowired
    private UserNoteQuestionService userNoteQuestionService;

    @Autowired
    private UserAskQuestionService userAskQuestionService;

    @Autowired
    private UserCourseService userCourseService;

    @Autowired
    private UserOrderService userOrderService;

    @Autowired
    private UserOrderRecordService userOrderRecordService;

    @Autowired
    private UserReportService userReportService;

    @Autowired
    private UserPaperService userPaperService;

    @Autowired
    private UserServiceService userServiceService;

    @Autowired
    private QuestionFlowService questionFlowService;

    @Autowired
    private CourseExtendService courseExtendService;

    @RequestMapping(value = "/exercise/progress", method = RequestMethod.GET)
    @ApiOperation(value = "练习进度", httpMethod = "GET")
    public Response<List<UserExerciseGroupDto>> exerciseProgress(
            @RequestParam(required = true) Integer structId, // 第二层,查询第4层,以及第三层汇总
            HttpSession session) {
        User user = (User) shiroHelp.getLoginUser();

        List<ExerciseStruct> three = exerciseStructService.children(structId, 0);
        List<UserExerciseGroupDto> p = new ArrayList<>(three.size());
        for(ExerciseStruct struct : three){
            UserExerciseGroupDto dto = Transform.convert(struct, UserExerciseGroupDto.class);
            // 获取第三层所有题目,并获取题目统计
            List<QuestionNo> list = questionNoService.listByStruct(StructModule.EXERCISE, struct.getId());
            dto.setStat(questionNoService.statPaper(list));
            dto.setQuestionNumber(list.size());
            Map<Object, UserQuestionStat> userQuestionStatMap = null;
            if(user != null){
                Collection questionNoIds = Transform.getIds(list, QuestionNo.class, "id");
                List<UserQuestion> userQuestionList = userQuestionService.listByQuestionNo(user.getId(), questionNoIds);
                userQuestionStatMap = userQuestionService.statQuestionNoMap(userQuestionList);

                dto.setUserStat(userQuestionService.statQuestion(userQuestionList));

                if (list.size() > userQuestionStatMap.size()){
                    dto.setUserNumber(userQuestionStatMap.size());
                    dto.setMinTimes(0);
                }else{
                    int minTimes = 0;
                    // 统计最小轮的已做题数
                    for(UserQuestionStat stat : userQuestionStatMap.values()){
                        if(stat.getUserNumber() < minTimes || minTimes == 0) minTimes = stat.getUserNumber();
                    }
                    int userNumber = 0;
                    for(UserQuestionStat stat : userQuestionStatMap.values()){
                        if(stat.getUserNumber() > minTimes) userNumber += 1;
                    }
                    dto.setMinTimes(minTimes);
                    dto.setUserNumber(userNumber);
                }
            }

            // 作文、阅读没有第4层
            // 获取第四层节点
            List<ExerciseStruct> children = exerciseStructService.children(struct.getId(), 0);
            if (children == null || children.size() == 0){
                // 以下属的paper作为children
                List<ExercisePaper> paperList = exercisePaperService.listByLogic(struct.getId(), 0, ExerciseLogic.NO, null);
                List<UserExerciseGroupExtendDto> childrenDtos = new ArrayList<>(paperList.size());

                for(ExercisePaper child : paperList){
                    UserExerciseGroupExtendDto extendDto = new UserExerciseGroupExtendDto();
                    extendDto.setId(child.getId());
                    extendDto.setTitle(child.getNo().toString());
                    extendDto.setQuestionNumber(child.getQuestionNumber());
                    if(user != null){
                        int minTimes = 0;
                        int userQuestionNumber = 0;
                        boolean flag = true;
                        for(int questionNoId : child.getQuestionNoIds()){
                            UserQuestionStat stat = userQuestionStatMap.get(questionNoId);
                            if (stat == null) {
                                flag = false;
                                break;
                            }
                            if (stat.getUserNumber() < minTimes || minTimes == 0) minTimes = stat.getUserNumber();
                        }
                        if (!flag) minTimes = 0;
                        for(int questionNoId : child.getQuestionNoIds()){
                            UserQuestionStat stat = userQuestionStatMap.get(questionNoId);
                            if (stat != null && stat.getUserNumber() > minTimes)  userQuestionNumber += 1;
                        }
                        extendDto.setUserNumber(userQuestionNumber);
                        extendDto.setMinTimes(minTimes);
                    }
                    extendDto.setType("paper");
                    childrenDtos.add(extendDto);
                }

                if (user != null){
                    Collection ids = Transform.getIds(paperList, ExercisePaper.class, "id");
                    List<UserPaper> userPaperList = userPaperService.listWithOrigin(user.getId(), PaperOrigin.EXERCISE, ids, null);
                    // 绑定userPaperId,用于关联report
                    Map userPaperMap = Transform.getMap(userPaperList, UserPaper.class, "originId", "id");
                    Transform.combine(childrenDtos, userPaperMap, UserExerciseGroupExtendDto.class, "id", "userPaperId");

                    // 获取最后一次作业结果
                    Collection paperIds = Transform.getIds(userPaperList, UserPaper.class, "id");
                    List<UserReport> reportList = userReportService.listWithLater(paperIds);
                    Transform.combine(childrenDtos, reportList, UserExerciseGroupExtendDto.class, "userPaperId", "report", UserReport.class, "paperId", UserReportExtendDto.class);
                }
                dto.setChildren(childrenDtos);
            }else{
                // 以struct作为children
                List<UserExerciseGroupExtendDto> childrenDtos = new ArrayList<>(children.size());
                for(ExerciseStruct child : children){
                    UserExerciseGroupExtendDto extendDto = new UserExerciseGroupExtendDto();
                    extendDto.setId(child.getId());
                    extendDto.setTitleEn(child.getTitleEn());
                    extendDto.setTitleZh(child.getTitleZh());
                    List<QuestionNo> childQuestionList = list.stream().filter((q)-> Collections.singletonList(q.getModuleStruct()).contains(child.getId().intValue())).collect(Collectors.toList());
                    extendDto.setQuestionNumber(childQuestionList.size());
                    if (user != null){
                        int minTimes = 0;
                        int userQuestionNumber = 0;
                        boolean flag = true;
                        for(QuestionNo questionNo : childQuestionList){
                            UserQuestionStat stat = userQuestionStatMap.get(questionNo.getId());
                            if (stat == null) {
                                flag = false;
                                break;
                            }
                            if (stat.getUserNumber() < minTimes || minTimes == 0) minTimes = stat.getUserNumber();
                        }
                        if (!flag) minTimes = 0;
                        for(QuestionNo questionNo : childQuestionList){
                            UserQuestionStat stat = userQuestionStatMap.get(questionNo.getId());
                            if (stat != null && stat.getUserNumber() > minTimes)  userQuestionNumber += 1;
                        }
                        extendDto.setUserNumber(userQuestionNumber);
                        extendDto.setMinTimes(minTimes);
                    }
                    extendDto.setType("struct");
                    childrenDtos.add(extendDto);
                }
                dto.setChildren(childrenDtos);
            }
            p.add(dto);
        }

        return ResponseHelp.success(p);
    }

    @RequestMapping(value = "/exercise/place", method = RequestMethod.GET)
    @ApiOperation(value = "练习组卷考点分组条件", httpMethod = "GET")
    public Response<List<String>> exercisePlace(
            @RequestParam(required = true) Integer structId, // 查询第4层
            HttpSession session) {
        return ResponseHelp.success(exercisePaperService.groupPlace(structId));
    }

    @RequestMapping(value = "/exercise/list", method = RequestMethod.GET)
    @ApiOperation(value = "练习组卷列表", httpMethod = "GET")
    public Response<PageMessage<UserExercisePaperDto>> listExercisePaper(
            @RequestParam(required = false, defaultValue = "1") int page,
            @RequestParam(required = false, defaultValue = "100") int size,
            @RequestParam(required = true) Integer structId,
            @RequestParam(required = true) String logic,
            @RequestParam(required = false) String logicExtend,
            @RequestParam(required = false)  Integer times,
            HttpSession session) {
        User user = (User) shiroHelp.getLoginUser();
        PageResult<ExercisePaper> p = exerciseService.list(page, size, structId, user != null ? user.getId():null, ExerciseLogic.ValueOf(logic), logicExtend, times);

        List<UserExercisePaperDto> pr = Transform.convert(p, UserExercisePaperDto.class);

        // 获取试卷统计信息
        Map<Integer, Integer[]> questionNoIdsMap = new HashMap<>();
        for(ExercisePaper paper : p){
            questionNoIdsMap.put(paper.getId(), paper.getQuestionNoIds());
        }
        Map statMap = questionNoService.statPaperMap(questionNoIdsMap);
        Transform.combine(pr, statMap, UserExercisePaperDto.class, "id", "stat");

        if (user != null){
            // 获取做题记录
            Collection ids = Transform.getIds(p, ExercisePaper.class, "id");
            List<UserPaper> paperList = userPaperService.listWithOrigin(user.getId(), PaperOrigin.EXERCISE, ids, null);
            Transform.combine(pr, paperList, UserExercisePaperDto.class, "id", "paper", UserPaper.class, "originId", UserPaperBaseExtendDto.class);
            // 绑定userPaperId,用于关联report
            Map userPaperMap = Transform.getMap(paperList, UserPaper.class, "originId", "id");
            Transform.combine(pr, userPaperMap, UserExercisePaperDto.class, "id", "userPaperId");

            // 获取最后一次作业结果
            Collection paperIds = Transform.getIds(paperList, UserPaper.class, "id");
            List<UserReport> reportList = userReportService.listWithLater(paperIds);
            Transform.combine(pr, reportList, UserExercisePaperDto.class, "id", "report", UserReport.class, "paperId", UserReportExtendDto.class);
        }

        return ResponseHelp.success(pr, page, size, p.getTotal());
    }


    @RequestMapping(value = "/examination/info", method = RequestMethod.GET)
    @ApiOperation(value = "模考信息", httpMethod = "GET")
    public Response<UserExaminationInfoDto> info(HttpSession session) {
        User user = (User) shiroHelp.getLoginUser();
        UserExaminationInfoDto dto = new UserExaminationInfoDto();

        if (user != null){
            UserService userService = userServiceService.getService(user.getId(), ServiceKey.QX_CAT);
            dto.setHasService(userService != null);
            UserOrderRecord record = userOrderRecordService.getUnUseService(user.getId(), ServiceKey.QX_CAT);
            dto.setUnUseRecord(Transform.convert(record, UserServiceRecordExtendDto.class));

            dto.setReset(userService != null && userService.getIsReset() > 0);
            dto.setExpireTime(userService != null ? userService.getExpireTime() : null);

            dto.setCanReset(examinationService.isFinishCat(user.getId()));
        }

        return ResponseHelp.success(dto);
    }

    @RequestMapping(value = "/examination/progress", method = RequestMethod.GET)
    @ApiOperation(value = "模考进度", httpMethod = "GET")
    public Response<List<UserExaminationGroupDto>> examinationProgress(
            @RequestParam(required = true) Integer structId, // 第一层,查询第三层,以及第二层汇总
            HttpSession session) {
        User user = (User) shiroHelp.getLoginUser();

        List<ExaminationStruct> two = examinationStructService.children(structId, 0);
        List<UserExaminationGroupDto> p = new ArrayList<>(two.size());
        for(ExaminationStruct struct : two){
            UserExaminationGroupDto dto = Transform.convert(struct, UserExaminationGroupDto.class);
            ServiceKey serviceKey = ServiceKey.ValueOf(struct.getExtend());
            dto.setNeedService(serviceKey != null);
            dto.setHasService(true);
            // 获取第三层节点
            // 以下属的paper作为children
            List<ExaminationPaper> paperList = examinationPaperService.listByTwo(struct.getId());
            dto.setPaperNumber(paperList.size());
            dto.setMinTimes(0);

            if(user != null){
                if (serviceKey != null){
                    // 服务, 判断对应服务状态
                    dto.setHasService(userServiceService.hasService(user.getId(), serviceKey));
                    UserOrderRecord record = userOrderRecordService.getUnUseService(user.getId(), serviceKey);
                    dto.setUnUseRecord(Transform.convert(record, UserServiceRecordExtendDto.class));
                }
                Collection ids = Transform.getIds(paperList, ExaminationPaper.class, "id");
                List<UserPaper> userPaperList = userPaperService.listWithOrigin(user.getId(), PaperOrigin.EXAMINATION, ids, null);

                if (paperList.size() > userPaperList.size()){
                    dto.setUserNumber(userPaperList.size());
                    dto.setMinTimes(0);
                }else{
                    int minTimes = 0;
                    // 统计最小轮的已做题数
                    for(UserPaper userPaper : userPaperList){
                        if(userPaper.getTimes() < minTimes || minTimes == 0) minTimes = userPaper.getTimes();
                    }
                    int userNumber = 0;
                    for(UserPaper userPaper : userPaperList){
                        if(userPaper.getTimes() > minTimes) userNumber += 1;
                    }
                    dto.setUserNumber(userNumber);
                }
            }
            p.add(dto);
        }

        return ResponseHelp.success(p);
    }

    @RequestMapping(value = "/examination/list", method = RequestMethod.GET)
    @ApiOperation(value = "模考组卷列表", httpMethod = "GET")
    public Response<PageMessage<UserExaminationPaperDto>> examinationPaperList(
            @RequestParam(required = false, defaultValue = "1") int page,
            @RequestParam(required = false, defaultValue = "100") int size,
            @RequestParam(required = true) Integer structId,
            @RequestParam(required = false)  Integer times,
            HttpSession session) {
        User user = (User) shiroHelp.getLoginUser();
        ExaminationStruct struct = examinationStructService.get(structId);
        ServiceKey serviceKey = ServiceKey.ValueOf(struct.getExtend());
        if (serviceKey == ServiceKey.QX_CAT){
            if (user == null){
                throw new ParameterException("请先登录");
            }
            if (!userServiceService.hasService(user.getId(), serviceKey)){
                throw new ParameterException("请先开通模考");
            }
        }
        PageResult<ExaminationPaper> p = examinationService.list(page, size, structId, user != null ? user.getId():null, serviceKey == ServiceKey.QX_CAT && user != null ? user.getQxCat() : null, times);

        List<UserExaminationPaperDto> pr = Transform.convert(p, UserExaminationPaperDto.class);

        // 获取试卷统计信息
        Map<Integer, Integer[]> questionNoIdsMap = new HashMap<>();
        for(ExaminationPaper paper : p){
            questionNoIdsMap.put(paper.getId(), paper.getQuestionNoIds());
        }
        Map statMap = questionNoService.statPaperMap(questionNoIdsMap);
        Transform.combine(pr, statMap, UserExercisePaperDto.class, "id", "stat");


        if (user != null){
            // 获取做题记录
            Collection ids = Transform.getIds(p, ExaminationPaper.class, "id");
            List<UserPaper> paperList;
            if (serviceKey == ServiceKey.QX_CAT){
                // cat模考,根据情况获取对应试题列表
                paperList = userPaperService.listWithCat(user.getId(), ids, user.getQxCat());
            }else{
                paperList = userPaperService.listWithOrigin(user.getId(), PaperOrigin.EXAMINATION, ids, null);
            }
            Transform.combine(pr, paperList, UserExaminationPaperDto.class, "id", "paper", UserPaper.class, "originId", UserPaperBaseExtendDto.class);
            // 绑定userPaperId,用于关联report
            Map userPaperMap = Transform.getMap(paperList, UserPaper.class, "originId", "id");
            Transform.combine(pr, userPaperMap, UserExaminationPaperDto.class, "id", "userPaperId");

            // 获取最后一次结果
            Collection paperIds = Transform.getIds(paperList, UserPaper.class, "id");
            List<UserReport> reportList = userReportService.listWithLater(paperIds);
            Transform.combine(pr, reportList, UserExaminationPaperDto.class, "id", "report", UserReport.class, "paperId", UserReportExtendDto.class);

            if (serviceKey == ServiceKey.QX_CAT && user.getQxCat() > 0){
                // 获取上一遍模考成绩
                UserService userService = userServiceService.getServiceBase(user.getId(), serviceKey);
                if (userService.getIsReset() > 0){
                    List<UserPaper> prevPaperList = userPaperService.listWithCat(user.getId(), ids, user.getQxCat() - 1);
                    Transform.combine(pr, prevPaperList, UserExaminationPaperDto.class, "id", "prevPaper", UserPaper.class, "originId", UserPaperBaseExtendDto.class);
                    // 绑定userPaperId,用于关联report
                    Map prevUserPaperMap = Transform.getMap(prevPaperList, UserPaper.class, "originId", "id");
                    Transform.combine(pr, prevUserPaperMap, UserExaminationPaperDto.class, "id", "prevUserPaperId");

                    // 获取最后一次结果
                    Collection prevPaperIds = Transform.getIds(prevPaperList, UserPaper.class, "id");
                    List<UserReport> prevReportList = userReportService.listWithLaterNoReset(prevPaperIds);
                    Transform.combine(pr, prevReportList, UserExaminationPaperDto.class, "id", "prevReport", UserReport.class, "paperId", UserReportExtendDto.class);
                }
            }
        }

        return ResponseHelp.success(pr, page, size, p.getTotal());
    }

    @RequestMapping(value = "/base", method = RequestMethod.GET)
    @ApiOperation(value = "获取题目详情", notes = "根据题目序号获取题目", httpMethod = "GET")
    public Response<UserQuestionBaseDto> base(
            @RequestParam(required = true) Integer userReportId,
            @RequestParam(required = true) Integer no
    )  {
        User user = (User) shiroHelp.getLoginUser();
        if (no == null || no == 0){
            no = 1;
        }
        UserQuestion userQuestion = userQuestionService.getByReportAndNo(user.getId(), userReportId, no);
        UserQuestionBaseDto dto = Transform.convert(userQuestion, UserQuestionBaseDto.class);

        return ResponseHelp.success(dto);
    }

    @RequestMapping(value = "/detail", method = RequestMethod.GET)
    @ApiOperation(value = "获取题目详情", notes = "获取题目详情", httpMethod = "GET")
    public Response<UserQuestionDetailDto> detail(
            @RequestParam(required = true) Integer userQuestionId
    )  {
        User user = (User) shiroHelp.getLoginUser();
        UserQuestion userQuestion = userQuestionService.get(userQuestionId);

        UserQuestionDetailDto dto = Transform.convert(userQuestion, UserQuestionDetailDto.class);

        UserReport userReport = userReportService.get(userQuestion.getReportId());
        dto.setReport(Transform.convert(userReport, UserReportExtendDto.class));

        UserPaper userPaper = userPaperService.get(userReport.getPaperId());
        dto.setPaper(Transform.convert(userPaper, UserPaperBaseExtendDto.class));

        Question question = questionService.get(userQuestion.getQuestionId());
        dto.setQuestion(Transform.convert(question, QuestionDetailExtendDto.class));

        UserCollectQuestion collect = userCollectQuestionService.getByUserAndQuestion(user.getId(), userQuestion.getQuestionId());
        dto.setCollect(collect != null);

        UserNoteQuestion userNoteQuestion = userNoteQuestionService.getByUserAndQuestion(user.getId(), userQuestion.getQuestionId());
        dto.setNote(Transform.convert(userNoteQuestion, UserNoteQuestionExtendDto.class));

        List<UserAskQuestion> userAskQuestionList = userAskQuestionService.listByQuestion(userQuestion.getQuestionId(), true);
        dto.setAsks(Transform.convert(userAskQuestionList, UserAskQuestionExtendDto.class));

        if (question.getAssociationContent() != null){
            List<QuestionNoRelation> associations = questionNoService.listWithRelationByIds(question.getAssociationContent());
            Collection questions = Transform.getIds(associations, QuestionNoRelation.class, "question");
            dto.setAssociations(Transform.convert(questions, QuestionBaseExtendDto.class));
        }

        switch (QuestionModule.ValueOf(userQuestion.getQuestionModule())){
            case BASE:
                List<QuestionNo> questionNoList = questionNoService.listByQuestion(userQuestion.getQuestionId());
                dto.setQuestionNos(Transform.convert(questionNoList, QuestionNoExtendDto.class));
                break;
            case SENTENCE:
                SentenceQuestion sentenceQuestion = sentenceQuestionService.get(userQuestion.getQuestionNoId());
                dto.setQuestionNo(Transform.convert(sentenceQuestion, QuestionNoExtendDto.class));
                break;
            case TEXTBOOK:
                TextbookQuestion textbookQuestion = textbookQuestionService.get(userQuestion.getQuestionNoId());
                dto.setQuestionNo(Transform.convert(textbookQuestion, QuestionNoExtendDto.class));
                break;
        }
        // 获取提问权限
        PaperOrigin origin= PaperOrigin.ValueOf(userReport.getPaperOrigin());
        Integer recordId = questionFlowService.questionRelationCourse(user.getId(), origin == PaperOrigin.PREVIEW ? userReport.getOriginId() : null, QuestionType.ValueOf(question.getQuestionType()));
        if (recordId != null){
            dto.setQuestionStatus(1);
        }else{
            // 获取基本当前权限
            switch(origin){
                case EXAMINATION:
                    ExaminationPaper examinationPaper = examinationPaperService.get(userReport.getOriginId());
                    ExaminationStruct examinationStruct = examinationStructService.get(examinationPaper.getStructThree());
                    dto.setQuestionStatus(examinationStruct.getQuestionStatus());
                    break;
                case EXERCISE:
                    ExercisePaper exercisePaper = exercisePaperService.get(userReport.getOriginId());
                    ExerciseStruct exerciseStruct = exerciseStructService.get(exercisePaper.getStructFour());
                    dto.setQuestionStatus(exerciseStruct.getQuestionStatus());
                    break;
                case TEXTBOOK:
                    TextbookQuestion textbookQuestion = textbookQuestionService.get(userQuestion.getQuestionNoId());
                    TextbookLibrary textbookLibrary = textbookLibraryService.get(textbookQuestion.getLibraryId());
                    dto.setQuestionStatus(textbookLibrary.getQuestionStatus());
                    break;
                case PREVIEW:
                    // 上面questionRelationCourse包括了,不会执行到
                default:
                    // 自由组卷,不提供提问显示
                    dto.setQuestionStatus(-1);
            }
        }


        return ResponseHelp.success(dto);
    }

    @RequestMapping(value = "/exercise/paper", method = RequestMethod.GET)
    @ApiOperation(value = "获取练习卷", notes = "获取练习卷", httpMethod = "GET")
    public Response<PaperBaseDto> detailExercise(
            @RequestParam(required = true) Integer paperId
    )  {
        User user = (User) shiroHelp.getLoginUser();
        UserPaper paper = questionFlowService.paper(user.getId(), PaperOrigin.EXERCISE, paperId);
        PaperBaseDto paperDto = Transform.convert(paper, PaperBaseDto.class);

        return ResponseHelp.success(paperDto);
    }

    @RequestMapping(value = "/examination/paper", method = RequestMethod.GET)
    @ApiOperation(value = "获取模考卷", notes = "获取模考卷", httpMethod = "GET")
    public Response<PaperBaseDto> detailExamination(
            @RequestParam(required = true) Integer paperId
    )  {
        User user = (User) shiroHelp.getLoginUser();
        UserPaper paper = questionFlowService.paper(user.getId(), PaperOrigin.EXAMINATION, paperId);
        PaperBaseDto paperDto = Transform.convert(paper, PaperBaseDto.class);

        return ResponseHelp.success(paperDto);
    }

    @RequestMapping(value = "/report/base", method = RequestMethod.GET)
    @ApiOperation(value = "获取练习记录", notes = "获取练习记录", httpMethod = "GET")
    public Response<UserReportBaseDto> baseReport(
            @RequestParam(required = true) Integer userReportId
    )  {
        User user = (User) shiroHelp.getLoginUser();
        UserReportRelation report = questionFlowService.baseReport(user.getId(), userReportId);

        UserReportBaseDto userReportDto = Transform.convert(report, UserReportBaseDto.class);
        return ResponseHelp.success(userReportDto);
    }

    @RequestMapping(value = "/report/detail", method = RequestMethod.GET)
    @ApiOperation(value = "获取练习详细记录", notes = "获取练习卷", httpMethod = "GET")
    public Response<UserReportDetailDto> detailReport(
            @RequestParam(required = true) Integer userReportId
    )  {
        User user = (User) shiroHelp.getLoginUser();
        UserReportRelation report = questionFlowService.baseReport(user.getId(), userReportId);

        UserReportDetailDto userReportDto = Transform.convert(report, UserReportDetailDto.class);

        // 用户paper
        UserPaper userPaper = userPaperService.get(report.getPaperId());
        userReportDto.setPaper(Transform.convert(userPaper, UserPaperBaseExtendDto.class));

        return ResponseHelp.success(userReportDto);
    }

    @RequestMapping(value = "/report/question", method = RequestMethod.GET)
    @ApiOperation(value = "获取做题记录", notes = "获取做题记录", httpMethod = "GET")
    public Response<List<UserQuestionExtendDto>> detailReportQuestion(
            @RequestParam(required = true) Integer userReportId
    )  {
        User user = (User) shiroHelp.getLoginUser();
        List<UserQuestion> userQuestionList = questionFlowService.listByReport(user.getId(), userReportId);
        List<UserQuestionExtendDto> userQuestionDtos = Transform.convert(userQuestionList, UserQuestionExtendDto.class);

        Collection ids = Transform.getIds(userQuestionList, UserQuestion.class, "questionId");
        List<UserCollectQuestion> userCollectQuestionList = userCollectQuestionService.listByUserAndQuestions(user.getId(), ids);
        Map collectMap = Transform.getMap(userCollectQuestionList, UserCollectQuestion.class, "questionId", "id");

        List<UserNoteQuestion> userNoteQuestionList = userNoteQuestionService.listByUserAndQuestions(user.getId(), ids);
        Map noteMap = Transform.getMap(userNoteQuestionList, UserNoteQuestion.class, "questionId", "id");

        for(UserQuestionExtendDto dto : userQuestionDtos){
            dto.setCollect(collectMap.containsKey(dto.getQuestionId()));
            dto.setNote(noteMap.containsKey(dto.getQuestionId()));
        }

        return ResponseHelp.success(userQuestionDtos);
    }

    @RequestMapping(value = "/examination/start", method = RequestMethod.POST)
    @ApiOperation(value = "开始: 模考", notes = "提交考试设置", httpMethod = "POST")
    public Response<UserReportBaseDto> startExamination(@RequestBody @Validated ExaminationStartDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        JSONObject setting = new JSONObject();
        setting.put("disorder", dto.getDisorder());
        setting.put("order", dto.getOrder());
        UserReport report = questionFlowService.start(user.getId(), PaperOrigin.EXAMINATION, dto.getPaperId(), setting);

        return ResponseHelp.success(Transform.convert(report, UserReportBaseDto.class));
    }

    @RequestMapping(value = "/exercise/start", method = RequestMethod.POST)
    @ApiOperation(value = "开始: 练习", notes = "提交考试设置", httpMethod = "POST")
    public Response<UserReportBaseDto> startExercise(@RequestBody @Validated ExerciseStartDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        JSONObject setting = new JSONObject();
        setting.put("disorder", dto.getDisorder());
        UserReport report = questionFlowService.start(user.getId(), PaperOrigin.EXERCISE, dto.getPaperId(), setting);

        return ResponseHelp.success(Transform.convert(report, UserReportBaseDto.class));
    }

    @RequestMapping(value = "/preview/start", method = RequestMethod.POST)
    @ApiOperation(value = "开始: 预习作业", notes = "提交考试设置", httpMethod = "POST")
    public Response<UserReportBaseDto> startPreview(@RequestBody @Validated PreviewStartDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        JSONObject setting = new JSONObject();
        setting.put("disorder", dto.getDisorder());
        UserReportRelation report = questionFlowService.start(user.getId(), PaperOrigin.PREVIEW, dto.getPaperId(), setting);

        UserReportBaseDto userReportBaseDto = Transform.convert(report, UserReportBaseDto.class);

        return ResponseHelp.success(userReportBaseDto);
    }

    @RequestMapping(value = "/textbook/paper", method = RequestMethod.GET)
    @ApiOperation(value = "获取机经练习卷", notes = "获取练习卷", httpMethod = "GET")
    public Response<PaperBaseDto> detailTextbookPaper(
            @RequestParam(required = true) Integer paperId
    )  {
        User user = (User) shiroHelp.getLoginUser();
        UserPaper paper = questionFlowService.paper(user.getId(), PaperOrigin.TEXTBOOK, paperId);

        PaperBaseDto paperDto = Transform.convert(paper, PaperBaseDto.class);

        return ResponseHelp.success(paperDto);
    }

    @RequestMapping(value = "/textbook/start", method = RequestMethod.POST)
    @ApiOperation(value = "开始: 机经", notes = "提交考试设置", httpMethod = "POST")
    public Response<UserReportBaseDto> startTextbook(@RequestBody @Validated TextbookStartDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        JSONObject setting = new JSONObject();
        setting.put("disorder", dto.getDisorder());
        UserReportRelation report = questionFlowService.start(user.getId(), PaperOrigin.TEXTBOOK, dto.getPaperId(), setting);

        UserReportBaseDto userReportBaseDto = Transform.convert(report, UserReportBaseDto.class);

        return ResponseHelp.success(userReportBaseDto);
    }

    @RequestMapping(value = "/sentence/paper", method = RequestMethod.GET)
    @ApiOperation(value = "获取长难句练习卷", notes = "获取练习卷", httpMethod = "GET")
    public Response<PaperBaseDto> detailSentencePaper(
            @RequestParam(required = true) Integer paperId
    )  {
        User user = (User) shiroHelp.getLoginUser();
        UserPaper paper = questionFlowService.paper(user.getId(), PaperOrigin.SENTENCE, paperId);

        PaperBaseDto paperDto = Transform.convert(paper, PaperBaseDto.class);

        return ResponseHelp.success(paperDto);
    }

    @RequestMapping(value = "/sentence/start", method = RequestMethod.POST)
    @ApiOperation(value = "开始: 长难句", notes = "提交考试设置", httpMethod = "POST")
    public Response<UserReportBaseDto> startSentence(@RequestBody @Validated SentenceStartDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        JSONObject setting = new JSONObject();
        UserReportRelation report = questionFlowService.start(user.getId(), PaperOrigin.SENTENCE, dto.getPaperId(), setting);

        UserReportBaseDto userReportBaseDto = Transform.convert(report, UserReportBaseDto.class);

        return ResponseHelp.success(userReportBaseDto);
    }

    @RequestMapping(value = "/error/paper", method = RequestMethod.GET)
    @ApiOperation(value = "获取错题组卷", notes = "获取错题组卷", httpMethod = "GET")
    public Response<PaperBaseDto> detailError(
            @RequestParam(required = true) Integer paperId
    )  {
        User user = (User) shiroHelp.getLoginUser();
        UserPaper paper = userPaperService.get(paperId);
        if (!paper.getPaperOrigin().equals(PaperOrigin.ERROR.key)){
            throw new ParameterException("试卷不存在");
        }
        if (!paper.getUserId().equals(user.getId())){
            throw new ParameterException("试卷不存在");
        }
        PaperBaseDto paperDto = Transform.convert(paper, PaperBaseDto.class);

        return ResponseHelp.success(paperDto);
    }

    @RequestMapping(value = "/error/start", method = RequestMethod.POST)
    @ApiOperation(value = "开始: 错题组卷", notes = "提交考试设置", httpMethod = "POST")
    public Response<UserReportBaseDto> startError(@RequestBody @Validated PreviewStartDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        JSONObject setting = new JSONObject();
        setting.put("disorder", dto.getDisorder());
        UserReportRelation report = questionFlowService.start(user.getId(), PaperOrigin.ERROR, dto.getPaperId(), setting);

        UserReportBaseDto userReportBaseDto = Transform.convert(report, UserReportBaseDto.class);

        return ResponseHelp.success(userReportBaseDto);
    }

    @RequestMapping(value = "/collect/paper", method = RequestMethod.GET)
    @ApiOperation(value = "获取收藏组卷", notes = "获取收藏组卷", httpMethod = "GET")
    public Response<PaperBaseDto> detailCollect(
            @RequestParam(required = true) Integer paperId
    )  {
        User user = (User) shiroHelp.getLoginUser();
        UserPaper paper = userPaperService.get(paperId);
        if (!paper.getPaperOrigin().equals(PaperOrigin.COLLECT.key)){
            throw new ParameterException("试卷不存在");
        }
        if (!paper.getUserId().equals(user.getId())){
            throw new ParameterException("试卷不存在");
        }
        PaperBaseDto paperDto = Transform.convert(paper, PaperBaseDto.class);

        return ResponseHelp.success(paperDto);
    }

    @RequestMapping(value = "/collect/start", method = RequestMethod.POST)
    @ApiOperation(value = "开始: 收藏组卷", notes = "提交考试设置", httpMethod = "POST")
    public Response<UserReportBaseDto> startCollect(@RequestBody @Validated PreviewStartDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        JSONObject setting = new JSONObject();
        setting.put("disorder", dto.getDisorder());
        UserReportRelation report = questionFlowService.start(user.getId(), PaperOrigin.COLLECT, dto.getPaperId(), setting);

        UserReportBaseDto userReportBaseDto = Transform.convert(report, UserReportBaseDto.class);

        return ResponseHelp.success(userReportBaseDto);
    }

    @RequestMapping(value = "/continue", method = RequestMethod.POST)
    @ApiOperation(value = "继续做题", notes = "获取报告信息", httpMethod = "POST")
    public Response<UserReportBaseDto> continueReport(@RequestBody @Validated ReportContinueDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        UserReportRelation report = questionFlowService.continueReport(user.getId(), dto.getUserReportId());

        UserReportBaseDto userReportBaseDto = Transform.convert(report, UserReportBaseDto.class);

        return ResponseHelp.success(userReportBaseDto);
    }

    @RequestMapping(value = "/next", method = RequestMethod.POST)
    @ApiOperation(value = "获取下一题", notes = "获取下一题", httpMethod = "POST")
    public Response<UserQuestionBaseDto> next(@RequestBody @Validated ReportNextDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        UserQuestion userQuestion = questionFlowService.next(user.getId(), dto.getUserReportId());
        if (userQuestion == null) {
            throw new ParameterException("finish");
        }
        UserQuestionBaseDto baseDto = Transform.convert(userQuestion, UserQuestionBaseDto.class);

        // 绑定questionNos
        baseDto.setQuestionNos(Transform.convert(questionNoService.listByQuestion(userQuestion.getQuestionId()), QuestionNoExtendDto.class));

        // 绑定question
        baseDto.setQuestion(Transform.convert(questionService.get(userQuestion.getQuestionId()), QuestionBaseExtendDto.class));

        // 绑定questionNo
        baseDto.setQuestionNo(Transform.convert(questionNoService.get(userQuestion.getQuestionNoId()), QuestionNoExtendDto.class));

        // 绑定collect
        baseDto.setCollect(userCollectQuestionService.getByUserAndQuestion(user.getId(), userQuestion.getQuestionId()) != null);

        return ResponseHelp.success(baseDto);
    }

    @RequestMapping(value = "/submit", method = RequestMethod.POST)
    @ApiOperation(value = "提交题目答案", notes = "提交题目", httpMethod = "POST")
    public Response<Boolean> submit(@RequestBody @Validated QuestionSubmitDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        UserQuestion userQuestion = userQuestionService.get(dto.getUserQuestionId());
        if (userQuestion == null){
            throw new ParameterException("做题不存在");
        }
        userQuestion.setUserTime(dto.getTime());
        userQuestion.setUserAnswer(JSONObject.parseObject(JSONObject.toJSONString(dto.getAnswer())));
        userQuestion.setSetting(JSONObject.parseObject(JSONObject.toJSONString(dto.getSetting())));
        Boolean result = questionFlowService.submit(user.getId(), userQuestion);
        return ResponseHelp.success(result);
    }

    @RequestMapping(value = "/finish", method = RequestMethod.POST)
    @ApiOperation(value = "完成考试", notes = "完成考试", httpMethod = "POST")
    public Response<Boolean> finish(@RequestBody @Validated ReportFinishDto dto)  {
        User user = (User) shiroHelp.getLoginUser();

        Boolean result = questionFlowService.finish(user.getId(), dto.getUserReportId());
        return ResponseHelp.success(result);
    }

    @RequestMapping(value = "/stage", method = RequestMethod.POST)
    @ApiOperation(value = "本阶段完成", notes = "结束当前阶段: 达到阶段时间时调用,然后继续调用next", httpMethod = "POST")
    public Response<Boolean> stage(@RequestBody @Validated ReportStageDto dto)  {
        User user = (User) shiroHelp.getLoginUser();
        Boolean result = questionFlowService.stage(user.getId(), dto.getUserReportId());
        return ResponseHelp.success(result);
    }

    @RequestMapping(value = "/restart/paper", method = RequestMethod.POST)
    @ApiOperation(value = "重置考试", notes = "重置考试", httpMethod = "POST")
    public Response<Boolean> restart(@RequestBody @Validated PaperRestartDto dto)  {
        User user = (User) shiroHelp.getLoginUser();

        questionFlowService.restart(dto.getUserPaperId(), user.getId());
        return ResponseHelp.success(true);
    }

    @RequestMapping(value = "/reset/cat", method = RequestMethod.POST)
    @ApiOperation(value = "重置整套模拟卷", notes = "重置考试", httpMethod = "POST")
    public Response<Boolean> resetCat()  {
        User user = (User) shiroHelp.getLoginUser();

        UserService userService = userServiceService.getServiceBase(user.getId(), ServiceKey.QX_CAT);
        if (userService == null){
            throw new ParameterException("无重置权限");
        }
        if (userService.getIsReset() > 0){
            throw new ParameterException("已重置,请再次购买服务");
        }
        // reset当前考卷的所有状态
        examinationService.resetCat(user.getId(), false);
        userServiceService.edit(UserService.builder().id(userService.getId()).isReset(1).build());
        return ResponseHelp.success(true);
    }
}