package com.cpop.core.handler;

import com.cpop.core.base.exception.UtilException;
import com.cpop.core.config.TencentCosProperties;
import com.cpop.core.utils.file.FileTypeUtils;
import com.cpop.core.utils.uuid.IdUtils;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.transfer.Download;
import com.qcloud.cos.transfer.TransferManager;
import com.qcloud.cos.transfer.TransferManagerConfiguration;
import com.qcloud.cos.transfer.Upload;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author DB
 * @createTime 2023/10/31 15:03
 * @description 腾讯云SDK相关工具类
 */
@Component
@EnableConfigurationProperties(TencentCosProperties.class)
public class TencentCosHandler {

    /**
     * 腾讯云配置
     */
    @Autowired
    private TencentCosProperties properties;

    private TransferManager transferManager;

    /**
     * @descriptions 初始化创建TransferManager 实例，这个实例用来后续调用高级接口
     * @author DB
     * @date 2023/11/01 10:34
     * @return: void
     */
    @PostConstruct
    public void createTransferManager(){
        // 创建一个 COSClient 实例，这是访问 COS 服务的基础实例。
        // 详细代码参见本页: 简单操作 -> 创建 COSClient
        COSClient cosClient = createCosClient();
        // 自定义线程池大小，建议在客户端与 COS 网络充足（例如使用腾讯云的 CVM，同地域上传 COS）的情况下，设置成16或32即可，可较充分的利用网络资源
        // 对于使用公网传输且网络带宽质量不高的情况，建议减小该值，避免因网速过慢，造成请求超时。
        ExecutorService threadPool = Executors.newFixedThreadPool(32);
        // 传入一个 threadPool, 若不传入线程池，默认 TransferManager 中会生成一个单线程的线程池。
        transferManager = new TransferManager(cosClient, threadPool);
        // 设置高级接口的配置项
        // 分块上传阈值和分块大小分别为 5MB 和 1MB
        TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
        transferManagerConfiguration.setMultipartUploadThreshold(5 * 1024 * 1024);
        transferManagerConfiguration.setMinimumUploadPartSize(1 * 1024 * 1024);
        transferManager.setConfiguration(transferManagerConfiguration);
    }

    /**
     * @descriptions 创建 COSClient 实例，这个实例用来后续调用请求
     * @author DB
     * @date 2023/10/31 16:47
     * @return: com.qcloud.cos.COSClient
     */
    private COSClient createCosClient() {
        // 设置用户身份信息。
        // SECRETID 和 SECRETKEY 请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理
        // 用户的 SecretId，建议使用子账号密钥，授权遵循最小权限指引，降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
        String secretId = properties.getSecretId();
        // 用户的 SecretKey，建议使用子账号密钥，授权遵循最小权限指引，降低使用风险。子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
        String secretKey = properties.getSecretKey();
        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        // ClientConfig 中包含了后续请求 COS 的客户端设置：
        ClientConfig clientConfig = new ClientConfig();
        // 设置 bucket 的地域
        // COS_REGION 请参见 https://cloud.tencent.com/document/product/436/6224
        clientConfig.setRegion(new Region(properties.getRegion()));
        // 设置请求协议, http 或者 https
        // 5.6.53 及更低的版本，建议设置使用 https 协议
        // 5.6.54 及更高版本，默认使用了 https
        clientConfig.setHttpProtocol(HttpProtocol.https);
        // 以下的设置，是可选的：
        // 设置 socket 读取超时，默认 30s
        clientConfig.setSocketTimeout(30 * 1000);
        // 设置建立连接超时，默认 30s
        clientConfig.setConnectionTimeout(30 * 1000);
        // 如果需要的话，设置 http 代理，ip 以及 port
        //clientConfig.setHttpProxyIp("httpProxyIp");
        //clientConfig.setHttpProxyPort(80);
        // 生成 cos 客户端。
        return new COSClient(cred, clientConfig);
    }

    /**
     * @descriptions 关闭 TransferManager
     * @author DB
     * @date 2023/10/31 17:09
     * @param transferManager 管理器
     * @return: void
     */
    public void shutdownTransferManager(TransferManager transferManager) {
        // 指定参数为 true, 则同时会关闭 transferManager 内部的 COSClient 实例。
        // 指定参数为 false, 则不会关闭 transferManager 内部的 COSClient 实例。
        transferManager.shutdownNow(true);
    }

    /**
     * @descriptions cdn上传
     * @author DB
     * @date 2023/10/31 17:21
     * @param file 文件
     * @return: com.qcloud.cos.model.UploadResult
     */
    public UploadResult cdnUpload(MultipartFile file) {
        String fileType = FileTypeUtils.getInstance().getFileType(file.getOriginalFilename());
        try {
            ObjectMetadata objectMetadata = new ObjectMetadata();
            // 上传的流如果能够获取准确的流长度，则推荐一定填写 content-length
            // 如果确实没办法获取到，则下面这行可以省略，但同时高级接口也没办法使用分块上传了
            objectMetadata.setContentLength(file.getSize());
            objectMetadata.setContentType(file.getContentType());
            InputStream inputStream = file.getInputStream();
            PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), IdUtils.fastSimpleUUID() + "." + fileType, inputStream, objectMetadata);
            // 设置存储类型（如有需要，不需要请忽略此行代码）, 默认是标准(Standard), 低频(standard_ia)
            // 更多存储类型请参见 https://cloud.tencent.com/document/product/436/33417
            putObjectRequest.setStorageClass(StorageClass.Standard);
            Upload upload = transferManager.upload(putObjectRequest);
            UploadResult uploadResult = upload.waitForUploadResult();
            inputStream.close();
            return uploadResult;
        } catch (IOException | InterruptedException e) {
            throw new UtilException(e);
        }
    }

    /**
     * @descriptions 获取cdn路径
     * @author DB
     * @date 2023/10/31 17:56
     * @return: java.lang.String
     */
    public String getCdnUrl(){
        return properties.getCdnUrl();
    }
}
