package com.qmth.themis.admin.api;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.reflect.TypeToken;
import com.qmth.boot.core.solar.config.SolarProperties;
import com.qmth.boot.core.solar.service.SolarService;
import com.qmth.themis.admin.config.DictionaryConfig;
//import com.qmth.themis.business.annotation.RedisLimitAnnotation;
import com.qmth.themis.business.bean.admin.DataCountBean;
import com.qmth.themis.business.bean.admin.MapDataCountBean;
import com.qmth.themis.business.bean.admin.OrgDataCountBean;
import com.qmth.themis.business.bean.result.VideoQueryResult;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.AuthDto;
import com.qmth.themis.business.dto.response.RoomCodeQueryDto;
import com.qmth.themis.business.dto.response.TEExamQueryDto;
import com.qmth.themis.business.dto.response.TEStudentMonitorRecordDto;
import com.qmth.themis.business.entity.*;
import com.qmth.themis.business.enums.*;
import com.qmth.themis.business.service.*;
import com.qmth.themis.business.util.*;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.GsonUtil;
import com.qmth.themis.common.util.Result;
import com.qmth.themis.common.util.ResultUtil;
import com.tencentcloudapi.vod.v20180717.models.MediaBasicInfo;
import com.tencentcloudapi.vod.v20180717.models.MediaInfo;
import com.tencentcloudapi.vod.v20180717.models.SearchMediaResponse;
import io.swagger.annotations.*;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * @Description: 系统信息 前端控制器
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2020/6/27
 */
@Api(tags = "系统信息Controller")
@RestController
@RequestMapping(SystemConstant.PREFIX_URL_ADMIN + "/sys")
public class SysController {

    @Resource
    TBPrivilegeService tbPrivilegeService;

    @Resource
    TBAttachmentService tbAttachmentService;

    @Resource
    TBTaskHistoryService tbTaskHistoryService;

    @Resource
    DictionaryConfig dictionaryConfig;

    @Resource
    OssUtil ossUtil;

    @Resource
    TBOrgService tbOrgService;

    @Resource
    TBRoleService tbRoleService;

    @Resource
    TEExamActivityService teExamActivityService;

    @Resource
    TEExamService teExamService;

    @Resource
    TBExamInvigilateUserService tbExamInvigilateUserService;

    @Resource
    TEExamStudentService teExamStudentService;

    @Resource
    ThemisCacheService themisCacheService;

    @Resource
    TOeExamRecordService tOeExamRecordService;

    @Resource
    TencentYunUtil tencentYunUtil;

    @Resource
    RedisUtil redisUtil;

    @Resource
    TMTencentVideoMessageService tencentVideoMessageService;

    @Resource
    AuthInfoService authInfoService;

    @Resource
    TIeInvigilateWarnInfoService tIeInvigilateWarnInfoService;

    @Resource
    SolarService solarService;

    @Resource
    SolarProperties solarProperties;

    @Resource
    TMTencentVideoMessageService tmTencentVideoMessageService;

    @ApiOperation(value = "同步机构接口")
    @RequestMapping(value = "/sync/org", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "菜单信息", response = Result.class)})
    public Result syncOrg() {
        solarService.update(solarProperties.getAccessKey(), solarProperties.getAccessSecret());
        authInfoService.appInfoInit();
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "菜单查询接口")
    @RequestMapping(value = "/getMenu", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "菜单信息", response = TBPrivilege.class)})
    public Result getMenu() {
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        if (Objects.isNull(tbUser)) {
            throw new BusinessException(ExceptionResultEnum.LOGIN_NO);
        }
        return ResultUtil.ok(tbPrivilegeService.getMenu(tbUser.getId()));
    }

    @ApiOperation(value = "获取环境接口")
    @RequestMapping(value = "/env", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "环境信息", response = Result.class)})
    public Result env(@ApiParam(value = "机构id", required = true) @RequestParam Long orgId) {
        if (Objects.isNull(orgId) || Objects.equals(orgId, "")) {
            throw new BusinessException(ExceptionResultEnum.ORG_ID_IS_NULL);
        }
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "上传文件接口")
    @RequestMapping(value = "/file/upload", method = RequestMethod.POST)
    @Transactional
    @ApiResponses({@ApiResponse(code = 200, message = "{\"id\":0}", response = Result.class)})
    public Result fileUpload(@ApiParam(value = "上传文件", required = true) @RequestParam MultipartFile file,
                             @ApiParam(value = "上传文件类型", required = true) @RequestParam UploadFileEnum type) throws IOException {
        if (Objects.isNull(file) || Objects.equals(file, "")) {
            throw new BusinessException(ExceptionResultEnum.ATTACHMENT_IS_NULL);
        }
        if (Objects.isNull(type) || Objects.equals(type, "")) {
            throw new BusinessException(ExceptionResultEnum.FILE_TYPE_IS_NULL);
        }
        Map<String, Object> mapParameter = null;
        if (type.getId() == UploadFileEnum.file.getId()) {
            throw new BusinessException("只能上传upload附件");
        }
        mapParameter = ossUtil.getAliYunOssPublicDomain().getMap();
        mapParameter.put(SystemConstant.UPLOAD_TYPE, UploadFileEnum.upload.name());
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        TBAttachment tbAttachment = tbAttachmentService
                .saveAttachment(file, ServletUtil.getRequestMd5(), ServletUtil.getRequestPath(), mapParameter, type,
                        tbUser.getOrgId(), tbUser.getId());
        if (Objects.isNull(tbAttachment)) {
            throw new BusinessException(ExceptionResultEnum.ATTACHMENT_ERROR);
        }
        JSONObject jsonObject = JSONObject.parseObject(tbAttachment.getRemark());
        String attachmentType = String.valueOf(jsonObject.get(SystemConstant.TYPE));
        String url = null;
        String filePath = (String) jsonObject.get(SystemConstant.PATH);

        SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.ADMIN_FILE_HOST);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置admin文件host"));
        if (Objects.equals(attachmentType, SystemConstant.LOCAL)) {
            url = "http://" + sysConfig.getConfigValue() + File.separator + filePath;
        } else {
            if (Objects.nonNull(mapParameter.get(SystemConstant.UPLOAD_TYPE))) {
                Integer fileId = UploadFileEnum.convertToId((String) mapParameter.get(SystemConstant.UPLOAD_TYPE));
                if (fileId.intValue() == 3) {
                    url = dictionaryConfig.aliYunOssDomain().getPrivateUrl() + File.separator + filePath;
                } else {
                    url = dictionaryConfig.aliYunOssDomain().getPublicUrl() + File.separator + filePath;
                }
            }
        }
        Map map = new HashMap();
        map.put(SystemConstant.ID, String.valueOf(tbAttachment.getId()));
        map.put(SystemConstant.NAME, tbAttachment.getName());
        map.put(SystemConstant.TYPE, tbAttachment.getType());
        map.put(SystemConstant.URL, url);
        return ResultUtil.ok(map);
    }

    @ApiOperation(value = "文件下载接口")
    @RequestMapping(value = "/file/download", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"url\":string}", response = Result.class)})
    public Result fileDownload(@ApiParam(value = "任务id", required = true) @RequestParam Long id,
                               @ApiParam(value = "下载文件类型", required = true) @RequestParam DownloadFileEnum type) {
        if (Objects.isNull(id) || Objects.equals(id, "")) {
            throw new BusinessException(ExceptionResultEnum.TASK_ID_IS_NULL);
        }
        if (Objects.isNull(type) || Objects.equals(type, "")) {
            throw new BusinessException(ExceptionResultEnum.FILE_TYPE_IS_NULL);
        }
        if (Objects.isNull(DownloadFileEnum.convertToName(type.getId()))) {
            throw new BusinessException(ExceptionResultEnum.DOWNLOAD_FILE_TYPE_ERROR);
        }
        TBTaskHistory tbTaskHistory = tbTaskHistoryService.getById(id);
        if (Objects.isNull(tbTaskHistory)) {
            throw new BusinessException(ExceptionResultEnum.TASK_NO);
        }
        boolean oss = dictionaryConfig.sysDomain().isOss();
        JSONObject jsonObject = null;
        switch (type.getId()) {
            case 0:
                jsonObject = JSONObject.parseObject(tbTaskHistory.getImportFilePath());
                break;
            case 1:
                jsonObject = JSONObject.parseObject(tbTaskHistory.getReportFilePath());
                break;
            default:
                jsonObject = JSONObject.parseObject(tbTaskHistory.getResultFilePath());
                break;
        }
        if (Objects.isNull(jsonObject) || Objects.isNull(jsonObject.get(SystemConstant.PATH))) {
            throw new BusinessException("下载文件地址不存在");
        }
        String filePath = (String) jsonObject.get(SystemConstant.PATH);
        String url = null;
        SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.ADMIN_FILE_HOST);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置admin文件host"));
        if (oss) {
            Integer uploadType = (Integer) jsonObject.get(SystemConstant.UPLOAD_TYPE);
            if (uploadType == UploadFileEnum.file.ordinal()) {
                url = ossUtil.getPrivateUrl(filePath);
            } else {
                url = dictionaryConfig.aliYunOssDomain().getPublicUrl() + File.separator + filePath;
            }
        } else {
            url = "http://" + sysConfig.getConfigValue() + File.separator + filePath;
        }
        return ResultUtil.ok(Collections.singletonMap(SystemConstant.URL, url));
    }

    @ApiOperation(value = "根据机构代码查询机构信息接口")
    @RequestMapping(value = "/org/queryByOrgCode", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "机构信息", response = TBOrg.class)})
    public Result queryByOrgCode(@ApiParam(value = "机构code", required = true) @RequestParam String code) {
        Optional.ofNullable(code).orElseThrow(() -> new BusinessException(ExceptionResultEnum.ORG_CODE_IS_NULL));
        Map<String, Object> map = new HashMap<>();
        map.put(SystemConstant.VERSION, SystemConstant.VERSION_VALUE);
        if (!Objects.equals(code.toUpperCase(), SystemConstant.ADMIN)) {
            TBOrg tbOrg = themisCacheService.addOrgCodeCache(code);
            Optional.ofNullable(tbOrg).orElseThrow(() -> new BusinessException(ExceptionResultEnum.ORG_NO));
            authInfoService.appHasExpired(code);
            map.put("logo", tbOrg.getLogo());
        } else {
            SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.ADMIN_LOGO_URL);
            Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置超级管理员logo"));
            map.put("logo", sysConfig.getConfigValue());
        }
        return ResultUtil.ok(map);
    }

    @ApiOperation(value = "机构查询接口")
    @RequestMapping(value = "/org/query", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "机构信息", response = TBOrg.class)})
    public Result query(@ApiParam(value = "机构id", required = false) @RequestParam(required = false) Long id,
                        @ApiParam(value = "机构代码", required = false) @RequestParam(required = false) String code,
                        @ApiParam(value = "机构名称", required = false) @RequestParam(required = false) String name,
                        @ApiParam(value = "是否启用", required = false) @RequestParam(required = false) Integer enable) {
        QueryWrapper<TBOrg> tbOrgQueryWrapper = new QueryWrapper<>();
        if (Objects.nonNull(id)) {
            tbOrgQueryWrapper.lambda().eq(TBOrg::getId, id);
        }
        if (Objects.nonNull(code)) {
            tbOrgQueryWrapper.lambda().eq(TBOrg::getCode, code);
        }
        if (Objects.nonNull(name)) {
            tbOrgQueryWrapper.lambda().like(TBOrg::getName, name);
        }
        if (Objects.nonNull(enable)) {
            tbOrgQueryWrapper.lambda().eq(TBOrg::getEnable, enable);
        }
        tbOrgQueryWrapper.lambda().orderByAsc(TBOrg::getCode);
        return ResultUtil.ok(tbOrgService.list(tbOrgQueryWrapper));
    }

    @ApiOperation(value = "角色查询接口")
    @RequestMapping(value = "/role/query", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "角色信息", response = TBRole.class)})
    @Cacheable(value = SystemConstant.roleCache, key = "methodName", unless = "#result == null")
    public Result roleList() {
        QueryWrapper<TBRole> tbRoleQueryWrapper = new QueryWrapper<>();
        tbRoleQueryWrapper.lambda().notIn(TBRole::getRoleCode, RoleEnum.SUPER_ADMIN.name(), RoleEnum.STUDENT.name());
        tbRoleQueryWrapper.lambda().orderByAsc(TBRole::getRoleCode);
        return ResultUtil.ok(tbRoleService.list(tbRoleQueryWrapper));
    }

    @ApiOperation(value = "考试场次查询接口")
    @RequestMapping(value = "/examActivity/query", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "角色信息", response = TEExamActivity.class)})
    public Result examActivityQuery() {
        return ResultUtil.ok(teExamActivityService.list());
    }

    @ApiOperation(value = "考试批次查询接口")
    @RequestMapping(value = "/exam/query", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "角色信息", response = TEExamQueryDto.class)})
    public Result examQuery(@ApiParam(value = "用户id") @RequestParam(required = false) Long userId,
                            @ApiParam(value = "批次名称") @RequestParam(required = false) String name,
                            @ApiParam(value = "类型(区分实时监考台和考务)") @RequestParam(required = false) String type,
                            @ApiParam(value = "是否查询预警消息条数") @RequestParam(required = false) Boolean warnCount) {
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        AuthDto authDto = themisCacheService.addAccountAuthCache(tbUser.getId());
        List<TEExam> teExamList = null;
        if (authDto.getRoleCodes().toString().contains(RoleEnum.ADMIN.name()) || authDto.getRoleCodes().toString()
                .contains(RoleEnum.SUPER_ADMIN.name())) {//管理员
            QueryWrapper<TEExam> teExamQueryWrapper = new QueryWrapper<>();
            teExamQueryWrapper.lambda().eq(TEExam::getOrgId, tbUser.getOrgId()).eq(TEExam::getEnable, 1);
            if (Objects.nonNull(name) && !Objects.equals(name, "")) {
                teExamQueryWrapper.lambda().like(TEExam::getName, name);
            }
            teExamQueryWrapper.lambda().orderByDesc(TEExam::getCreateTime);
            teExamList = teExamService.list(teExamQueryWrapper);
        } else {
            if (Objects.nonNull(userId) && !Objects.equals(userId, "")) {
                teExamList = teExamService.examPrivilegeQuery(userId, tbUser.getOrgId(), type);
            } else {
                QueryWrapper<TEExam> teExamQueryWrapper = new QueryWrapper<>();
                teExamQueryWrapper.lambda().eq(TEExam::getOrgId, tbUser.getOrgId());
                if (Objects.nonNull(name) && !Objects.equals(name, "")) {
                    teExamQueryWrapper.lambda().like(TEExam::getName, name);
                }
                if (Objects.nonNull(type) && !Objects.equals(type, "") && Objects.equals(type, "monitor")) {
                    teExamQueryWrapper.lambda()
                            .ne(TEExam::getMonitorStatus, InvigilateMonitorStatusEnum.FINISHED.name());
                }
                teExamQueryWrapper.lambda().orderByDesc(TEExam::getCreateTime);
                teExamList = teExamService.list(teExamQueryWrapper);
            }
        }
        List<TEExamQueryDto> teExamQueryDtoList = null;
        if (!CollectionUtils.isEmpty(teExamList)) {
            teExamQueryDtoList = GsonUtil.fromJson(GsonUtil.toJson(teExamList), new TypeToken<List<TEExamQueryDto>>() {
            }.getType());
            if (Objects.nonNull(warnCount) && warnCount) {
                List<Long> examIdList = new ArrayList<>(teExamQueryDtoList.size());
                Map<Long, TEExamQueryDto> map = new HashMap<>(teExamQueryDtoList.size());
                for (TEExamQueryDto t : teExamQueryDtoList) {
                    examIdList.add(t.getId());
                    map.computeIfAbsent(t.getId(), v -> t);
                }
                Long warnUserId = null, warnOrgId = null;
                if (authDto.getRoleCodes().toString().contains(RoleEnum.INSPECTION.name()) || authDto.getRoleCodes().toString()
                        .contains(RoleEnum.INVIGILATE.name())) {
                    warnUserId = tbUser.getId();
                    warnOrgId = tbUser.getOrgId();
                }
                List<TEExamQueryDto> warnCountList = tIeInvigilateWarnInfoService.warningCountByList(examIdList, warnUserId, warnOrgId);
                if (!CollectionUtils.isEmpty(warnCountList)) {
                    for (TEExamQueryDto t : warnCountList) {
                        map.get(t.getId()).setWarnCount(t.getWarnCount());
                    }
                    teExamQueryDtoList = new ArrayList<>(map.values());
                }
            }
        }
        return ResultUtil.ok(teExamQueryDtoList);
    }

    @ApiOperation(value = "考场查询接口")
    @RequestMapping(value = "/examRoom/query", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "角色信息", response = RoomCodeQueryDto.class)})
    public Result examRoomQuery(
            @ApiParam(value = "考场名称", required = false) @RequestParam(required = false) String roomName) {
        return ResultUtil.ok(teExamStudentService.examRoomQuery(roomName));
    }

    @ApiOperation(value = "根据权限获取场次和考场接口")
    @RequestMapping(value = "/exam/privilegeQuery", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "场次和考场信息", response = Result.class)})
    public Result examPrivilegeQuery(@ApiParam(value = "考试id", required = true) @RequestParam Long examId) {
        if (Objects.isNull(examId) || Objects.equals(examId, "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
        }
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        TBSession tbSession = (TBSession) ServletUtil.getRequestSession();
        if (Objects.isNull(tbSession)) {
            throw new BusinessException(ExceptionResultEnum.LOGIN_NO);
        }
        //首先查询当前用户所要监控的roomCode
        QueryWrapper<TBExamInvigilateUser> examInvigilateUserQueryWrapper = new QueryWrapper<>();
        AuthDto authDto = themisCacheService.addAccountAuthCache(tbUser.getId());
        examInvigilateUserQueryWrapper.lambda().eq(TBExamInvigilateUser::getOrgId, tbUser.getOrgId())
                .eq(TBExamInvigilateUser::getExamId, examId);
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
            examInvigilateUserQueryWrapper.lambda().eq(TBExamInvigilateUser::getUserId, tbUser.getId());
        }
        examInvigilateUserQueryWrapper.lambda().last(" order by CAST(room_code AS UNSIGNED) ");
        List<TBExamInvigilateUser> tbExamInvigilateUserList = tbExamInvigilateUserService
                .list(examInvigilateUserQueryWrapper);
        Set<String> roomCodeSet = null;
        if (Objects.nonNull(tbExamInvigilateUserList) && tbExamInvigilateUserList.size() > 0) {
            Map<String, TBExamInvigilateUser> tbExamInvigilateUserMap = new LinkedHashMap<>();
            tbExamInvigilateUserList.forEach(e -> {
                tbExamInvigilateUserMap.put(e.getRoomCode(), e);
            });
            roomCodeSet = new LinkedHashSet<>();
            tbExamInvigilateUserList.clear();
            Set<String> finalRoomCodeSet = roomCodeSet;
            List<TBExamInvigilateUser> finalTbExamInvigilateUserList = tbExamInvigilateUserList;
            tbExamInvigilateUserMap.forEach((k, v) -> {
                finalRoomCodeSet.add(k);
                finalTbExamInvigilateUserList.add(v);
            });
        } else {
            tbExamInvigilateUserList = null;
        }
        List<TEExamActivity> teExamActivityList = null;
        Set<Map> courseSet = null;
        if (Objects.nonNull(roomCodeSet)) {
            //根据roomCode获取当前老师所要监考的全部应考学生数
            QueryWrapper<TEExamStudent> teExamStudentQueryWrapper = new QueryWrapper<>();
            teExamStudentQueryWrapper.lambda().eq(TEExamStudent::getExamId, examId)
                    .in(TEExamStudent::getRoomCode, roomCodeSet);
            List<TEExamStudent> teExamStudentList = teExamStudentService.list(teExamStudentQueryWrapper);
            Set<Long> examActivityIdSet = null;
            if (Objects.nonNull(teExamStudentList) && teExamStudentList.size() > 0) {
                examActivityIdSet = new HashSet<>();
                courseSet = new HashSet<>();
                Set<Long> finalExamActivityIdSet = examActivityIdSet;
                Set<Map> finalCourseSet = courseSet;
                teExamStudentList.forEach(s -> {
                    finalExamActivityIdSet.add(s.getExamActivityId());
                    Map map = new HashMap();
                    map.put(SystemConstant.COURSE_CODE, s.getCourseCode());
                    map.put(SystemConstant.COURSE_NAME, s.getCourseName());
                    finalCourseSet.add(map);
                });
                QueryWrapper<TEExamActivity> teExamActivityQueryWrapper = new QueryWrapper<>();
                teExamActivityQueryWrapper.lambda().in(TEExamActivity::getId, examActivityIdSet);
                teExamActivityQueryWrapper.lambda().last(" order by CAST(code AS UNSIGNED) ");
                teExamActivityList = teExamActivityService.list(teExamActivityQueryWrapper);
            }
        }
        Map map = new HashMap();
        map.put("examActivitys", teExamActivityList);
        map.put("examRooms", tbExamInvigilateUserList);
        map.put("examCourses", courseSet);
        return ResultUtil.ok(map);
    }

    @ApiOperation(value = "获取播放域接口")
    @RequestMapping(value = "/getPlayUrls", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "播放域接口", response = Result.class)})
    public Result getPlayUrls() {
        return ResultUtil.ok(tencentYunUtil.getUrls());
    }

    @ApiOperation(value = "查询已监考结束的考试批次接口")
    @RequestMapping(value = "/exam/finish/query", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "角色信息", response = TEExamQueryDto.class)})
    public Result examFinishQuery() {
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        QueryWrapper<TEExam> teExamQueryWrapper = new QueryWrapper<>();
        teExamQueryWrapper.lambda().eq(TEExam::getMonitorStatus, InvigilateMonitorStatusEnum.FINISHED)
                .eq(TEExam::getEnable, 1);
        if (Objects.nonNull(tbUser)) {
            teExamQueryWrapper.lambda().eq(TEExam::getOrgId, tbUser.getOrgId());
        }
        List<TEExam> teExamList = teExamService.list(teExamQueryWrapper);
        List<TEExamQueryDto> teExamQueryDtoList = null;
        if (!CollectionUtils.isEmpty(teExamList)) {
            teExamQueryDtoList = GsonUtil.fromJson(GsonUtil.toJson(teExamList), new TypeToken<List<TEExamQueryDto>>() {
            }.getType());
        }
        return ResultUtil.ok(teExamQueryDtoList);
    }

    @ApiOperation(value = "获取腾讯视频接口")
    @RequestMapping(value = "/get_tencent_video", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "腾讯视频接口", response = Result.class)})
    @Transactional
    public Result getTencentVideo(@ApiParam(value = "考试批次id", required = true) @RequestParam Long examId) {
        List<TOeExamRecord> tOeExamRecordList = tOeExamRecordService.findExamRecordByNotVideoUrl(examId);
        if (!CollectionUtils.isEmpty(tOeExamRecordList)) {
            for (TOeExamRecord t : tOeExamRecordList) {
                Map<String, Object> resultMap = tencentYunUtil.callTencentVideo(t.getId());
                if (!CollectionUtils.isEmpty(resultMap)) {
                    SearchMediaResponse response = (SearchMediaResponse) resultMap.get("object");
                    if (Objects.nonNull(response) && response.getTotalCount().intValue() > 0) {
                        MediaInfo[] mediaInfos = response.getMediaInfoSet();
                        for (int i = 0; i < mediaInfos.length; i++) {
                            MediaBasicInfo mediaBasicInfo = mediaInfos[i].getBasicInfo();
                            String mediaBasicName = mediaBasicInfo.getName();
                            String videoSource = SystemConstant.getMonitorRecordStreamId(mediaBasicName);
                            String[] timeArrays = mediaBasicName.split("_");
                            Long startTime = DateUtil.parse(timeArrays[6], SystemConstant.VIDEO_TIME_FORMAT).getTime();
                            Long endTime = DateUtil.parse(timeArrays[7], SystemConstant.VIDEO_TIME_FORMAT).getTime();
                            boolean lock = false;
                            for (int y = 0; y < SystemConstant.CONSUME_MESSAGE_BATCH_MAX_SIZE; y++) {
                                lock = redisUtil.lock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_PREFIX + videoSource, SystemConstant.REDIS_LOCK_TENCENT_VIDEO_TIME_OUT);
                                if (lock) {
                                    try {
                                        tencentVideoMessageService.saveAndCount(new TMTencentVideoMessage(response.getRequestId(), JacksonUtil.parseJson(response), t.getId(), videoSource, startTime, endTime, mediaBasicInfo.getMediaUrl()));
                                        break;
                                    } finally {
                                        redisUtil.releaseLock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_PREFIX + videoSource);
                                    }
                                } else {
                                    try {
                                        Thread.sleep(500);
                                    } catch (InterruptedException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "数据统计接口")
    @RequestMapping(value = "/data_count", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "超管数据统计接口", response = Result.class)})
//    @RedisLimitAnnotation(key = "dataCount", period = 1, count = 1)
    public Result dataCount() {
        List<Long> list = tOeExamRecordService.dataCount();
        List<OrgDataCountBean> orgDataCountBeanList = tOeExamRecordService.orgDataCount();
        List<MapDataCountBean> mapDataCountBeanList = tOeExamRecordService.mapDataCount();
        if (!CollectionUtils.isEmpty(list)) {
            return ResultUtil.ok(new DataCountBean(list, orgDataCountBeanList, mapDataCountBeanList));
        } else {
            return ResultUtil.ok(new DataCountBean(orgDataCountBeanList, mapDataCountBeanList));
        }
    }

    @ApiOperation(value = "视频存储查询接口")
    @RequestMapping(value = "/video/select", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "超管视频存储查询接口", response = VideoQueryResult.class)})
    public Result videoSelect(@ApiParam(value = "考试批次名称") @RequestParam(required = false) String examName,
                              @ApiParam(value = "机构名称") @RequestParam(required = false) String orgName,
                              @ApiParam(value = "状态，ALL：全部，TRUE：已删除，FALSE：有效") @RequestParam(required = false) VideoTypeEnum type,
                              @ApiParam(value = "分页页码", required = true) @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) int pageNumber,
                              @ApiParam(value = "分页数", required = true) @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.SELECT_PAGE_SIZE_MAX) int pageSize) {
        return ResultUtil.ok(teExamService.videoQuery(new Page<>(pageNumber, pageSize), examName, orgName, type));
    }

    @ApiOperation(value = "视频删除状态重置接口")
    @RequestMapping(value = "/video/status/reset", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "超管视频存储查询接口", response = VideoQueryResult.class)})
    public Result videoStatusReset(@ApiParam(value = "考试id", required = true) @RequestParam Long examId) {
        TEExam teExam = teExamService.getById(examId);
        if (Objects.nonNull(teExam) && Objects.nonNull(teExam.getVideoDelete()) && teExam.getVideoDelete()) {
            TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
            teExam.setVideoDelete(false);
            teExam.setUpdateId(tbUser.getId());
            teExam.setUpdateTime(System.currentTimeMillis());
            teExamService.updateById(teExam);
            teExamService.updateExamCacheBean(examId);
        }
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "视频存储删除接口")
    @RequestMapping(value = "/video/delete", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "超管视频存储删除接口", response = Result.class)})
    public Result videoDelete(@ApiParam(value = "考试id", required = true) @RequestParam Long examId) {
        //新考试视频
        List<TMTencentVideoMessage> tmTencentVideoMessageList = tmTencentVideoMessageService.videoQuery(examId);
        if (!CollectionUtils.isEmpty(tmTencentVideoMessageList)) {
            Boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_DELETE_PREFIX + examId, SystemConstant.REDIS_LOCK_TENCENT_VIDEO_DELETE_TIME_OUT);
            if (lock) {
                try {
                    int min = 0, max = SystemConstant.MAX_IMPORT_SIZE, size = tmTencentVideoMessageList.size();
                    if (max >= size) {
                        max = size;
                    }
                    while (max <= size) {
                        deleteNewMedia(tmTencentVideoMessageList.subList(min, max));
                        if (max == size) {
                            break;
                        }
                        min = max;
                        max += SystemConstant.MAX_IMPORT_SIZE;
                        if (max >= size) {
                            max = size;
                        }
                    }
                } finally {
                    redisUtil.releaseLock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_DELETE_PREFIX + examId);
                }
            } else {
                throw new BusinessException("该考试批次正在删除视频，请稍候再试");
            }
        }

        //老考试视频
        QueryWrapper<TOeExamRecord> tOeExamRecordQueryWrapper = new QueryWrapper<>();
        tOeExamRecordQueryWrapper.lambda().eq(TOeExamRecord::getExamId, examId).isNotNull(TOeExamRecord::getTencentVideoUrl)
                .and(w -> w.eq(TOeExamRecord::getVideoStatus, false).or().isNull(TOeExamRecord::getVideoStatus));
        List<TOeExamRecord> tOeExamRecordList = tOeExamRecordService.list(tOeExamRecordQueryWrapper);
        if (!CollectionUtils.isEmpty(tOeExamRecordList)) {
            Boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_DELETE_PREFIX + examId, SystemConstant.REDIS_LOCK_TENCENT_VIDEO_DELETE_TIME_OUT);
            if (lock) {
                try {
                    int min = 0, max = SystemConstant.MAX_IMPORT_SIZE, size = tOeExamRecordList.size();
                    if (max >= size) {
                        max = size;
                    }
                    while (max <= size) {
                        deleteOldMedia(tOeExamRecordList.subList(min, max));
                        if (max == size) {
                            break;
                        }
                        min = max;
                        max += SystemConstant.MAX_IMPORT_SIZE;
                        if (max >= size) {
                            max = size;
                        }
                    }
                } finally {
                    redisUtil.releaseLock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_DELETE_PREFIX + examId);
                }
            }
        }

        TEExam teExam = teExamService.getById(examId);
        Optional.ofNullable(teExam).orElseThrow(() -> new BusinessException(ExceptionResultEnum.EXAM_NO));

        Long time = System.currentTimeMillis();
        teExam.setVideoDelete(true);
        teExam.setVideoDeleteTime(time);
        teExam.setUpdateTime(time);
        teExamService.updateById(teExam);
        teExamService.updateExamCacheBean(examId);
        return ResultUtil.ok(true);
    }

    /**
     * 删除老考试视频
     *
     * @param tOeExamRecordList
     */
    @Transactional
    public void deleteOldMedia(List<TOeExamRecord> tOeExamRecordList) {
        Set<Long> tencentVideoMessageIds = new HashSet<>(tOeExamRecordList.size());
        Map<Long, TMTencentVideoMessage> tmTencentVideoMessageMap = new HashMap<>(tOeExamRecordList.size());
        for (TOeExamRecord t : tOeExamRecordList) {
            List<TEStudentMonitorRecordDto> monitorRecordList = GsonUtil.fromJson(t.getTencentVideoUrl(), new TypeToken<List<TEStudentMonitorRecordDto>>() {
            }.getType());
            for (TEStudentMonitorRecordDto teStudentMonitorRecordDto : monitorRecordList) {
                QueryWrapper<TMTencentVideoMessage> tencentVideoMessageQueryWrapper = new QueryWrapper<>();
                tencentVideoMessageQueryWrapper.lambda().like(TMTencentVideoMessage::getObject, teStudentMonitorRecordDto.getVideoSource());
                List<TMTencentVideoMessage> tencentVideoMessageList = tmTencentVideoMessageService.list(tencentVideoMessageQueryWrapper);
                for (TMTencentVideoMessage tencentVideoMessage : tencentVideoMessageList) {
                    if (!tmTencentVideoMessageMap.containsKey(tencentVideoMessage.getId())) {
                        tmTencentVideoMessageMap.put(tencentVideoMessage.getId(), tencentVideoMessage);
                    } else {
                        continue;
                    }
                    tencentVideoMessageIds.add(t.getId());
                    if (Objects.nonNull(tencentVideoMessage.getObject()) && !Objects.equals(tencentVideoMessage.getObject().trim(), "")) {
                        JSONObject jsonObject = JSONObject.parseObject(tencentVideoMessage.getObject());
                        if (Objects.nonNull(jsonObject.get("mediaInfoSet")) && !Objects.equals(jsonObject.getString("mediaInfoSet").trim(), "")) {
                            JSONArray jsonArray = JSONArray.parseArray(jsonObject.getString("mediaInfoSet"));
                            for (int i = 0; i < jsonArray.size(); i++) {
                                JSONObject object = jsonArray.getJSONObject(i);
                                if (Objects.nonNull(object.get("fileId")) && !Objects.equals(object.getString("fileId").trim(), "")) {
                                    tencentYunUtil.deleteMedia(tencentYunUtil.getSecretId(),
                                            tencentYunUtil.getSecretKey(),
                                            tencentYunUtil.getQueryUrl(),
                                            "",
                                            tencentYunUtil.getVodAppId(),
                                            object.getString("fileId"));
                                    t.setVideoStatus(true);
                                }
                            }
                        } else if (Objects.nonNull(jsonObject.get("file_id")) && !Objects.equals(jsonObject.getString("file_id").trim(), "")) {
                            tencentYunUtil.deleteMedia(tencentYunUtil.getSecretId(),
                                    tencentYunUtil.getSecretKey(),
                                    tencentYunUtil.getQueryUrl(),
                                    "",
                                    tencentYunUtil.getVodAppId(),
                                    jsonObject.getString("file_id"));
                            t.setVideoStatus(true);
                        }
                    }
                }
            }
        }
        tmTencentVideoMessageService.removeByIds(tencentVideoMessageIds);
        tOeExamRecordService.updateBatchById(tOeExamRecordList);
    }

    /**
     * 删除新考试视频
     *
     * @param tmTencentVideoMessageList
     */
    @Transactional
    public void deleteNewMedia(List<TMTencentVideoMessage> tmTencentVideoMessageList) {
        Set<Long> tencentVideoMessageIds = new HashSet<>(tmTencentVideoMessageList.size());
        List<Long> tOeExamRecordIdList = new ArrayList<>(tmTencentVideoMessageList.size());
        for (TMTencentVideoMessage t : tmTencentVideoMessageList) {
            tencentVideoMessageIds.add(t.getId());
            tOeExamRecordIdList.add(t.getExamRecordId());
            if (Objects.nonNull(t.getObject()) && !Objects.equals(t.getObject().trim(), "")) {
                JSONObject jsonObject = JSONObject.parseObject(t.getObject());
                if (Objects.nonNull(jsonObject.get("mediaInfoSet")) && !Objects.equals(jsonObject.getString("mediaInfoSet").trim(), "")) {
                    JSONArray jsonArray = JSONArray.parseArray(jsonObject.getString("mediaInfoSet"));
                    for (int i = 0; i < jsonArray.size(); i++) {
                        JSONObject object = jsonArray.getJSONObject(i);
                        if (Objects.nonNull(object.get("fileId")) && !Objects.equals(object.getString("fileId").trim(), "")) {
                            tencentYunUtil.deleteMedia(tencentYunUtil.getSecretId(),
                                    tencentYunUtil.getSecretKey(),
                                    tencentYunUtil.getQueryUrl(),
                                    "",
                                    tencentYunUtil.getVodAppId(),
                                    object.getString("fileId"));
                        }
                    }
                } else if (Objects.nonNull(jsonObject.get("file_id")) && !Objects.equals(jsonObject.getString("file_id").trim(), "")) {
                    tencentYunUtil.deleteMedia(tencentYunUtil.getSecretId(),
                            tencentYunUtil.getSecretKey(),
                            tencentYunUtil.getQueryUrl(),
                            "",
                            tencentYunUtil.getVodAppId(),
                            jsonObject.getString("file_id"));
                }
            }
        }
        tmTencentVideoMessageService.removeByIds(tencentVideoMessageIds);
        UpdateWrapper<TOeExamRecord> tOeExamRecordUpdateWrapper = new UpdateWrapper<>();
        tOeExamRecordUpdateWrapper.lambda().set(TOeExamRecord::getVideoStatus, true).in(TOeExamRecord::getId, tOeExamRecordIdList);
        tOeExamRecordService.update(tOeExamRecordUpdateWrapper);
    }
}
