package com.qmth.themis.exam.api;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qmth.themis.business.annotation.ApiJsonObject;
import com.qmth.themis.business.annotation.ApiJsonProperty;
import com.qmth.themis.business.bean.exam.*;
import com.qmth.themis.business.cache.ExamBreakCacheUtil;
import com.qmth.themis.business.cache.ExamRecordCacheUtil;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.MqDto;
import com.qmth.themis.business.dto.cache.TEStudentCacheDto;
import com.qmth.themis.business.dto.response.TEExamDto;
import com.qmth.themis.business.dto.response.TEExamResultDto;
import com.qmth.themis.business.entity.TEExam;
import com.qmth.themis.business.enums.*;
import com.qmth.themis.business.service.MqDtoService;
import com.qmth.themis.business.service.TEExamService;
import com.qmth.themis.business.service.TEExamStudentService;
import com.qmth.themis.business.util.JacksonUtil;
import com.qmth.themis.business.util.RedisUtil;
import com.qmth.themis.business.util.ServletUtil;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.Result;
import com.qmth.themis.common.util.ResultUtil;
import com.qmth.themis.exam.config.ExamConstant;
import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

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

@Api(tags = "考试Controller")
@RestController
@RequestMapping("/${prefix.url.exam}/exam")
public class TEExamController {

    @Resource
    TEExamService teExamService;

    @Resource
    RedisUtil redisUtil;

    @Resource
    MqDtoService mqDtoService;

    @Resource
    TEExamStudentService teExamStudentService;

    @ApiOperation(value = "验证考试口令接口")
    @RequestMapping(value = "/short_code", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "考试信息", response = TEExam.class)})
    public Result shortCode(@ApiJsonObject(name = "shortCode", value = {
            @ApiJsonProperty(key = "shortCode", description = "考试口令"),
    }) @ApiParam(value = "考试口令", required = true) @RequestBody Map<String, Object> mapParameter) {
        if (Objects.isNull(mapParameter.get("shortCode")) || Objects.equals(mapParameter.get("shortCode"), "")) {
            throw new BusinessException(ExceptionResultEnum.SHORT_CODE_IS_NULL);
        }
        String shortCode = String.valueOf(mapParameter.get("shortCode"));
        QueryWrapper<TEExam> teExamQueryWrapper = new QueryWrapper<>();
        teExamQueryWrapper.lambda().eq(TEExam::getShortCode, shortCode);
        TEExam teExam = teExamService.getOne(teExamQueryWrapper);
        if (Objects.isNull(teExam)) {
            throw new BusinessException(ExceptionResultEnum.EXAM_NO);
        }
        Map<String, Object> map = new HashMap<>();
        map.put(SystemConstant.ID, teExam.getId());
        map.put(SystemConstant.NAME, teExam.getName());
        return ResultUtil.ok(map);
    }

    @ApiOperation(value = "获取待考列表接口")
    @RequestMapping(value = "/waiting", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "考试信息", response = TEExamResultDto.class)})
    public Result waiting() {
        TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
        List<TEExamDto> list = teExamService.getWaitingExam(teStudent.getId(), null, teStudent.getOrgId());
        Map<String, Object> map = new HashMap<>();
        if (Objects.nonNull(list) && list.size() > 0) {
            map.put("waiting", list);
        }
        return ResultUtil.ok(map);
    }

    @ApiOperation(value = "开始候考")
    @RequestMapping(value = "/prepare", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result prepare(@RequestBody PrepareParamBean param) {
        TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
        String lockKey = SystemConstant.REDIS_LOCK_STUDENT_PREFIX + teStudent.getId();
        Boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_CACHE_TIME_OUT);
        if (!lock) {
            throw new BusinessException(ExceptionResultEnum.REQUEST_AWAIT);
        }
        try {
            if (param.getExamStudentId() == null) {
                throw new BusinessException("考生id不能为空");
            }
            return ResultUtil.ok(teExamService.prepare(teStudent.getId(), param.getExamStudentId()));
        } finally {
            redisUtil.releaseLock(lockKey);
        }
    }

    @ApiOperation(value = "候考/答题状态退出")
    @RequestMapping(value = "/exit", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    public Result exit(@ApiJsonObject(name = "saveInvigilateUser", value = {
            @ApiJsonProperty(key = "recordId", type = "long", example = "1", description = "考试记录id", required = true)
    }) @ApiParam(value = "考试记录id", required = true) @RequestBody Map<String, Object> mapParameter) {
        if (Objects.isNull(mapParameter.get("recordId")) || Objects.equals(mapParameter.get("recordId"), "")) {
            throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
        }
        String recordId = (String) mapParameter.get("recordId");
        ExamRecordStatusEnum status = ExamRecordCacheUtil.getStatus(Long.valueOf(recordId));
        if (Objects.nonNull(status) && Objects.equals(ExamRecordStatusEnum.ANSWERING, status)) {//答题状态，强制断点
            ExamConstant.sendExamBreakMsg(Long.valueOf(recordId));
        } else if (Objects.nonNull(status) && Objects.equals(ExamRecordStatusEnum.FIRST_PREPARE, status)) {
            ExamConstant.sendExamStopMsg(Long.valueOf(recordId), false);
        } else {
            throw new BusinessException("考试状态出错");
        }
        return ResultUtil.ok(Collections.singletonMap("success", true));
    }

    @ApiOperation(value = "开始考试")
    @RequestMapping(value = "/start", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result start(@RequestBody ExamStartParamBean param) {
        TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
        String lockKey = SystemConstant.REDIS_LOCK_STUDENT_PREFIX + teStudent.getId();
        Boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_CACHE_TIME_OUT);
        if (!lock) {
            throw new BusinessException(ExceptionResultEnum.REQUEST_AWAIT);
        }
        try {
            if (param.getRecordId() == null) {
                throw new BusinessException("考试记录id不能为空");
            }
            ExamStartBean examStartBean = teExamService.start(teStudent.getId(), param.getRecordId());
            if (Objects.nonNull(param.getReason())) {
                Long breakId = ExamRecordCacheUtil.getLastBreakId(param.getRecordId());
                if (Objects.nonNull(breakId)) {
                    JSONObject jsonObject = JSONObject.parseObject(param.getReason());
                    ExceptionEnum exceptionEnum = ExceptionEnum.valueOf(ExceptionEnum.convertToName(String.valueOf(jsonObject.get("type"))));
                    String reason = String.valueOf(jsonObject.get("reason"));
                    ExamBreakCacheUtil.setBreakReason(breakId, exceptionEnum);
                    ExamBreakCacheUtil.setResumeReason(breakId, reason);
                    //考试断点异常原因 发送mq start
                    MqDto mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.EXCEPTION_LOG.name(), JacksonUtil.parseJson(param), MqTagEnum.EXCEPTION_LOG, String.valueOf(param.getRecordId()), param.getReason());
                    mqDtoService.assembleSendOneWayMsg(mqDto);
                    //考试断点异常原因 发送mq end
                }
            } else {
                //mq发送消息start
                MqDto mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.STUDENT.name(), SystemOperationEnum.ANSWERING, MqTagEnum.STUDENT, String.valueOf(teStudent.getId()), teStudent.getIdentity());
                teExamService.sendOeLogMessage(SystemOperationEnum.ANSWERING, ExamRecordCacheUtil.getExamStudentId(param.getRecordId()), param.getRecordId(), mqDto);
                //mq发送消息end
            }
            ExamConstant.sendExamStartMsg(param.getRecordId());
            return ResultUtil.ok(examStartBean);
        } finally {
            redisUtil.releaseLock(lockKey);
        }
    }

    @ApiOperation(value = "上传个人试卷结构")
    @RequestMapping(value = "/student_paper_struct/upload", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result studentPaperStruct(@RequestBody StudentPaperStructParamBean param) {
        TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
        if (param.getRecordId() == null) {
            throw new BusinessException("考试记录id不能为空");
        }
        if (param.getContent() == null) {
            throw new BusinessException("试卷结构json字符串不能为空");
        }
        return ResultUtil
                .ok(teExamService.studentPaperStruct(teStudent.getId(), param.getRecordId(), param.getContent()));
    }

    @ApiOperation(value = "提交作答结果")
    @RequestMapping(value = "/answer/submit", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result answerSubmit(@RequestBody AnswerSubmitParamBean param) {
        TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
        if (param.getRecordId() == null) {
            throw new BusinessException("考试记录id不能为空");
        }
        if (param.getMainNumber() == null) {
            throw new BusinessException("大题号不能为空");
        }
        if (param.getSubNumber() == null) {
            throw new BusinessException("小题号不能为空");
        }
        if (param.getAnswer() == null) {
            throw new BusinessException("答案json字符串不能为空");
        }
        if (param.getVersion() == null) {
            throw new BusinessException("时间戳不能为空");
        }
        AnswerSubmitBean ret = teExamService.answerSubmit(teStudent.getId(), param.getRecordId(), param.getMainNumber(),
                param.getSubNumber(), param.getSubIndex(), param.getAnswer(), param.getVersion(),
                param.getDurationSeconds());
        return ResultUtil.ok(ret);
    }

    @ApiOperation(value = "更新音频剩余播放次数")
    @RequestMapping(value = "/audio_left_play_count/submit", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result audioLeftPlayCountSubmit(@RequestBody AudioLeftPlayCountSubmitParamBean param) {
        TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
        if (param.getRecordId() == null) {
            throw new BusinessException("考试记录id不能为空");
        }
        if (param.getKey() == null) {
            throw new BusinessException("音频标识不能为空");
        }
        if (param.getCount() == null) {
            throw new BusinessException("剩余播放次数不能为空");
        }
        return ResultUtil.ok(teExamService.audioLeftPlayCountSubmit(teStudent.getId(), param.getRecordId(), param.getKey(), param.getCount()));
    }

    @ApiOperation(value = "文件上传")
    @RequestMapping(value = "/file/upload", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result fileUpload(@ApiParam(value = "考试记录ID", required = true) @RequestParam Long recordId,
                             @ApiParam(value = "上传文件", required = true) @RequestParam MultipartFile file,
                             @ApiParam(value = "后缀名", required = true) @RequestParam String suffix,
                             @ApiParam(value = "md5", required = true) @RequestParam String md5) {
        TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
        return ResultUtil.ok(teExamService.fileUpload(teStudent.getId(), recordId, file, suffix, md5));
    }

    @ApiOperation(value = "断点恢复")
    @RequestMapping(value = "/resume", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result resume(@RequestBody ResumeParamBean param) {
        TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
        String lockKey = SystemConstant.REDIS_LOCK_STUDENT_PREFIX + teStudent.getId();
        Boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_CACHE_TIME_OUT);
        if (!lock) {
            throw new BusinessException(ExceptionResultEnum.REQUEST_AWAIT);
        }
        try {
            if (param.getRecordId() == null) {
                throw new BusinessException("考试记录id不能为空");
            }
            return ResultUtil.ok(teExamService.resume(teStudent.getId(), param.getRecordId()));
        } finally {
            redisUtil.releaseLock(lockKey);
        }
    }

    @ApiOperation(value = "结束考试")
    @RequestMapping(value = "/finish", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result finish(@RequestBody FinishParamBean param) {
        TEStudentCacheDto teStudent = (TEStudentCacheDto) ServletUtil.getRequestStudentAccount();
        String lockKey = SystemConstant.REDIS_LOCK_STUDENT_PREFIX + teStudent.getId();
        Boolean lock = redisUtil.lock(lockKey, SystemConstant.REDIS_CACHE_TIME_OUT);
        if (!lock) {
            throw new BusinessException(ExceptionResultEnum.REQUEST_AWAIT);
        }
        try {
            if (param.getRecordId() == null) {
                throw new BusinessException("考试记录id不能为空");
            }
            if (param.getType() == null) {
                throw new BusinessException("结束类型不能为空");
            }
            if (param.getDurationSeconds() == null) {
                throw new BusinessException("总用时秒数不能为空");
            }
            Result re = ResultUtil.ok(teExamService.finish(teStudent.getId(), param.getRecordId(), param.getType(), param.getDurationSeconds()));
            ExamConstant.sendExamStopMsg(param.getRecordId(), false);
            return re;
        } finally {
            redisUtil.releaseLock(lockKey);
        }
    }


    @ApiOperation(value = "查询交卷结果")
    @RequestMapping(value = "/result", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "试卷信息")})
    public Result result(@RequestBody ResultParamBean param) {
        if (param.getRecordId() == null) {
            throw new BusinessException("考试记录id不能为空");
        }
        return ResultUtil.ok(teExamService.result(param.getRecordId()));
    }
}
