package com.qmth.themis.exam.api;

import com.qmth.themis.business.annotation.ApiJsonObject;
import com.qmth.themis.business.annotation.ApiJsonProperty;
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.WebsocketDto;
import com.qmth.themis.business.entity.TIeExamInvigilateCallLog;
import com.qmth.themis.business.enums.*;
import com.qmth.themis.business.service.MqDtoService;
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.websocket.WebSocketMobileServer;
import com.qmth.themis.exam.websocket.WebSocketOeServer;
import io.swagger.annotations.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

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

    @Resource
    MqDtoService mqDtoService;

    @ApiOperation(value = "监控观看地址更新接口")
    @RequestMapping(value = "/live_url", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    @Transactional
    public Result liveUrl(@ApiJsonObject(name = "liveUrlMobile", value = {
            @ApiJsonProperty(key = "recordId", type = "long", example = "1", description = "考试记录id", required = true),
            @ApiJsonProperty(key = "source", description = "监考视频源", required = true),
            @ApiJsonProperty(key = "liveUrl", description = "观看地址", required = true)
    }) @ApiParam(value = "监控信息", 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);
        }
        Long recordId = null;
        try {
            recordId = Long.parseLong(String.valueOf(mapParameter.get("recordId")));
            if (Objects.isNull(mapParameter.get("source")) || Objects.equals(mapParameter.get("source"), "")) {
                throw new BusinessException("监考视频源不能为空");
            }
            MonitorVideoSourceEnum source = MonitorVideoSourceEnum.valueOf(String.valueOf(mapParameter.get("source")).toUpperCase());
            if (Objects.isNull(mapParameter.get("liveUrl")) || Objects.equals(mapParameter.get("liveUrl"), "")) {
                throw new BusinessException("观看地址不能为空");
            }
            ConcurrentHashMap<String, WebSocketMobileServer> webSocketMap = WebSocketMobileServer.getWebSocketMap();
            if (Objects.isNull(webSocketMap.get(recordId + "-" + source.name()))) {
                throw new BusinessException("网络连接失败");
            }
            String liveUrl = String.valueOf(mapParameter.get("liveUrl"));
            String monitorKey = ExamRecordCacheUtil.getMonitorKey(recordId);
            TIeExamInvigilateCallLog tIeExamInvigilateCallLog = new TIeExamInvigilateCallLog(recordId, source, liveUrl, MonitorStatusSourceEnum.INIT, monitorKey);
            //获取考试记录缓存
            ExamRecordCacheUtil.setMonitorLiveUrl(recordId, source.name(), liveUrl, true);

            //监考监控通话信息 发送mq start
            MqDto mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.MONITOR_LOG.name(), tIeExamInvigilateCallLog, MqTagEnum.MONITOR_LOG, String.valueOf(tIeExamInvigilateCallLog.getId()), source.name());
            mqDtoService.assembleSendOneWayMsg(mqDto);
            //监考监控通话信息 发送mq end
        } catch (Exception e) {
            log.error("请求出错", e);
            if (e instanceof DuplicateKeyException) {
                String errorColumn = e.getCause().toString();
                String columnStr = errorColumn.substring(errorColumn.lastIndexOf("key") + 3, errorColumn.length()).replaceAll("'", "");
                throw new BusinessException("考试记录id[" + recordId + "]下的" + FieldUniqueEnum.convertToCode(columnStr) + "数据不允许重复插入");
            } else if (e instanceof BusinessException) {
                throw new BusinessException(e.getMessage());
            } else {
                throw new RuntimeException(e);
            }
        }
        return ResultUtil.ok(Collections.singletonMap("updateTime", System.currentTimeMillis()));
    }

    @ApiOperation(value = "发送通话申请接口")
    @RequestMapping(value = "/call/apply", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    @Transactional
    public Result callApply(@ApiJsonObject(name = "callApplyMobile", 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("recordId")) || Objects.equals(mapParameter.get("recordId"), "")) {
            throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
        }
        Long recordId = Long.parseLong(String.valueOf(mapParameter.get("recordId")));
        if (Objects.isNull(mapParameter.get("source")) || Objects.equals(mapParameter.get("source"), "")) {
            throw new BusinessException("来源不能为空!");
        }
        MonitorVideoSourceEnum source = MonitorVideoSourceEnum.valueOf(String.valueOf(mapParameter.get("source")));
        //获取考试记录缓存
        String liveUrl = null;
        if (Objects.isNull(source) || Objects.isNull(ExamRecordCacheUtil.getMonitorStatus(recordId, source.name()))) {
            throw new BusinessException("推流状态为空");
        }
        MonitorStatusSourceEnum status = ExamRecordCacheUtil.getMonitorStatus(recordId, source.name());
        if (!Objects.equals(status, MonitorStatusSourceEnum.START)) {
            throw new BusinessException("推流状态不正常");
        }
        ExamRecordCacheUtil.setMonitorCallStatus(recordId, source.name(), MonitorCallStatusSourceEnum.START);
        String monitorKey = ExamRecordCacheUtil.getMonitorKey(recordId);
        TIeExamInvigilateCallLog tIeExamInvigilateCallLog = new TIeExamInvigilateCallLog(recordId, source, liveUrl, status, monitorKey, MonitorCallStatusSourceEnum.START);

        //监考监控通话信息 发送mq start
        MqDto mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.MONITOR_LOG.name(), tIeExamInvigilateCallLog, MqTagEnum.MONITOR_LOG, String.valueOf(tIeExamInvigilateCallLog.getId()), source.name());
        mqDtoService.assembleSendOneWayMsg(mqDto);
        //监考监控通话信息 发送mq end
        return ResultUtil.ok(Collections.singletonMap("updateTime", System.currentTimeMillis()));
    }

    @ApiOperation(value = "监控状态更新接口")
    @RequestMapping(value = "/status", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "{\"success\":true}", response = Result.class)})
    @Transactional
    public Result status(@ApiJsonObject(name = "statusMobile", value = {
            @ApiJsonProperty(key = "recordId", type = "long", example = "1", description = "考试记录id", required = true),
            @ApiJsonProperty(key = "source", description = "监考视频源", required = true),
            @ApiJsonProperty(key = "status", description = "当前状态", required = true),
            @ApiJsonProperty(key = "type", description = "异常类型，status=stop时需要传递")
    }) @ApiParam(value = "监控信息", 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);
        }
        Long recordId = Long.parseLong(String.valueOf(mapParameter.get("recordId")));
        if (Objects.isNull(mapParameter.get("source")) || Objects.equals(mapParameter.get("source"), "")) {
            throw new BusinessException("监考视频源不能为空");
        }
        MonitorVideoSourceEnum source = MonitorVideoSourceEnum.valueOf(String.valueOf(mapParameter.get("source")).toUpperCase());
        if (Objects.isNull(mapParameter.get("status")) || Objects.equals(mapParameter.get("status"), "")) {
            throw new BusinessException("监考视频状态不能为空");
        }
        MonitorStatusSourceEnum status = MonitorStatusSourceEnum.valueOf(String.valueOf(mapParameter.get("status")).toUpperCase());
        if (Objects.equals(status, MonitorStatusSourceEnum.STOP.name()) && (Objects.isNull(mapParameter.get("type")) || Objects.equals(mapParameter.get("type"), ""))) {
            throw new BusinessException("异常类型不能为空");
        }
        ConcurrentHashMap<String, WebSocketMobileServer> mobileWebSocketMap = WebSocketMobileServer.getWebSocketMap();
        if (Objects.isNull(mobileWebSocketMap.get(recordId + "-" + source.name()))) {
            throw new BusinessException("网络连接失败");
        }
        //获取考试记录缓存
        String liveUrl = null;
        if (Objects.nonNull(ExamRecordCacheUtil.getMonitorLiveUrl(recordId, source.name()))) {
            liveUrl = ExamRecordCacheUtil.getMonitorLiveUrl(recordId, source.name());
        }
        MonitorCallStatusSourceEnum callStatus = Objects.nonNull(ExamRecordCacheUtil.getMonitorCallStatus(recordId, source.name())) ? ExamRecordCacheUtil.getMonitorCallStatus(recordId, source.name()) : null;
        ExamRecordCacheUtil.setMonitorStatus(recordId, source.name(), status, true);
        ExamRecordCacheUtil.setMonitorCallStatus(recordId, source.name(), callStatus);
        String monitorKey = ExamRecordCacheUtil.getMonitorKey(recordId);
        TIeExamInvigilateCallLog tIeExamInvigilateCallLog = new TIeExamInvigilateCallLog(recordId, source, liveUrl, MonitorStatusSourceEnum.START, monitorKey, callStatus);
        if (Objects.nonNull(mapParameter.get("type")) && !Objects.equals(mapParameter.get("type"), "")) {
            tIeExamInvigilateCallLog.setType(ExceptionEnum.valueOf(String.valueOf(mapParameter.get("type"))));
        }

        //监考监控通话信息 发送mq start
        MqDto mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.MONITOR_LOG.name(), tIeExamInvigilateCallLog, MqTagEnum.MONITOR_LOG, String.valueOf(tIeExamInvigilateCallLog.getId()), source.name());
        mqDtoService.assembleSendOneWayMsg(mqDto);
        //监考监控通话信息 发送mq end

        ConcurrentHashMap<Long, WebSocketOeServer> webSocketMap = WebSocketOeServer.getWebSocketMap();
        if (Objects.nonNull(webSocketMap.get(recordId)) && Objects.equals(status.name(), MonitorStatusSourceEnum.START.name())) {
            WebSocketOeServer webSocketOeServer = webSocketMap.get(recordId);
            Map map = new HashMap<>();
            map.put(SystemConstant.RECORD_ID, recordId);
            map.put("source", source.name());
            WebsocketDto websocketDto = new WebsocketDto(WebsocketTypeEnum.MOBILE_MONITOR_START.name(), map);
            webSocketOeServer.sendMessage(websocketDto);
        } else if (Objects.nonNull(webSocketMap.get(recordId)) && Objects.equals(status.name(), MonitorStatusSourceEnum.STOP.name())) {
            WebSocketOeServer webSocketOeServer = webSocketMap.get(recordId);
            Map map = new HashMap<>();
            map.put(SystemConstant.RECORD_ID, recordId);
            map.put("monitorVideoSource", source.name());
            WebsocketDto websocketDto = new WebsocketDto(WebsocketTypeEnum.MOBILE_MONITOR_STOP.name(), map);
            webSocketOeServer.sendMessage(websocketDto);
        }
        Map map = new HashMap();
        map.put("status", status.name());
        map.put("updateTime", System.currentTimeMillis());
        return ResultUtil.ok(map);
    }

    @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 = "callCancelExamMobile", 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("recordId")) || Objects.equals(mapParameter.get("recordId"), "")) {
            throw new BusinessException(ExceptionResultEnum.RECORD_ID_IS_NULL);
        }
        Long recordId = Long.parseLong(String.valueOf(mapParameter.get("recordId")));
        if (Objects.isNull(mapParameter.get("source")) || Objects.equals(mapParameter.get("source"), "")) {
            throw new BusinessException("来源不能为空!");
        }
        MonitorVideoSourceEnum source = MonitorVideoSourceEnum.valueOf(String.valueOf(mapParameter.get("source")));
        //获取考试记录缓存
        String liveUrl = null;
        if (Objects.isNull(source) || Objects.isNull(ExamRecordCacheUtil.getMonitorStatus(recordId, source.name()))) {
            throw new BusinessException("推流状态为空");
        }
        ExamRecordCacheUtil.setMonitorCallStatus(recordId, source.name(), MonitorCallStatusSourceEnum.STOP);
        String monitorKey = ExamRecordCacheUtil.getMonitorKey(recordId);
        MonitorStatusSourceEnum status = ExamRecordCacheUtil.getMonitorStatus(recordId, source.name());
        TIeExamInvigilateCallLog tIeExamInvigilateCallLog = new TIeExamInvigilateCallLog(recordId, source, liveUrl, status, monitorKey, MonitorCallStatusSourceEnum.STOP);
        //监考监控通话信息 发送mq start
        MqDto mqDto = new MqDto(MqTopicEnum.THEMIS_TOPIC.getCode(), MqTagEnum.MONITOR_LOG.name(), tIeExamInvigilateCallLog, MqTagEnum.MONITOR_LOG, String.valueOf(tIeExamInvigilateCallLog.getId()), source.name());
        mqDtoService.assembleSendOneWayMsg(mqDto);
        //监考监控通话信息 发送mq end
        return ResultUtil.ok(Collections.singletonMap("updateTime", System.currentTimeMillis()));
    }
}
