package com.qmth.themis.admin.api;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Sets;
import com.qmth.themis.business.annotation.ApiJsonObject;
import com.qmth.themis.business.annotation.ApiJsonProperty;
import com.qmth.themis.business.bean.mobile.MobileAuthorizationMonitorBean;
import com.qmth.themis.business.cache.ExamRecordCacheUtil;
import com.qmth.themis.business.cache.bean.ExamCacheBean;
import com.qmth.themis.business.cache.bean.ExamStudentCacheBean;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.AuthDto;
import com.qmth.themis.business.dto.MqDto;
import com.qmth.themis.business.dto.response.TIeExamInvigilateCallDto;
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.contanst.Constants;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.enums.Source;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.Result;
import com.qmth.themis.common.util.ResultUtil;
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

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

/**
 * @Description: mobile监考监控通话信息 前端控制器
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2020/6/25
 */
@Api(tags = "mobile监考监控通话信息Controller")
@RestController
@RequestMapping(SystemConstant.PREFIX_URL_ADMIN + "/monitor")
@Validated
public class TIeInvigilateCallMobileController {
    private final static Logger log = LoggerFactory.getLogger(TIeInvigilateCallMobileController.class);

    @Resource
    RedisUtil redisUtil;

    @Resource
    MqDtoService mqDtoService;

    @Resource
    TIeExamInvigilateCallService tIeExamInvigilateCallService;

    @Resource
    TencentYunUtil tencentYunUtil;

    @Resource
    OssUtil ossUtil;

    @Resource
    TOeExamRecordService tOeExamRecordService;

    @Resource
    TEExamStudentService teExamStudentService;

    @Resource
    MqUtil mqUtil;

    @Resource
    ThemisCacheService themisCacheService;

    @Resource
    TEExamService teExamService;

    @ApiOperation(value = "监考监控通话查询接口")
    @RequestMapping(value = "/call/list", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = TIeExamInvigilateCallDto.class)})
    public Result callList(@ApiParam(value = "考试批次id", required = true) @RequestParam(required = true) Long examId,
                           @ApiParam(value = "通话状态", required = false) @RequestParam(required = false) String callStatus,
                           @ApiParam(value = "虚拟考场代码", required = false) @RequestParam(required = false) String roomCode,
                           @ApiParam(value = "分页页码", required = true) @RequestParam @Min(SystemConstant.PAGE_NUMBER_MIN) int pageNumber,
                           @ApiParam(value = "分页数", required = true) @RequestParam @Min(SystemConstant.PAGE_SIZE_MIN) @Max(SystemConstant.PAGE_SIZE_MAX) int pageSize) {
        if (Objects.isNull(examId) || Objects.equals(examId, "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
        }
        List<String> callStatusList = new ArrayList<>();
        if (Objects.nonNull(callStatus) && !Objects.equals(callStatus, "")) {
            if (callStatus.contains(",")) {
                String[] callStatusStrs = callStatus.split(",");
                callStatusList = Arrays.asList(callStatusStrs);
            } else {
                MonitorCallStatusSourceEnum callStatusEnum = MonitorCallStatusSourceEnum.valueOf(callStatus);
                callStatusList.add(callStatusEnum.name());
            }
        }
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        AuthDto authDto = themisCacheService.addAccountAuthCache(tbUser.getId());
        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
        Long userId = null;
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
            userId = tbUser.getId();
        }
        IPage<TIeExamInvigilateCallDto> tIeExamInvigilateCallIPage = tIeExamInvigilateCallService
                .examInvigilateCallQuery(new Page<>(pageNumber, pageSize), examId, userId, tbUser.getOrgId(),
                        MonitorStatusSourceEnum.START.name(), callStatusList, roomCode, callStatus);
        tIeExamInvigilateCallIPage.getRecords().forEach(s -> {
            s.setBasePhotoPath(
                    ossUtil.getAliYunOssPublicDomain().getPublicUrl() + File.separator + s.getBasePhotoPath());
            try {
                s.setSourceUserId(this.getSourceUserId(s.getExamRecordId()));
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
        });
        return ResultUtil.ok(tIeExamInvigilateCallIPage);
    }

    @ApiOperation(value = "监考监控通话提醒接口")
    @RequestMapping(value = "/call/count", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = Integer.class)})
    public Result callCount(@ApiParam(value = "考试批次id", required = true) @RequestParam Long examId,
                            @ApiParam(value = "通话状态", required = false) @RequestParam(required = false) String callStatus,
                            @ApiParam(value = "虚拟考场代码", required = false) @RequestParam(required = false) String roomCode) {
        if (Objects.isNull(examId) || Objects.equals(examId, "")) {
            throw new BusinessException(ExceptionResultEnum.EXAM_ID_IS_NULL);
        }
        List<String> callStatusList = new ArrayList<>();
        if (Objects.nonNull(callStatus) && !Objects.equals(callStatus, "")) {
            if (callStatus.contains(",")) {
                String[] callStatusStrs = callStatus.split(",");
                callStatusList = Arrays.asList(callStatusStrs);
            } else {
                MonitorCallStatusSourceEnum callStatusEnum = MonitorCallStatusSourceEnum.valueOf(callStatus);
                callStatusList.add(callStatusEnum.name());
            }
        }
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        AuthDto authDto = themisCacheService.addAccountAuthCache(tbUser.getId());
        //如果有监考员角色，只能查看自己所监考的考场，巡考员和管理员则可以查看全部考场
        Long userId = null;
        if (authDto.getRoleCodes().toString().contains(RoleEnum.INVIGILATE.name())) {
            userId = tbUser.getId();
        }
        int count = tIeExamInvigilateCallService
                .examInvigilateCallQueryCount(examId, userId, tbUser.getOrgId(), MonitorStatusSourceEnum.START.name(),
                        callStatusList, roomCode, callStatus);
        return ResultUtil.ok(Collections.singletonMap("count", count));
    }

    @ApiOperation(value = "监考监控通话查询来源接口")
    @RequestMapping(value = "/call/query", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "监考监控信息", response = TIeExamInvigilateCall.class)})
    public Result callQuery(@ApiParam(value = "考试记录id", required = true) @RequestParam(required = true) Long recordId) {
        QueryWrapper<TIeExamInvigilateCall> tIeExamInvigilateCallQueryWrapper = new QueryWrapper<>();
        tIeExamInvigilateCallQueryWrapper.lambda().eq(TIeExamInvigilateCall::getExamRecordId, recordId);
        List<TIeExamInvigilateCall> tIeExamInvigilateCallTempList = tIeExamInvigilateCallService.list(tIeExamInvigilateCallQueryWrapper);

        String monitorRecord = ExamRecordCacheUtil.getMonitorVideoSource(recordId);
        if (Objects.isNull(monitorRecord)) {
            TOeExamRecord tOeExamRecord = SystemConstant.getExamRecord(recordId);
            monitorRecord = tOeExamRecord.getMonitorVideoSource();
        }
        Map<String, String> monitorRecordMap = SystemConstant.getMonitorRecordMap(monitorRecord);
        List<TIeExamInvigilateCall> tIeExamInvigilateCallList = new ArrayList<>();
        for (TIeExamInvigilateCall t : tIeExamInvigilateCallTempList) {
            if (!CollectionUtils.isEmpty(monitorRecordMap) && Objects.nonNull(monitorRecordMap.get(t.getSource().name()))) {
                tIeExamInvigilateCallList.add(t);
            }
        }
        return ResultUtil.ok(tIeExamInvigilateCallList);
    }

    @ApiOperation(value = "通话中接口")
    @RequestMapping(value = "/call/calling", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    @Transactional
    public Result callCalling(@ApiJsonObject(name = "callCallingAdminMobile", value = {
            @ApiJsonProperty(key = "recordId", type = "long", example = "1", description = "考试记录id", required = true),
            @ApiJsonProperty(key = "source", description = "监考视频源", required = true)}) @ApiParam(value = "监控信息", required = true) @RequestBody Map<String, Object> mapParameter) {
        if (Objects.isNull(mapParameter.get(SystemConstant.RECORD_ID)) || Objects.equals(mapParameter.get(SystemConstant.RECORD_ID), "")) {
            throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
        }
        Long recordId = Long.parseLong(String.valueOf(mapParameter.get(SystemConstant.RECORD_ID)));
        if (Objects.isNull(mapParameter.get(Constants.HEADER_SOURCE)) || Objects.equals(mapParameter.get(Constants.HEADER_SOURCE), "")) {
            throw new BusinessException("来源不能为空!");
        }
        MonitorVideoSourceEnum source = MonitorVideoSourceEnum.valueOf(String.valueOf(mapParameter.get(Constants.HEADER_SOURCE)));
        SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.MONITOR_CONFIG_PREFIX);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置监控前缀"));

        //获取考试记录缓存
        String liveUrl = SystemConstant.setStreamId(sysConfig.getConfigValue(), recordId, source);
        if (Objects.isNull(ExamRecordCacheUtil.getMonitorStatus(recordId, source))) {
            throw new BusinessException("推流状态为空");
        }
        //2022/04/21加入校验
        String monitorRecord = ExamRecordCacheUtil.getMonitorVideoSource(recordId);
        if (Objects.isNull(monitorRecord)) {
            TOeExamRecord tOeExamRecord = SystemConstant.getExamRecord(recordId);
            monitorRecord = tOeExamRecord.getMonitorVideoSource();
        }
        Map<String, String> monitorRecordMap = SystemConstant.getMonitorRecordMap(monitorRecord);
        if (Objects.isNull(monitorRecordMap)) {
            return ResultUtil.ok();
        } else {
            String monitorRecordStr = monitorRecordMap.get(source.name());
            if (Objects.isNull(monitorRecordStr)) {
                return ResultUtil.ok();
            }
        }
        Long timestamp = System.currentTimeMillis();
        ExamRecordCacheUtil.setMonitorCallStatus(recordId, source, MonitorCallStatusSourceEnum.CALLING, timestamp);
        tOeExamRecordService.sendExamRecordDataSaveMq(recordId, timestamp);
        String monitorKey = ExamRecordCacheUtil.getMonitorKey(recordId);
        MonitorStatusSourceEnum status = ExamRecordCacheUtil.getMonitorStatus(recordId, source);
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        TIeExamInvigilateCallLog tIeExamInvigilateCallLog = new TIeExamInvigilateCallLog(recordId, source, liveUrl, status, monitorKey, MonitorCallStatusSourceEnum.CALLING, ExamRecordCacheUtil.getExamStudentId(recordId), tbUser.getId());
        //监考监控通话信息 发送mq start
        MqDto mqDto = new MqDto(mqUtil.getTopic(), MqTagEnum.MONITOR_LOG.name(),
                tIeExamInvigilateCallLog, MqTagEnum.MONITOR_LOG, String.valueOf(tIeExamInvigilateCallLog.getId()),
                source.name());
        mqDtoService.assembleSendAsyncMsg(mqDto);
        //监考监控通话信息 发送mq end
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "撤销通话申请接口")
    @RequestMapping(value = "/call/cancel", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    @Transactional
    public Result callCancel(@ApiJsonObject(name = "callCancelAdminMobile", value = {
            @ApiJsonProperty(key = "recordId", type = "long", example = "1", description = "考试记录id", required = true),
            @ApiJsonProperty(key = "source", description = "监考视频源", required = true)}) @ApiParam(value = "监控信息", required = true) @RequestBody Map<String, Object> mapParameter) {
        if (Objects.isNull(mapParameter.get(SystemConstant.RECORD_ID)) || Objects.equals(mapParameter.get(SystemConstant.RECORD_ID), "")) {
            throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
        }
        Long recordId = Long.parseLong(String.valueOf(mapParameter.get(SystemConstant.RECORD_ID)));
        if (Objects.isNull(mapParameter.get(Constants.HEADER_SOURCE)) || Objects.equals(mapParameter.get(Constants.HEADER_SOURCE), "")) {
            throw new BusinessException("来源不能为空!");
        }
        MonitorVideoSourceEnum source = MonitorVideoSourceEnum.valueOf(String.valueOf(mapParameter.get(Constants.HEADER_SOURCE)));

        SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.MONITOR_CONFIG_PREFIX);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置监控前缀"));
        //获取考试记录缓存
        String liveUrl = SystemConstant.setStreamId(sysConfig.getConfigValue(), recordId, source);
        ExamRecordStatusEnum statusEnum = ExamRecordCacheUtil.getStatus(recordId);
        if (Objects.isNull(statusEnum) || Objects.equals(statusEnum, ExamRecordStatusEnum.FINISHED) || Objects
                .equals(statusEnum, ExamRecordStatusEnum.PERSISTED) || statusEnum == ExamRecordStatusEnum.JUNK) {
            return ResultUtil.ok(true);
        } else if (Objects.isNull(ExamRecordCacheUtil.getMonitorStatus(recordId, source))) {
            throw new BusinessException("推流状态为空");
        }
        //2022/04/21加入校验
        String monitorRecord = ExamRecordCacheUtil.getMonitorVideoSource(recordId);
        if (Objects.isNull(monitorRecord)) {
            TOeExamRecord tOeExamRecord = SystemConstant.getExamRecord(recordId);
            monitorRecord = tOeExamRecord.getMonitorVideoSource();
        }
        Map<String, String> monitorRecordMap = SystemConstant.getMonitorRecordMap(monitorRecord);
        if (Objects.isNull(monitorRecordMap)) {
            return ResultUtil.ok();
        } else {
            String monitorRecordStr = monitorRecordMap.get(source.name());
            if (Objects.isNull(monitorRecordStr)) {
                return ResultUtil.ok();
            }
        }

        Long timestamp = System.currentTimeMillis();
        ExamRecordCacheUtil.setMonitorCallStatus(recordId, source, MonitorCallStatusSourceEnum.STOP, timestamp);
        tOeExamRecordService.sendExamRecordDataSaveMq(recordId, timestamp);
        String monitorKey = ExamRecordCacheUtil.getMonitorKey(recordId);
        MonitorStatusSourceEnum status = ExamRecordCacheUtil.getMonitorStatus(recordId, source);
        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        TIeExamInvigilateCallLog tIeExamInvigilateCallLog = new TIeExamInvigilateCallLog(recordId, source, liveUrl, status, monitorKey, MonitorCallStatusSourceEnum.STOP, ExamRecordCacheUtil.getExamStudentId(recordId), tbUser.getId());
        //监考监控通话信息 发送mq start
        MqDto mqDto = new MqDto(mqUtil.getTopic(), MqTagEnum.MONITOR_LOG.name(),
                tIeExamInvigilateCallLog, MqTagEnum.MONITOR_LOG, String.valueOf(tIeExamInvigilateCallLog.getId()),
                source.name());
        mqDtoService.assembleSendAsyncMsg(mqDto);
        //监考监控通话信息 发送mq end
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "监考获取monitorKey接口")
    @RequestMapping(value = "/getMonitorKey", method = RequestMethod.POST)
    @ApiResponses({
            @ApiResponse(code = 200, message = "获取监考monitorKey", response = MobileAuthorizationMonitorBean.class)})
    public Result getMonitorKey(@ApiParam(value = "考试记录id", required = true) @RequestParam Long recordId)
            throws NoSuchAlgorithmException {
        TBSession tbSession = (TBSession) ServletUtil.getRequestSession();
        String monitorKey = ExamRecordCacheUtil.getMonitorKey(recordId);
        String monitorUserId = null;
        String sourceUserId = this.getSourceUserId(recordId);
        monitorUserId = "m_" + tbSession.getId();
        String monitorUserSig = tencentYunUtil.getSign(monitorUserId, SystemConstant.TENCENT_EXPIRE_TIME);
        Map<String, Object> map = new HashMap<>();
        map.put("monitorKey", monitorKey);
        map.put("monitorUserId", monitorUserId);
        map.put("sourceUserId", sourceUserId);
        map.put("monitorUserSig", monitorUserSig);
        map.put("appId", tencentYunUtil.getAppId());
        map.put("key", tencentYunUtil.getKey());

        TBUser tbUser = (TBUser) ServletUtil.getRequestAccount();
        Long examId = ExamRecordCacheUtil.getExamId(recordId);
        Optional.ofNullable(examId).orElseThrow(() -> new BusinessException("该考生已交卷"));
        ExamCacheBean examCacheBean = teExamService.getExamCacheBean(examId);
        String monitorVideoSource = examCacheBean.getMonitorVideoSource();
        MonitorVideoSourceEnum source = null;
        if (Objects.nonNull(monitorVideoSource) && (!monitorVideoSource.toUpperCase().contains(MonitorVideoSourceEnum.CLIENT_SCREEN.name())
                && !monitorVideoSource.toUpperCase().contains(MonitorVideoSourceEnum.CLIENT_CAMERA.name())
                && monitorVideoSource.toUpperCase().contains(MonitorVideoSourceEnum.MOBILE_FIRST.name()))) {
            source = MonitorVideoSourceEnum.MOBILE_FIRST;
        } else if (Objects.nonNull(monitorVideoSource) && (monitorVideoSource.toUpperCase().contains(MonitorVideoSourceEnum.CLIENT_SCREEN.name())
                || monitorVideoSource.toUpperCase()
                .contains(MonitorVideoSourceEnum.CLIENT_CAMERA.name()))) {
            source = MonitorVideoSourceEnum.CLIENT_CAMERA;
        }
        SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.MONITOR_CONFIG_PREFIX);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置监控前缀"));
        String liveUrl = SystemConstant.setStreamId(sysConfig.getConfigValue(), recordId, source);
//        String batchId = ExamInvigilateCallCacheUtil.getBatchId(recordId);
//        if (Objects.isNull(batchId)) {
//            batchId = SystemConstant.getNanoId();
//            ExamInvigilateCallCacheUtil.expire(recordId, batchId);
//        }
//        TIeExamInvigilateCallLog tIeExamInvigilateCallLog = new TIeExamInvigilateCallLog(recordId, source, liveUrl, MonitorStatusSourceEnum.START, monitorKey, MonitorCallStatusSourceEnum.START, tbUser.getId(), ExamRecordCacheUtil.getExamStudentId(recordId), batchId);
        TIeExamInvigilateCallLog tIeExamInvigilateCallLog = new TIeExamInvigilateCallLog(recordId, source, liveUrl, MonitorStatusSourceEnum.START, monitorKey, MonitorCallStatusSourceEnum.START, tbUser.getId(), ExamRecordCacheUtil.getExamStudentId(recordId));

        //监考监控通话信息 发送mq start
        MqDto mqDto = new MqDto(mqUtil.getTopic(), MqTagEnum.MONITOR_LOG.name(), tIeExamInvigilateCallLog, MqTagEnum.MONITOR_LOG, String.valueOf(tIeExamInvigilateCallLog.getId()), source.name());
        mqDtoService.assembleSendAsyncMsg(mqDto);
        //监考监控通话信息 发送mq end
        return ResultUtil.ok(map);
    }

    /**
     * 获取来源userId
     *
     * @param recordId
     * @return
     * @throws NoSuchAlgorithmException
     */
    private String getSourceUserId(Long recordId) throws NoSuchAlgorithmException {
        String sourceUserId = null;
        //Long examId = ExamRecordCacheUtil.getExamId(recordId);
        Long examStudentId = ExamRecordCacheUtil.getExamStudentId(recordId);
        if (Objects.isNull(examStudentId)) {
            TOeExamRecord tOeExamRecord = tOeExamRecordService.getById(recordId);
            //examId = tOeExamRecord.getExamId();
            if (tOeExamRecord == null) {
                throw new BusinessException(ExceptionResultEnum.NOT_FOUND_EXAM_RECORD);
            }
            examStudentId = tOeExamRecord.getExamStudentId();
        }
        //ExamCacheBean ec = teExamService.getExamCacheBean(examId);//考试缓存
        ExamStudentCacheBean examStudentCacheBean = teExamStudentService.getExamStudentCacheBean(examStudentId);
        //只针对客户端摄像头/屏幕、移动端主机位开启监考通话
        List<MonitorVideoSourceEnum> sourceList = Arrays
                .asList(MonitorVideoSourceEnum.CLIENT_CAMERA, MonitorVideoSourceEnum.CLIENT_SCREEN,
                        MonitorVideoSourceEnum.MOBILE_FIRST);
        for (MonitorVideoSourceEnum source : sourceList) {
            Source sessionSource = source.getSessionSource();
            String sessionId = SessionUtil.digest(examStudentCacheBean.getStudentId(),
                    Math.abs(Sets.newHashSet(RoleEnum.STUDENT.name()).toString().hashCode()), sessionSource.name());
            TBSession tbSession = (TBSession) redisUtil.getUserSession(sessionId);
            if (Objects.nonNull(tbSession) && tbSession.getExpireTime() > System.currentTimeMillis() && Objects
                    .nonNull(ExamRecordCacheUtil.getMonitorStatus(recordId, source))) {
                sourceUserId = "s_" + tbSession.getId();
                break;
            }
        }
        return sourceUserId;
    }
}
