package com.qmth.themis.business.util;

import com.qmth.themis.business.constant.SystemConstant;
import com.qmth.themis.business.entity.SysConfig;
import com.qmth.themis.business.entity.TOeExamRecord;
import com.qmth.themis.business.enums.MonitorVideoSourceEnum;
import com.qmth.themis.business.service.TGErrorService;
import com.qmth.themis.business.service.ThemisCacheService;
import com.qmth.themis.common.exception.BusinessException;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.trtc.v20190722.TrtcClient;
import com.tencentcloudapi.trtc.v20190722.models.DismissRoomByStrRoomIdRequest;
import com.tencentcloudapi.trtc.v20190722.models.DismissRoomByStrRoomIdResponse;
import com.tencentcloudapi.vod.v20180717.VodClient;
import com.tencentcloudapi.vod.v20180717.models.DeleteMediaRequest;
import com.tencentcloudapi.vod.v20180717.models.DeleteMediaResponse;
import com.tencentcloudapi.vod.v20180717.models.SearchMediaRequest;
import com.tencentcloudapi.vod.v20180717.models.SearchMediaResponse;
import com.tencentyun.TLSSigAPIv2;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

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

/**
 * @Description: 腾讯云util
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2020/8/21
 */
@Component
public class TencentYunUtil {
    private final static Logger log = LoggerFactory.getLogger(TencentYunUtil.class);

    @Resource
    RedisUtil redisUtil;

    @Resource
    TGErrorService tgErrorService;

    @Resource
    ThemisCacheService themisCacheService;

    private String appId;
    private String key;
    private List<String> urls;
    private String queryUrl;
    private String service;
    private String secretId;
    private String secretKey;
    private Long vodAppId;
    private String callbackPwd;
    private String callbackTime;
    private String trtcQueryUrl;
    private String trtcRegion;

    @Bean
    public void tencentCloudEnv() {
        SysConfig sysConfigTencentCloudSdkAppId = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_APPID);
        Optional.ofNullable(sysConfigTencentCloudSdkAppId).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_app_id"));
        this.appId = sysConfigTencentCloudSdkAppId.getConfigValue();

        SysConfig sysConfigTencentCloudSdkKey = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_KEY);
        Optional.ofNullable(sysConfigTencentCloudSdkKey).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_key"));
        this.key = sysConfigTencentCloudSdkKey.getConfigValue();

        SysConfig sysConfigTencentCloudSdkUrls = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_URLS);
        Optional.ofNullable(sysConfigTencentCloudSdkUrls).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_url"));
        this.urls = Arrays.asList(StringUtils.split(sysConfigTencentCloudSdkUrls.getConfigValue(), ","));

        SysConfig sysConfigTencentCloudSdkService = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_SERVICE);
        Optional.ofNullable(sysConfigTencentCloudSdkService).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_service"));
        this.service = sysConfigTencentCloudSdkService.getConfigValue();

        SysConfig sysConfigTencentCloudSdkQueryUrl = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_QUERY_URL);
        Optional.ofNullable(sysConfigTencentCloudSdkQueryUrl).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_query_url"));
        this.queryUrl = sysConfigTencentCloudSdkQueryUrl.getConfigValue();

        SysConfig sysConfigTencentCloudSdkSecretId = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_SECRET_ID);
        Optional.ofNullable(sysConfigTencentCloudSdkSecretId).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_secret_id"));
        this.secretId = sysConfigTencentCloudSdkSecretId.getConfigValue();

        SysConfig sysConfigTencentCloudSdkSecretKey = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_SECRET_KEY);
        Optional.ofNullable(sysConfigTencentCloudSdkSecretKey).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_secret_key"));
        this.secretKey = sysConfigTencentCloudSdkSecretKey.getConfigValue();

        SysConfig sysConfigTencentCloudSdkVodAppId = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_VOD_APP_ID);
        Optional.ofNullable(sysConfigTencentCloudSdkVodAppId).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_vod_app_id"));
        this.vodAppId = Long.parseLong(sysConfigTencentCloudSdkVodAppId.getConfigValue());

        SysConfig sysConfigTencentCloudSdkCallBackPwd = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_CALLBACK_PWD);
        Optional.ofNullable(sysConfigTencentCloudSdkCallBackPwd).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_回调密码"));
        this.callbackPwd = sysConfigTencentCloudSdkCallBackPwd.getConfigValue();

        SysConfig sysConfigTencentCloudSdkCallBackTime = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_CALLBACK_TIME);
        Optional.ofNullable(sysConfigTencentCloudSdkCallBackTime).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_回调时间"));
        this.callbackTime = sysConfigTencentCloudSdkCallBackTime.getConfigValue();

        SysConfig sysConfigTencentCloudSdkTrtcQueryUrl = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_TRTC_QUERY_URL);
        Optional.ofNullable(sysConfigTencentCloudSdkTrtcQueryUrl).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_trtc_query_url"));
        this.trtcQueryUrl = sysConfigTencentCloudSdkTrtcQueryUrl.getConfigValue();

        SysConfig sysConfigTencentCloudSdkTrtcRegion = themisCacheService.addSysConfigCache(SystemConstant.TENCENT_CLOUD_SDK_TRTC_REGION);
        Optional.ofNullable(sysConfigTencentCloudSdkTrtcRegion).orElseThrow(() -> new BusinessException("未配置腾讯云sdk_trtc_region"));
        this.trtcRegion = sysConfigTencentCloudSdkTrtcRegion.getConfigValue();
    }

    /**
     * 获取签名
     *
     * @param sessionId
     * @param expireTime
     * @return
     */
    public String getSign(String sessionId, Long expireTime) {
        TLSSigAPIv2 api = new TLSSigAPIv2(Long.parseLong(this.getAppId()), this.getKey());
        return api.genSig(sessionId, expireTime);
    }

    /**
     * 请求腾讯云视频地址
     *
     * @param recordId
     * @return
     */
    public Map<String, Object> callTencentVideo(Long recordId) {
        TOeExamRecord tOeExamRecord = SystemConstant.getExamRecord(recordId);
        List<String> monitorRecordList = Arrays.asList(tOeExamRecord.getMonitorRecord().trim().toUpperCase().split(","));
        String[] streamIds = new String[monitorRecordList.size()];
        SysConfig sysConfig = themisCacheService.addSysConfigCache(SystemConstant.MONITOR_CONFIG_PREFIX);
        Optional.ofNullable(sysConfig).orElseThrow(() -> new BusinessException("未配置监控前缀"));

        for (int i = 0; i < monitorRecordList.size(); i++) {
            streamIds[i] = SystemConstant.setStreamId(sysConfig.getConfigValue(), recordId, MonitorVideoSourceEnum.convertToName(monitorRecordList.get(i)));
        }

        Map<String, Object> resultMap = null;
        boolean lockStreamIds = false;
        for (int i = 0; i < SystemConstant.MAX_TENCENT_VIDEO_STREAM_ID_COUNT; i++) {
            lockStreamIds = redisUtil.lock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_STREAM_ID_PREFIX + streamIds.toString(),
                    SystemConstant.REDIS_LOCK_TENCENT_VIDEO_STREAM_ID_TIME_OUT);
            if (lockStreamIds) {
                try {
                    resultMap = this.tencentVodSdk(this.getSecretId(),
                            this.getSecretKey(),
                            this.getQueryUrl(),
                            this.getVodAppId(),
                            streamIds);
                    break;
                } catch (Exception e) {
                    log.error("请求出错", e);
                    if (i == SystemConstant.MAX_TENCENT_VIDEO_STREAM_ID_COUNT - 1) {
                        tgErrorService.saveExamTgError(recordId, "execMqTencentVideoLogic");
                    } else {
                        try {
                            Thread.sleep(3000);
                        } catch (InterruptedException ex) {
                            ex.printStackTrace();
                        }
                    }
                } finally {
                    if (Objects.nonNull(streamIds)) {
                        redisUtil.releaseLock(SystemConstant.REDIS_LOCK_TENCENT_VIDEO_STREAM_ID_PREFIX + streamIds.toString());
                    }
                }
            } else {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        return resultMap;
    }

    /**
     * 腾讯云vodsdk
     *
     * @param secretId
     * @param secretKey
     * @param endPoint
     * @param subAppId
     * @param streamIds
     * @return
     */
    public Map<String, Object> tencentVodSdk(String secretId, String secretKey, String endPoint, Long subAppId, String... streamIds) {
        log.info("腾讯云vodsdk进来了");
        Map<String, Object> resultMap = null;
        try {
            // 实例化一个认证对象，入参需要传入腾讯云账户secretId，secretKey,此处还需注意密钥对的保密
            // 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
            Credential cred = new Credential(secretId, secretKey);
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint(endPoint);
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            VodClient client = new VodClient(cred, "", clientProfile);
            // 实例化一个请求对象,每个接口都会对应一个request对象
            SearchMediaRequest req = new SearchMediaRequest();
            req.setSubAppId(subAppId);
            if (Objects.nonNull(streamIds) && streamIds.length > 0) {
                req.setStreamIds(streamIds);
            }

            // 返回的resp是一个SearchMediaResponse的实例，与请求对象对应
            SearchMediaResponse resp = client.SearchMedia(req);
            if (Objects.nonNull(resp)) {
                resultMap = new HashMap<>();
                // 输出json格式的字符串回包
                String result = SearchMediaResponse.toJsonString(resp);
                resultMap.put("object", resp);
                log.info("result:{}", result);
            }
        } catch (TencentCloudSDKException e) {
            log.error(SystemConstant.LOG_ERROR, e);
        }
        return resultMap;
    }

    /**
     * 解散房间
     *
     * @param secretId
     * @param secretKey
     * @param endPoint
     * @param region
     * @param appId
     * @param roomId
     * @return
     */
    public Map<String, Object> dismissRoomByStrRoomId(String secretId, String secretKey, String endPoint, String region, Long appId, String roomId) {
        log.info("腾讯云解散房间进来了");
        Map<String, Object> resultMap = null;
        try {
            // 实例化一个认证对象，入参需要传入腾讯云账户secretId，secretKey,此处还需注意密钥对的保密
            // 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
            Credential cred = new Credential(secretId, secretKey);
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint(endPoint);
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            TrtcClient client = new TrtcClient(cred, region, clientProfile);
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DismissRoomByStrRoomIdRequest req = new DismissRoomByStrRoomIdRequest();
            req.setSdkAppId(appId);
            req.setRoomId(roomId);
            // 返回的resp是一个DismissRoomByStrRoomIdResponse的实例，与请求对象对应
            DismissRoomByStrRoomIdResponse resp = client.DismissRoomByStrRoomId(req);
            if (Objects.nonNull(resp)) {
                resultMap = new HashMap<>();
                // 输出json格式的字符串回包
                String result = DismissRoomByStrRoomIdResponse.toJsonString(resp);
                resultMap.put("object", resp);
                log.info("result:{}", result);
            }
        } catch (TencentCloudSDKException e) {
            log.error(SystemConstant.LOG_ERROR, e);
        }
        return resultMap;
    }

    /**
     * 腾讯云删除媒体文件进来了
     *
     * @param secretId
     * @param secretKey
     * @param endPoint
     * @param region
     * @param subAppId
     * @param fileId
     * @return
     */
    public Map<String, Object> deleteMedia(String secretId, String secretKey, String endPoint, String region, Long subAppId, String fileId) {
        log.info("腾讯云删除媒体文件进来了");
        Map<String, Object> resultMap = null;
        try {
            // 实例化一个认证对象，入参需要传入腾讯云账户 SecretId 和 SecretKey，此处还需注意密钥对的保密
            // 代码泄露可能会导致 SecretId 和 SecretKey 泄露，并威胁账号下所有资源的安全性。以下代码示例仅供参考，建议采用更安全的方式来使用密钥，请参见：https://cloud.tencent.com/document/product/1278/85305
            // 密钥可前往官网控制台 https://console.cloud.tencent.com/cam/capi 进行获取
            Credential cred = new Credential(secretId, secretKey);
            // 实例化一个http选项，可选的，没有特殊需求可以跳过
            HttpProfile httpProfile = new HttpProfile();
            httpProfile.setEndpoint(endPoint);
            // 实例化一个client选项，可选的，没有特殊需求可以跳过
            ClientProfile clientProfile = new ClientProfile();
            clientProfile.setHttpProfile(httpProfile);
            // 实例化要请求产品的client对象,clientProfile是可选的
            VodClient client = new VodClient(cred, region, clientProfile);
            // 实例化一个请求对象,每个接口都会对应一个request对象
            DeleteMediaRequest req = new DeleteMediaRequest();
            req.setFileId(fileId);
            req.setSubAppId(subAppId);
            // 返回的resp是一个DeleteMediaResponse的实例，与请求对象对应
            DeleteMediaResponse resp = client.DeleteMedia(req);
            if (Objects.nonNull(resp)) {
                resultMap = new HashMap<>();
                // 输出json格式的字符串回包
                String result = DeleteMediaResponse.toJsonString(resp);
                resultMap.put("object", resp);
                log.info("result:{}", result);
            }
        } catch (TencentCloudSDKException e) {
            log.error(SystemConstant.LOG_ERROR, e);
        }
        return resultMap;
    }

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public List<String> getUrls() {
        return urls;
    }

    public void setUrls(List<String> urls) {
        this.urls = urls;
    }

    public String getQueryUrl() {
        return queryUrl;
    }

    public void setQueryUrl(String queryUrl) {
        this.queryUrl = queryUrl;
    }

    public String getService() {
        return service;
    }

    public void setService(String service) {
        this.service = service;
    }

    public String getSecretId() {
        return secretId;
    }

    public void setSecretId(String secretId) {
        this.secretId = secretId;
    }

    public String getSecretKey() {
        return secretKey;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }

    public Long getVodAppId() {
        return vodAppId;
    }

    public void setVodAppId(Long vodAppId) {
        this.vodAppId = vodAppId;
    }

    public String getCallbackPwd() {
        return callbackPwd;
    }

    public void setCallbackPwd(String callbackPwd) {
        this.callbackPwd = callbackPwd;
    }

    public String getCallbackTime() {
        return callbackTime;
    }

    public void setCallbackTime(String callbackTime) {
        this.callbackTime = callbackTime;
    }

    public String getTrtcQueryUrl() {
        return trtcQueryUrl;
    }

    public void setTrtcQueryUrl(String trtcQueryUrl) {
        this.trtcQueryUrl = trtcQueryUrl;
    }

    public String getTrtcRegion() {
        return trtcRegion;
    }

    public void setTrtcRegion(String trtcRegion) {
        this.trtcRegion = trtcRegion;
    }
}