package com.qmth.themis.admin.api;

import com.alibaba.fastjson.JSONObject;
import com.qmth.themis.business.bean.admin.TencentEventBean;
import com.qmth.themis.business.bean.status.MonitorStatusBean;
import com.qmth.themis.business.cache.ExamRecordCacheUtil;
import com.qmth.themis.business.cache.bean.ExamCacheBean;
import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.dto.MqDto;
import com.qmth.themis.business.entity.*;
import com.qmth.themis.business.enums.*;
import com.qmth.themis.business.service.*;
import com.qmth.themis.business.util.MqUtil;
import com.qmth.themis.business.util.RedisUtil;
import com.qmth.themis.business.util.ServletUtil;
import com.qmth.themis.business.util.TencentYunUtil;
import com.qmth.themis.common.contanst.Constants;
import com.qmth.themis.common.enums.Platform;
import com.qmth.themis.common.enums.Source;
import com.qmth.themis.common.exception.BusinessException;
import com.qmth.themis.common.util.Base64Util;
import com.qmth.themis.common.util.MD5Util;
import com.qmth.themis.common.util.Result;
import com.qmth.themis.common.util.ResultUtil;
import com.tencentcloudapi.common.Sign;
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.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.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * @Description: 视频源回调接口
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2022/3/25
 */
@Api(tags = "视频源回调接口")
@RestController
@RequestMapping(SystemConstant.PREFIX_URL_NOTIFY + "/monitor")
public class TENotifyController {
    private final static Logger log = LoggerFactory.getLogger(TENotifyController.class);

    @Resource
    TOeExamRecordService tOeExamRecordService;

    @Resource
    TencentYunUtil tencentYunUtil;

    @Resource
    RedisUtil redisUtil;

    @Resource
    TMTencentVideoMessageService tencentVideoMessageService;

    @Resource
    MqUtil mqUtil;

    @Resource
    MqDtoService mqDtoService;

    @Resource
    ThemisCacheService themisCacheService;

    @Resource
    TEExamService teExamService;

    @ApiOperation(value = "腾讯云视频接口回调")
    @RequestMapping(value = "/record/tencent", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "结果信息")})
    @Transactional
    public Result tencentCallback(@ApiParam(value = "腾讯云视频接口回调信息", required = true) @RequestBody JSONObject jsonObject) throws NoSuchAlgorithmException {
        log.info("腾讯云视频接口回调 jsonObject:{}", jsonObject.toJSONString());
        String callbackPwd = tencentYunUtil.getCallbackPwd();
        Optional.ofNullable(callbackPwd).orElseThrow(() -> new BusinessException("配置文件回调密码为空"));

        String sign = (String) jsonObject.get("sign");
        Optional.ofNullable(sign).orElseThrow(() -> new BusinessException("腾讯云签名为空"));

        Long t = Long.parseLong(String.valueOf(jsonObject.get("t")));
        Optional.ofNullable(t).orElseThrow(() -> new BusinessException("腾讯云回调时间为空"));

        if (!MD5Util.verify(callbackPwd + String.valueOf(t), sign)) {
            throw new BusinessException("腾讯云签名不匹配");
        }

        String eventType = String.valueOf(jsonObject.get("event_type"));
        //说明是录制视频回调成功
        if (Objects.nonNull(eventType) && Objects.equals(eventType, "100")) {
            String streamId = String.valueOf(jsonObject.get("stream_id"));
            Long startTime = jsonObject.getLong("start_time");
            Long endTime = jsonObject.getLong("end_time");
            String videoSource = SystemConstant.getMonitorRecordStreamId(streamId);
            boolean lock = redisUtil.lock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_PREFIX + videoSource, SystemConstant.REDIS_LOCK_TENCENT_VIDEO_TIME_OUT);
            if (lock) {
                try {
                    Long recordId = null;
                    if (Objects.nonNull(streamId)) {
                        String[] str = streamId.split("_");
                        recordId = Long.parseLong(str[2]);
                        tencentVideoMessageService.saveAndCount(new TMTencentVideoMessage(SystemConstant.getNanoId(), jsonObject.toJSONString(), recordId, videoSource, startTime * 1000, endTime * 1000, String.valueOf(jsonObject.get("video_url"))));
                    }
                } finally {
                    redisUtil.releaseLock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_PREFIX + videoSource);
                }
            }
        }
        return ResultUtil.ok(true);
    }

    @ApiOperation(value = "腾讯云状态事件接口回调")
    @RequestMapping(value = "/status/tencent", method = RequestMethod.POST)
    @ApiResponses({@ApiResponse(code = 200, message = "结果信息")})
    public Result tencentEventCallback(@ApiParam(value = "腾讯云事件接口回调信息", required = true) @RequestBody String string) throws Exception {
        log.info("腾讯云状态事件接口回调 string:{}", string);
        String callbackPwd = tencentYunUtil.getCallbackPwd();
        Optional.ofNullable(callbackPwd).orElseThrow(() -> new BusinessException("配置文件回调密码为空"));

        String sign = ServletUtil.getRequestSign();
        Optional.ofNullable(sign).orElseThrow(() -> new BusinessException("腾讯云签名为空"));
        log.info("sign:{}", sign);

        String localSign = Base64Util.encode(Sign.hmac256(callbackPwd.getBytes(StandardCharsets.UTF_8), string));
        log.info("localSign:{}", localSign);

        if (!Objects.equals(localSign, sign)) {
            throw new BusinessException("腾讯云状态事件签名不匹配");
        }

        TencentEventBean tencentEventBean = JSONObject.toJavaObject(JSONObject.parseObject(string), TencentEventBean.class);
        if (Objects.nonNull(tencentEventBean)) {
            Long eventTime = null;
            if (Objects.nonNull(tencentEventBean.getEventInfo()) &&
                    Objects.nonNull(tencentEventBean.getEventInfo().getEventMsTs())) {
                eventTime = tencentEventBean.getEventInfo().getEventMsTs();
            } else if (Objects.nonNull(tencentEventBean.getEventInfo()) &&
                    Objects.nonNull(tencentEventBean.getEventInfo().getEventTs())) {
                eventTime = tencentEventBean.getEventInfo().getEventTs() * 1000L;
            }
            TencentEventGroupEnum tencentEventGroupEnum = TencentEventGroupEnum.convertToName(tencentEventBean.getEventGroupId());
            if (Objects.isNull(tencentEventBean.getEventInfo().getUserId())) {
                return ResultUtil.ok(true);
            }
            String userId = tencentEventBean.getEventInfo().getUserId();
            Long recordId = Long.parseLong(tencentEventBean.getEventInfo().getRoomId());
            String[] userIds = userId.split("_");
            String sessionId = userIds[1];
            TBSession tbSession = null;
            Platform platform = null;
            Source source = null;
            //s是客户端和移动端，m是监控端
//            if (Objects.equals(userIds[0], "s")) {
            tbSession = (TBSession) redisUtil.getUserSession(sessionId);
            platform = Platform.valueOf(tbSession.getPlatform());
            source = Source.valueOf(tbSession.getSource());
//            }
            String monitorRecord = ExamRecordCacheUtil.getMonitorVideoSource(recordId);
            if (Objects.isNull(monitorRecord)) {
                TOeExamRecord tOeExamRecord = SystemConstant.getExamRecord(recordId);
                monitorRecord = tOeExamRecord.getMonitorVideoSource();
            }
            Map<String, String> monitorRecordMap = SystemConstant.getMonitorRecordMap(monitorRecord);
            TencentEventTypeEnum tencentEventTypeEnum = TencentEventTypeEnum.convertToName(tencentEventBean.getEventType());
            switch (tencentEventGroupEnum) {
                case EVENT_GROUP_ROOM://房间事件组
                    switch (tencentEventTypeEnum) {
                        case EVENT_TYPE_ENTER_ROOM://进入房间
                            if (Objects.equals(userIds[0], "s")) {
                                this.startLogic(userIds[0], source, platform, recordId, monitorRecordMap, eventTime);
                            } else if (Objects.equals(userIds[0], "m")) {
                                this.updateInvigilateCallLog(recordId, MonitorCallStatusSourceEnum.CALLING, eventTime);
                            }
                            break;
                        case EVENT_TYPE_EXIT_ROOM://退出房间
                            if (Objects.equals(userIds[0], "s")) {
                                this.stopLogic(userIds[0], source, platform, recordId, monitorRecordMap, eventTime);
                            } else if (Objects.equals(userIds[0], "m")) {
                                this.updateInvigilateCallLog(recordId, MonitorCallStatusSourceEnum.STOP, eventTime);
                            }
                            break;
                        default:
                            break;
                    }
                    break;
                case EVENT_GROUP_MEDIA://媒体事件组
                    switch (tencentEventTypeEnum) {
                        case EVENT_TYPE_START_VIDEO://开始推送视频数据
                            this.startLogic(userIds[0], source, platform, recordId, monitorRecordMap, eventTime);
                            break;
                        case EVENT_TYPE_STOP_VIDEO://停止推送视频数据
                            this.stopLogic(userIds[0], source, platform, recordId, monitorRecordMap, eventTime);
                            break;
                        default:
                            break;
                    }
                    break;
                default:
                    break;
            }
        }
        return ResultUtil.ok(true);
    }

    /**
     * 开始逻辑
     *
     * @param userIdPrefix
     * @param source
     * @param platform
     * @param recordId
     * @param monitorRecordMap
     * @param eventTime
     */
    protected void startLogic(String userIdPrefix,
                              Source source,
                              Platform platform,
                              Long recordId,
                              Map<String, String> monitorRecordMap,
                              Long eventTime) {
        //s是客户端和移动端，m是监控端
        if (Objects.equals(userIdPrefix, "s")) {
//            if (source == Source.OE_CLIENT && (platform == Platform.WIN || platform == Platform.MAC)) {
//                if (Objects.nonNull(monitorRecordMap.get(MonitorVideoSourceEnum.CLIENT_CAMERA.name()))) {
//                    this.updateStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.CLIENT_CAMERA.name()), MonitorStatusSourceEnum.START, MonitorCallStatusSourceEnum.START);
//                } else if (Objects.nonNull(monitorRecordMap.get(MonitorVideoSourceEnum.CLIENT_SCREEN.name()))) {
//                    this.updateStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.CLIENT_SCREEN.name()), MonitorStatusSourceEnum.START, MonitorCallStatusSourceEnum.START);
//                }
//            } else
            if (source == Source.MOBILE_MONITOR_FIRST && (platform == Platform.ANDROID || platform == Platform.IOS)) {
                if (!CollectionUtils.isEmpty(monitorRecordMap) && Objects.nonNull(monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_FIRST.name()))) {
                    MonitorStatusBean monitorStatusBean = ExamRecordCacheUtil.getMonitorStatusBean(recordId, MonitorVideoSourceEnum.valueOf(monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_FIRST.name())));
                    if (Objects.isNull(monitorStatusBean) || (Objects.nonNull(monitorStatusBean) && monitorStatusBean.getMonitorStatusSourceEnum() != MonitorStatusSourceEnum.START)) {
                        this.updateStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_FIRST.name()), MonitorStatusSourceEnum.START, eventTime);
                        this.monitorStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_FIRST.name()), MqTagEnum.MONITOR_START, eventTime);
                    }
                }
            } else if (source == Source.MOBILE_MONITOR_SECOND && (platform == Platform.ANDROID || platform == Platform.IOS)) {
                if (!CollectionUtils.isEmpty(monitorRecordMap) && Objects.nonNull(monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_SECOND.name()))) {
                    MonitorStatusBean monitorStatusBean = ExamRecordCacheUtil.getMonitorStatusBean(recordId, MonitorVideoSourceEnum.valueOf(monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_SECOND.name())));
                    if (Objects.isNull(monitorStatusBean) || (Objects.nonNull(monitorStatusBean) && monitorStatusBean.getMonitorStatusSourceEnum() != MonitorStatusSourceEnum.START)) {
                        this.updateStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_SECOND.name()), MonitorStatusSourceEnum.START, eventTime);
                        this.monitorStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_SECOND.name()), MqTagEnum.MONITOR_START, eventTime);
                    }
                }
            }
        }
    }

    /**
     * 退出逻辑
     *
     * @param userIdPrefix
     * @param source
     * @param platform
     * @param recordId
     * @param monitorRecordMap
     * @param eventTime
     */
    protected void stopLogic(String userIdPrefix,
                             Source source,
                             Platform platform,
                             Long recordId,
                             Map<String, String> monitorRecordMap,
                             Long eventTime) {
        //s是客户端和移动端，m是监控端
        if (Objects.equals(userIdPrefix, "s")) {
//            if (source == Source.OE_CLIENT && (platform == Platform.WIN || platform == Platform.MAC)) {
//                if (Objects.nonNull(monitorRecordMap.get(MonitorVideoSourceEnum.CLIENT_CAMERA.name()))) {
//                    this.updateStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.CLIENT_CAMERA.name()), MonitorStatusSourceEnum.STOP, MonitorCallStatusSourceEnum.STOP);
//                } else if (Objects.nonNull(monitorRecordMap.get(MonitorVideoSourceEnum.CLIENT_SCREEN.name()))) {
//                    this.updateStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.CLIENT_SCREEN.name()), MonitorStatusSourceEnum.STOP, MonitorCallStatusSourceEnum.STOP);
//                }
//            } else
            if (source == Source.MOBILE_MONITOR_FIRST && (platform == Platform.ANDROID || platform == Platform.IOS)) {
                if (!CollectionUtils.isEmpty(monitorRecordMap) && Objects.nonNull(monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_FIRST.name()))) {
                    MonitorStatusBean monitorStatusBean = ExamRecordCacheUtil.getMonitorStatusBean(recordId, MonitorVideoSourceEnum.valueOf(monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_FIRST.name())));
                    if (Objects.isNull(monitorStatusBean) || (Objects.nonNull(monitorStatusBean) && monitorStatusBean.getMonitorStatusSourceEnum() != MonitorStatusSourceEnum.STOP)) {
                        this.updateStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_FIRST.name()), MonitorStatusSourceEnum.STOP, eventTime);
                        this.monitorStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_FIRST.name()), MqTagEnum.MONITOR_STOP, eventTime);
                    }
                }
            } else if (source == Source.MOBILE_MONITOR_SECOND && (platform == Platform.ANDROID || platform == Platform.IOS)) {
                if (!CollectionUtils.isEmpty(monitorRecordMap) && Objects.nonNull(monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_SECOND.name()))) {
                    MonitorStatusBean monitorStatusBean = ExamRecordCacheUtil.getMonitorStatusBean(recordId, MonitorVideoSourceEnum.valueOf(monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_SECOND.name())));
                    if (Objects.isNull(monitorStatusBean) || (Objects.nonNull(monitorStatusBean) && monitorStatusBean.getMonitorStatusSourceEnum() != MonitorStatusSourceEnum.STOP)) {
                        this.updateStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_SECOND.name()), MonitorStatusSourceEnum.STOP, eventTime);
                        this.monitorStatus(recordId, monitorRecordMap.get(MonitorVideoSourceEnum.MOBILE_SECOND.name()), MqTagEnum.MONITOR_STOP, eventTime);
                    }
                }
            }
        }
    }

    /**
     * 更新状态
     *
     * @param recordId
     * @param source
     * @param monitorStatusSourceEnum
     * @param eventTime
     */
    protected void updateStatus(Long recordId, String source,
                                MonitorStatusSourceEnum monitorStatusSourceEnum,
                                Long eventTime) {
        ExamRecordCacheUtil.setMonitorStatus(recordId, MonitorVideoSourceEnum.valueOf(source), monitorStatusSourceEnum, eventTime);
        MonitorCallStatusSourceEnum callStatus = Objects.nonNull(ExamRecordCacheUtil.getMonitorCallStatus(recordId, MonitorVideoSourceEnum.valueOf(source))) ? ExamRecordCacheUtil.getMonitorCallStatus(recordId, MonitorVideoSourceEnum.valueOf(source)) : null;
        ExamRecordCacheUtil.setMonitorCallStatus(recordId, MonitorVideoSourceEnum.valueOf(source), callStatus, eventTime);

        SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.MONITOR_CONFIG_PREFIX);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置监控前缀"));

        String liveUrl = SystemConstant.setStreamId(sysConfig.getConfigValue(), recordId, MonitorVideoSourceEnum.valueOf(source));
        String monitorKey = ExamRecordCacheUtil.getMonitorKey(recordId);
        TIeExamInvigilateCallLog tIeExamInvigilateCallLog = new TIeExamInvigilateCallLog(recordId, MonitorVideoSourceEnum.valueOf(source), liveUrl, MonitorStatusSourceEnum.START, monitorKey, callStatus, null, null);

        tOeExamRecordService.sendExamRecordDataSaveMqNotIp(recordId, eventTime);

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

    /**
     * 更新通话记录log
     *
     * @param recordId
     * @param callStatus
     * @param eventTime
     */
    protected void updateInvigilateCallLog(Long recordId, MonitorCallStatusSourceEnum callStatus, Long eventTime) {
        SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.MONITOR_CONFIG_PREFIX);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置监控前缀"));
        ExamCacheBean examCacheBean = teExamService.getExamCacheBean(ExamRecordCacheUtil.getExamId(recordId));
        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;
        }
        String liveUrl = SystemConstant.setStreamId(sysConfig.getConfigValue(), recordId, source);
        String monitorKey = ExamRecordCacheUtil.getMonitorKey(recordId);
        TIeExamInvigilateCallLog tIeExamInvigilateCallLog = new TIeExamInvigilateCallLog(recordId, source, liveUrl, MonitorStatusSourceEnum.START, monitorKey, callStatus, null, null);
        if (callStatus == MonitorCallStatusSourceEnum.START) {
            tIeExamInvigilateCallLog.setStartTime(eventTime);
        } else if (callStatus == MonitorCallStatusSourceEnum.CALLING) {
            tIeExamInvigilateCallLog.setCallTime(eventTime);
        } else if (callStatus == MonitorCallStatusSourceEnum.STOP) {
            tIeExamInvigilateCallLog.setEndTime(eventTime);
        }
        tOeExamRecordService.sendExamRecordDataSaveMqNotIp(recordId, eventTime);

        //监考监控通话信息 发送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
    }

    /**
     * 监考状态
     *
     * @param recordId
     * @param source
     * @param mqTagEnum
     * @param eventTime
     */
    protected void monitorStatus(Long recordId, String source, MqTagEnum mqTagEnum, Long eventTime) {
        Map mqMap = new HashMap<>();
        mqMap.put(SystemConstant.RECORD_ID, recordId);
        mqMap.put(Constants.HEADER_SOURCE, source);
        MqDto mqDtoStart = new MqDto(mqUtil.getTopic(), mqTagEnum.name(), recordId, mqTagEnum, String.valueOf(recordId), mqMap, String.valueOf(recordId), eventTime);
        mqDtoService.assembleSendAsyncOrderMsg(mqDtoStart);
    }
}
