package com.backendsys.service.SDKService.SDKTencent;

import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import com.backendsys.aspect.HttpRequestAspect;
import com.backendsys.entity.SDKEntity.TencentCOSCredentialsDTO;
import com.backendsys.entity.System.SysFile.SysFileCategoryDTO;
import com.backendsys.entity.System.SysFile.SysFileDTO;
import com.backendsys.entity.Tencent.TencentCos.MultipartUploadRespDTO;
import com.backendsys.entity.Tencent.TencentCos.MultipartUploadDTO;
import com.backendsys.entity.Tencent.TencentCos.TempCredentialsDTO;
import com.backendsys.entity.Tencent.TencentCos.UploadOriginDTO;
import com.backendsys.entity.Tencent.TencentCos.UploadOriginResp;
import com.backendsys.exception.CustException;
//import com.backendsys.mapper.System.SysFileCategoryMapper;
//import com.backendsys.mapper.System.SysFileMapper;
//import com.backendsys.service.System.SysFileService;
import com.backendsys.utils.CommonUtil;
import com.backendsys.utils.MapUtil;
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.CosServiceException;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.transfer.TransferManager;
import com.qcloud.cos.utils.IOUtils;
import com.tencent.cloud.CosStsClient;
import com.tencent.cloud.Response;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.*;
import java.net.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


@Service
public class SDKTencentCOSServiceImpl implements SDKTencentCOSService {

    @Autowired
    private HttpRequestAspect httpRequestAspect;

    @Value("${tencent.cos.secret-id}")
    private String secretId;
    @Value("${tencent.cos.secret-key}")
    private String secretKey;

    @Value("${tencent.cos.region}")
    private String region;
    @Value("${tencent.cos.bucket-name}")
    private String bucketName;
    @Value("${tencent.cos.accessible-domain}")
    private String accessibleDomain;
    @Value("${tencent.cos.max-size}")
    private long maxSize;

    @Autowired
    private SDKTencentService sdkTencentService;

//    @Autowired
//    private SysFileMapper sysFileMapper;

//    @Autowired
//    private SysFileService sysFileService;
//
//    @Autowired
//    private SysFileCategoryMapper sysFileCategoryMapper;

    /**
     * 生成cos客户端
     * COSClient cosClient = getClient(secretId, secretKey);
     */

    private COSClient getClient(String secretId, String secretKey) {
        // 初始化用户身份信息(secretId, secretKey)
        COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
        // 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
        Region region1 = new Region(region);
        ClientConfig clientConfig = new ClientConfig(region1);
        clientConfig.setRegion(region1);
        // 生成cos客户端
        COSClient cosClient = new COSClient(cred, clientConfig);
        return cosClient;
    }



//    /**
//     * 临时密钥生成及使用指引
//     * https://www.tencentcloud.com/zh/document/product/436/14048
//     */
//    @Override
//    public TempCredentialsDTO getTempCredentials(String allowPrefix) {
//
//        TreeMap<String, Object> config = new TreeMap<String, Object>();
//        try {
//
//            // 密钥，使用子账号密钥，授权遵循最小权限指引
//            config.put("secretId", secretId);
//            config.put("secretKey", secretKey);
//
//            // 临时密钥有效时长，单位是秒，默认 120 秒，目前主账号最长 2 小时（即 7200 秒），子账号最长 36 小时（即 129600）秒
//            Integer durationSeconds = 120;
//            config.put("durationSeconds", durationSeconds);
//            config.put("bucket", bucketName);
//            config.put("region", region);
//
//            // 获取当前时间戳（毫秒）
//            long nowDateTime = System.currentTimeMillis() / 1000L;
//            // 计算1800秒后的时间戳
//            long expireDateTime = nowDateTime + durationSeconds;
//
//            // 这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径，例子：a.jpg 或者 a/* 或者 * 。
//            // 如果填写了“*”，将允许用户访问所有资源；除非业务需要，否则请按照最小权限原则授予用户相应的访问权限范围。
//            config.put("allowPrefix", allowPrefix);
//
//            // 密钥的权限列表。简单上传、表单上传和分片上传需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
//            String[] allowActions = new String[] {
//                //简单上传操作
//                "name/cos:PutObject",
//                //表单上传对象
//                "name/cos:PostObject",
//                //分块上传：初始化分块操作
//                "name/cos:InitiateMultipartUpload",
//                //分块上传：List 进行中的分块上传
//                "name/cos:ListMultipartUploads",
//                //分块上传：List 已上传分块操作
//                "name/cos:ListParts",
//                //分块上传：上传分块操作
//                "name/cos:UploadPart",
//                //分块上传：完成所有分块上传操作
//                "name/cos:CompleteMultipartUpload",
//                //取消分块上传操作
//                "name/cos:AbortMultipartUpload"
//            };
//            // "name/cos:*"
//
//            config.put("allowActions", allowActions);
//
//            // -- 在后台 存储桶->设置策略 -------------------------------------------------------------
//
//            // 限制上传文件的大小（cos:content-length）
//            // 限制上传文件的类型（cos:content-type）
//            // https://cloud.tencent.com/document/product/436/71307#content-length
//
//
//
//            Response response = CosStsClient.getCredential(config);
//
//            TempCredentialsDTO dto = new TempCredentialsDTO();
//            dto.setRegion(region);
//            dto.setBucket(bucketName);
//            dto.setAllow_prefix(allowPrefix.replace("*", ""));
//            dto.setTmp_secret_id(response.credentials.tmpSecretId);
//            dto.setTmp_secret_key(response.credentials.tmpSecretKey);
//            dto.setSession_token(response.credentials.sessionToken);
//            dto.setStart_time(nowDateTime);
//            dto.setExpired_time(expireDateTime);
//            dto.setDuration_seconds(durationSeconds);
//
//            return dto;
//
//        } catch (Exception e) {
//            e.printStackTrace();
//            throw new CustException(e.getMessage());
//        }
//
//    }

//    /**
//     * [储存桶操作] 获得当前储存桶的文件列表
//     * https://cloud.tencent.com/document/product/436/65938
//     */
//    @Override
//    public Map<String, Object> getBucketList(String next_marker) {
//        COSClient cosClient = getClient(secretId, secretKey);
//
//        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
//        // 设置 bucket 名称
//        listObjectsRequest.setBucketName(bucketName);
//
//
//
//
//
//        // 设置列出的对象名以 prefix 为前缀
//        listObjectsRequest.setPrefix("/materials/1/");
////        listObjectsRequest.setPrefix("");
//
//
//
//
//
//        // 设置最大列出多少个对象, 一次 listobject 最大支持1000
//        listObjectsRequest.setMaxKeys(5);
//
//
//        // 设置被截断开始的位置
//        if (next_marker != null) {
//            listObjectsRequest.setMarker(next_marker);
//        }
//
//        // 保存列出的结果
//        ObjectListing objectListing = null;
//
//        try {
//            objectListing = cosClient.listObjects(listObjectsRequest);
//            System.out.println(objectListing);
//
//        } catch (CosServiceException e) {
//            e.printStackTrace();
//        }
//
//        // object summary 表示此次列出的对象列表
//        List<COSObjectSummary> cosObjectSummaries = objectListing.getObjectSummaries();
//        System.out.println(cosObjectSummaries);
//
//        List<Map<String, Object>> list = new ArrayList<>();
//
//        for (COSObjectSummary cosObjectSummary : cosObjectSummaries) {
//
//            // 对象的 key
//            String key = cosObjectSummary.getKey();
//            // 对象的 etag
//            String etag = cosObjectSummary.getETag();
//            // 对象的长度
//            long fileSize = cosObjectSummary.getSize();
//            // 对象的存储类型
//            String storageClasses = cosObjectSummary.getStorageClass();
//
//            System.out.println("key = " + key);
//            System.out.println("etag = " + etag);
//            System.out.println("fileSize = " + fileSize);
//            System.out.println("storageClasses = " + storageClasses);
//            System.out.println("------------------------------------------");
//
//            Map<String, Object> map = new LinkedHashMap<>();
//            map.put("key", cosObjectSummary.getKey());
//            map.put("etag", cosObjectSummary.getETag());
//            map.put("file_size", cosObjectSummary.getSize());
//            map.put("storage_classes", cosObjectSummary.getStorageClass());
//            list.add(map);
//
//        }
//
//        String nextMarker = null;
//        if (objectListing.isTruncated()) {
//            // 表示还没有列完，被截断了
//            // 下一次开始的位置
//            nextMarker = objectListing.getNextMarker();
//            System.out.println("isTruncated! nextMarker:");
//            System.out.println(nextMarker);
//        }
//
//        Map<String, Object> result = new LinkedHashMap<>();
//        result.put("next_marker", nextMarker);
//        result.put("list", list);
//
//        return result;
//    }



    /**
     * 在调用 [AI数智人-定制接口] 前，需要将素材文件上传到指定的云端存储。
     * - 通过接口获取上传临时令牌，接口返回令牌后，由前端上传对应的文件到对象存储
     */

    /**
     * 文档中心 > 腾讯云智能数智人 > API 接入 > 2D真人小样本形象定制 API 文档-v0.2.1 > 上传素材文件到云端存储 > 获取上传临时令牌
     * https://cloud.tencent.com/document/product/1240/96067
     */

    /**
     * 获取上传临时令牌 (返回远程参数)
     */
    private Map<String, Object> getIvhTempCredentialsOrigin(String appKey, String accessToken) {

        // 获得 Signature 签名
        Map<String, Object> signature = sdkTencentService.getSignature(appKey, accessToken);

        // 构建带参数的 URI (注意参数不要编码) (signature 由于会带+号所以需要改成这种写法)
        String url = "https://gw.tvs.qq.com/v2/ivh/assetmanager/customservice/getuploadcredentials";
        URI uriWithParams = UriComponentsBuilder.fromUriString(url)
                .queryParam("appkey", signature.get("appkey"))
                .queryParam("signature", "{signature}")
                .queryParam("timestamp", signature.get("timestamp"))
                .build(signature.get("signature"));

        String jsonStr = "{\"Payload\": {}}";
        JSONObject bodyData = new JSONObject(jsonStr);

        // Http 远程调用
        Map<String, Object> resp = WebClient.create()
                .method(HttpMethod.POST)
                .uri(uriWithParams)
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE + ";charset=utf-8")
                .bodyValue(bodyData)
                .retrieve()
                .bodyToMono(new ParameterizedTypeReference<Map<String, Object>>() {
                })
                .block();
        /*
        resp:
        {
             "Header": {
                 "RequestID": "gze0f59a6c17104906483254895",
                 "SessionID": "gze0f59a6c17104906483254896",
                 "DialogID": "",
                 "Code": 0,
                 "Message": "ok"
             },
             "Payload": {
                 "ExpiredTime": 1710494248,
                 "PathPrefix": "https://virtualhuman-cos-prod-1251316161.cos.accelerate.myqcloud.com/customer-pipeline/884/70c53bfe-c0e1-481a-8ae3-4e76f03d2971/",
                 "StartTime": 1710490648,
                 "Credentials": {
                     "Token": "owlVQ..",
                     "TmpSecretId": "AKIDxTiqWc9i0bSsPJy2uduUCJOTxVvMYxrWiJTS0V4OtTEm_UQqiq-ECwf38Xtrtl44",
                     "TmpSecretKey": "zmsLEogWD0lAY/SWHnjH8XfzrDs0Vicfd4RGECehBCE="
                 }
             }
         }
         */

        return resp;
    }

    /**
     * 获取上传临时令牌 (返回格式化后的参数)
     */
    public TencentCOSCredentialsDTO getIvhTempCredentials(String appKey, String accessToken) {
        Map<String, Object> resp = getIvhTempCredentialsOrigin(appKey, accessToken);

        // 判断是否获取成功
        Integer respHeaderCode = (Integer) MapUtil.get(resp, "Header.Code");
        if (respHeaderCode != 0) {
            String respHeaderMessage = (String) MapUtil.get(resp, "Header.Message");
            throw new RuntimeException(respHeaderMessage);
        }

        TencentCOSCredentialsDTO credentialsDTO = new TencentCOSCredentialsDTO();
        credentialsDTO.setStart_time((Integer) MapUtil.get(resp, "Payload.StartTime"));
        credentialsDTO.setExpired_time((Integer) MapUtil.get(resp, "Payload.ExpiredTime"));
        credentialsDTO.setTmp_secret_id((String) MapUtil.get(resp, "Payload.Credentials.TmpSecretId"));
        credentialsDTO.setTmp_secret_key((String) MapUtil.get(resp, "Payload.Credentials.TmpSecretKey"));
        credentialsDTO.setSession_token((String) MapUtil.get(resp, "Payload.Credentials.Token"));


        // 通过 PathPrefix 获得 bucket 和 region
        String pathPrefix = (String) MapUtil.get(resp, "Payload.PathPrefix");
        Pattern pattern = Pattern.compile("https://([a-zA-Z0-9\\-]+)\\.cos\\.(\\w+)\\.myqcloud\\.com/.*");
        Matcher matcher = pattern.matcher(pathPrefix);
        if (matcher.find()) {
            credentialsDTO.setBucket(matcher.group(1));
            credentialsDTO.setRegion(matcher.group(2));
        }
        credentialsDTO.setPath_prefix(pathPrefix);

        int startIndex = pathPrefix.indexOf("customer-pipeline");
        String lastPath = pathPrefix.substring(startIndex);
        credentialsDTO.setKey_dir(lastPath);
        /*
        credentialsDTO:
        {
             "bucket": "virtualhuman-cos-prod-1251316161",
             "region": "accelerate",
             "path_prefix": "https://virtualhuman-cos-prod-1251316161.cos.accelerate.myqcloud.com/customer-pipeline/884/3e52ec21-02ea-403c-8d1c-ebb727ceac1f/",
             "key_dir": "customer-pipeline/884/3e52ec21-02ea-403c-8d1c-ebb727ceac1f/",
             "tmp_secret_id": "AKIDqv8LDYIWVdqq5PrNpGD9J4YY0p8QQ7qXe2ZNV78W_y9wXFGXe6z4wkRJu0OQ3jhE",
             "tmp_secret_key": "8TQWL6+YweYQc2yDCNBDe2/V/qdNZYhYczSS6QdUcwk=",
             "session_token": "czCmJWynL6j78eXE..",
             "start_time": 1712253511,
             "expired_time": 1712257111
         }
         */
        return credentialsDTO;
    }


    /**
     * 高级操作
     */
    public TransferManager createTransferManager(COSClient cosClient) {
        // 自定义线程池大小，建议在客户端与 COS 网络充足（例如使用腾讯云的 CVM，同地域上传 COS）的情况下，设置成16或32即可，可较充分的利用网络资源
        // 对于使用公网传输且网络带宽质量不高的情况，建议减小该值，避免因网速过慢，造成请求超时。
        ExecutorService threadPool = Executors.newFixedThreadPool(32);
        // 传入一个 threadpool, 若不传入线程池，默认 TransferManager 中会生成一个单线程的线程池。
        TransferManager transferManager = new TransferManager(cosClient, threadPool);
        return transferManager;
    }
    public void shutdownTransferManager(TransferManager transferManager) {
        // 指定参数为 true, 则同时会关闭 transferManager 内部的 COSClient 实例。
        // 指定参数为 false, 则不会关闭 transferManager 内部的 COSClient 实例。
        transferManager.shutdownNow(true);
    }


//    /**
//     * 压缩图片 (入参 InputStream，出参 File)
//     */
//    public File compressImage(InputStream inputStream, String suffix) throws IOException {
//
//        System.out.println("suffix: " + suffix);
//
//        // 将文件内容写入临时文件
////        String tempFilename = CommonUtil.generateFilename(suffix);
////        String tempFilenameThumb = tempFilename.replaceAll("." + suffix, "-thumb." + suffix);
//
////        System.out.println("tempFilename: " + tempFilename);
////        System.out.println("tempFilenameThumb: " + tempFilenameThumb);
//        System.out.println("-------------------------------------------");
//
//        File tempFile = File.createTempFile("tmp", "." + suffix);
//        File tempFileThumb = File.createTempFile("tmp", "." + suffix);
//
//        System.out.println("tempFile: " + tempFile);
//        System.out.println("tempFileThumb: " + tempFileThumb);
//
//        try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
//            System.out.println("IOUtils.copy start: ");
//            IOUtils.copy(inputStream, outputStream);
//        } catch (Exception e) {
//            System.out.println("IOUtils.copy Error:");
//            System.out.println(e);
//        }
//        // 创建缩略图
//        boolean isImage = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp").contains(suffix.toLowerCase());
//        if (isImage) {
//            File sourceFile = new File(tempFile.getPath());
//            File sourceFileThumb = new File(tempFileThumb.getPath());
//            // 使用Thumbnailator生成缩略图并写入到临时文件
//            System.out.println("Thumbnails start");
//            try {
//                System.out.println("sourceFile:");
//                System.out.println(sourceFile);
//                System.out.println("sourceFileThumb:");
//                System.out.println(sourceFileThumb);
//                Thumbnails.of(sourceFile).size(60, 60).outputQuality(0.2).toFile(sourceFileThumb);
//                tempFileThumb.delete();
//            } catch (Exception e) {
//                System.out.println(e);
//            }
//            System.out.println("Thumbnails end");
//            return sourceFile;
//        }
//        return null;
//    }

    /**
     * [小文件] 将 远程文件 异步保存到 COS储存桶
     * 注意：
     * - 由于是异步上传，可能访问会有延迟
     * - 如果文件过大，可能会造成内存溢出，所以最好 不要作为上传大文件去使用，不要作为公开接口
     */
    public UploadOriginResp uploadOrigin(UploadOriginDTO uploadOriginDTO) throws IOException {

        if (uploadOriginDTO.getUrl() == null) throw new CustException("图片不能为空");

        // 获得自身 user_id
        Long user_id = httpRequestAspect.getUserId();

        // 获得远程 url
        URL url = new URL(uploadOriginDTO.getUrl());

        // 获得文件元数据，并判断文件是否失效或过期
        HttpURLConnection connection = (HttpURLConnection) url.openConnection();
        connection.connect();
        int responseCode = connection.getResponseCode();
        if (responseCode != HttpURLConnection.HTTP_OK) throw new CustException("文件失效或已过期");

        // 获取系统默认的临时目录
        Path tempDirPath = Paths.get(System.getProperty("java.io.tmpdir"));

        // 下载文件到本地 (可能会有性能问题？)
        HttpUtil.downloadFile(uploadOriginDTO.getUrl(), tempDirPath.toFile());

        // 获得 文件名
        String filename = Paths.get(url.getPath()).getFileName().toString();
        // 获得 后缀名
        String suffix = StringUtils.getFilenameExtension(filename);

        // 判断 是否为图片
        boolean isImage = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp").contains(suffix.toLowerCase());

        // 获得 文件名 (缩略图)
        String filenameThumb = isImage ? filename.replaceAll("." + suffix, "-thumb." + suffix) : null;

        File downloadFile = new File(tempDirPath + File.separator + filename);
        File downloadFileThumb = isImage ? new File(File.createTempFile("tmp", "." + suffix).getPath()) : null;

        if (isImage) {
            // 压缩图片
            Thumbnails.of(downloadFile.getPath()).size(60, 60).outputQuality(0.2).toFile(downloadFileThumb);
        }

        // 获得 要保存的路径/文件名
        String key = uploadOriginDTO.getDir() + "/" + filename;
        String keyThumb = isImage ? uploadOriginDTO.getDir() + "/" + filenameThumb : null;

        String remotePath = accessibleDomain + "/" + key;
        String remotePathThumb = isImage ? accessibleDomain + "/" + keyThumb : null;

        // 创建返回值实体类
        UploadOriginResp resp = new UploadOriginResp();
        resp.setUrl(accessibleDomain + "/" + key);
        resp.setUrl_thumb(isImage ? accessibleDomain + "/" + keyThumb : null);
        resp.setOrigin_url(uploadOriginDTO.getUrl());

        // 返回文件元数据 (类型，大小，最后更新时间)
        resp.setContent_type(connection.getContentType());
        resp.setContent_length(connection.getContentLength());
        resp.setLast_modified(connection.getLastModified());

        // -- 创建异步上传任务 ---------------------------------------------------------------------
        CompletableFuture.runAsync(() -> {
            try {

                // -- 将文件流保存到对象储存桶 ------------------------------------------------------
                COSClient cosClient = getClient(secretId, secretKey);

//                // 检查对象是否存在于存储桶中
//                if (checkObjectExist(cosClient, key)) {
//                    System.out.println(key + " 已存在，不需要上传");
//                } else {

                    // 上传原图
                    PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, downloadFile);
                    PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
                    System.out.println("putObjectResult: " + putObjectResult.toString());

                    // 上传缩略图
                    if (isImage) {
                        PutObjectRequest putObjectRequestThumb = new PutObjectRequest(bucketName, keyThumb, downloadFileThumb);
                        PutObjectResult putObjectResultThumb = cosClient.putObject(putObjectRequestThumb);
                        System.out.println("putObjectResultThumb: " + putObjectResultThumb.toString());
                    }

                    // -- [DB] 插入数据 -------------------------------------
                    SysFileDTO fileDTO = new SysFileDTO();
                    fileDTO.setUser_id(user_id);
                    fileDTO.setName(filename);
                    fileDTO.setFile_key(key);
                    fileDTO.setFile_remote_path(remotePath);

                    if (isImage) {
                        fileDTO.setFile_remote_path_thumb(remotePathThumb);
                    }
                    fileDTO.setSize(Long.valueOf(connection.getContentLength()));
                    InputStream inputStream = new FileInputStream(downloadFile);
                    String md5 = DigestUtil.md5Hex(inputStream);
                    fileDTO.setMd5(md5);
//                    fileDTO.setCategory_id(getCategoryIdBySuffix(suffix));

//                    sysFileMapper.insertFile(fileDTO);
                    System.out.println("insert success");
//                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
        return resp;
    }
//
//    /**
//     * 查询分块上传任务
//     */
//    public List<String> listMultipartUploads(String key){
//
//        COSClient cosClient = getClient(secretId, secretKey);
//
//        ListMultipartUploadsRequest listMultipartUploadsRequest = new ListMultipartUploadsRequest(bucketName);
//        // 每次请求最多列出多少个
//        listMultipartUploadsRequest.setMaxUploads(100);
//        // 设置要查询的分块上传任务的目标前缀，直接设置要查询的 key
//        listMultipartUploadsRequest.setPrefix(key);
//
//        MultipartUploadListing multipartUploadListing = null;
//
//        boolean found = false;
//        List<String> list = new ArrayList<>();
//        do {
//            multipartUploadListing = cosClient.listMultipartUploads(listMultipartUploadsRequest);
//            List<MultipartUpload> multipartUploads = multipartUploadListing.getMultipartUploads();
//
//            for (MultipartUpload mUpload : multipartUploads) {
//                if (mUpload.getKey().equals(key)) {
//                    System.out.println(mUpload.getUploadId());
//                    list.add(mUpload.getUploadId());
//                    //found = true;
//                    //break;
//                }
//            }
//
//            //if (found) {
//            //    break;
//            //}
//            listMultipartUploadsRequest.setKeyMarker(multipartUploadListing.getNextKeyMarker());
//            listMultipartUploadsRequest.setUploadIdMarker(multipartUploadListing.getNextUploadIdMarker());
//        } while (multipartUploadListing.isTruncated());
//
//        if (!found) {
//            System.out.printf("do not found upload task with key: %s\n", key);
//        }
//
//        return list;
//    }


//    /**
//     * 简单上传
//     */
//    public MultipartUploadRespDTO simpleUpload(MultipartFile file) {
//
//        // 检查上传的文件是否为空
//        if (file == null || file.isEmpty()) throw new CustException("file 上传文件不能为空");
//
//        // 判断文件大小是否超过
//        if (file.getSize() > maxSize) {
//            throw new CustException("上传文件不能大于 " + maxSize/1024/1024 + " MB，如有需要请使用大文件上传功能");
//        }
//
//        // 生成cos客户端
//        COSClient cosClient = getClient(secretId, secretKey);
//
//        MultipartUploadRespDTO resp = new MultipartUploadRespDTO();
//        try {
//
//            String md5 = DigestUtil.md5Hex(file.getInputStream());
//            String suffix = FileUtil.extName(file.getOriginalFilename()).toLowerCase();
//            String filename = md5 + "." + suffix;
//            String filenameThumb = filename.replaceAll("." + suffix, "-thumb." + suffix);
//            String key = "temp/" + filename;
//            String keyThumb = "temp/" + filenameThumb;
//            String remotePath = accessibleDomain + "/" + key;
//            String remotePathThumb = accessibleDomain + "/" + keyThumb;
//
////            // 获得文件分类列表
////            SysFileCategoryDTO categoryDTO = new SysFileCategoryDTO();
////            List<SysFileCategoryDTO> categoryList = sysFileCategoryMapper.queryFileCategoryList(categoryDTO);
//
//            // 获得图片分类的后缀 (待做)，再进行以下判断
//
//            // 还有删除也要同时删除缩略图
//
//            // 还有大文件分片也要做缩略图
//
//            // 还有 视频要做截图生成
//
//
//
//
//
//            boolean isImage = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp").contains(suffix.toLowerCase());
//
//
//
//
//
//
//
//            // 检查对象是否存在于存储桶中
//            if (checkObjectExist(cosClient, key)) {
//                // -- [COS] 对象存在时，直接返回路径 -------------------------------
//                resp.setIs_fast_upload(true);
//
//            } else {
//                // 获取文件内容
//                InputStream inputStream = file.getInputStream();
//
//                // 将文件内容写入临时文件
//                String tempFilename = CommonUtil.generateFilename(null);
//                File tempFile = File.createTempFile(tempFilename, null);
//                try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
//                    IOUtils.copy(inputStream, outputStream);
//                }
//                // 创建 PutObjectRequest 对象，PutObject 请求。(异步)
//                PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, tempFile);
//                cosClient.putObject(putObjectRequest);
//
//
//                // 创建缩略图
//                if (isImage) {
//                    File tempFileThumb = File.createTempFile(tempFilename, "." + suffix);
//                    String sourceFilePath = tempFileThumb.getPath();
//                    File sourceFile = new File(sourceFilePath);
//                    // 使用Thumbnailator生成缩略图并写入到临时文件
//                    Thumbnails.of(tempFile).size(60, 60).outputQuality(0.2).toFile(tempFileThumb);
//                    PutObjectRequest putObjectRequestThumb = new PutObjectRequest(bucketName, keyThumb, sourceFile);
//                    cosClient.putObject(putObjectRequestThumb);
//                    resp.setPath_thumb(remotePathThumb);
//                    tempFileThumb.delete();
//                }
//
//                tempFile.delete();
//
//            }
//
//            // -- [DB] 查询数据 ---------------------------------------------
////            SysFileDTO fileDetail = sysFileMapper.queryFileByKey(key);
////            // 如果表中没有数据，则添加数据
////            if (fileDetail == null) {
//                // -- [DB] 插入数据 -----------------------------------------
//                SysFileDTO fileDTO = new SysFileDTO();
//                fileDTO.setUser_id(httpRequestAspect.getUserId());
//                fileDTO.setName(filename);
//                fileDTO.setFile_key(key);
//                fileDTO.setFile_remote_path(remotePath);
//                if (isImage) {
//                    fileDTO.setFile_remote_path_thumb(remotePathThumb);
//                }
////                fileDTO.setCategory_id(getCategoryIdBySuffix(suffix));
//                fileDTO.setSize(file.getSize());
//                fileDTO.setMd5(md5);
//                sysFileMapper.insertFile(fileDTO);
////            }
//            // -------------------------------------------------------------
//
//            resp.setFilename(filename);
//            resp.setKey(key);
//            resp.setMd5(md5);
//            resp.setSize(file.getSize());
//            resp.setPath(remotePath);
//
//        } catch (IOException e) {
//            throw new CustException(e.getMessage());
//        } finally {
//            if (cosClient != null) {
//                cosClient.shutdown();
//            }
//        }
//        return resp;
//
//    }


//    // -- 检查对象是否存在于存储桶中 ---------------------------------------------------------------------
//    private Boolean checkObjectExist(COSClient cosClient, String key) {
//        try {
//            return cosClient.doesObjectExist(bucketName, key);
//        } catch (CosServiceException e) {
//            e.printStackTrace();
//        }
//        return false;
//    }
//    // -- 初始化分块任务 -------------------------------------------------------------------------------
//    private MultipartUploadRespDTO initiateMultipartUpload(COSClient cosClient, MultipartFile file, String key, String filename){
//        System.out.println("-- initiateMultipartUpload --");
//        try {
//            //
//            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, key);
//            // 分块上传的过程中，仅能通过初始化分块指定文件上传之后的 metadata, 需要的头部可以在这里指定
//            ObjectMetadata objectMetadata = new ObjectMetadata();
//            request.setObjectMetadata(objectMetadata);
//            //
//            InitiateMultipartUploadResult initResult = cosClient.initiateMultipartUpload(request);
//
//            MultipartUploadRespDTO resp = new MultipartUploadRespDTO();
//            resp.setUpload_id(initResult.getUploadId());
//            resp.setKey(key);
//            resp.setSize(file.getSize());
//            resp.setFilename(filename);
//            System.out.println("-------------");
//            System.out.println(filename);
//            System.out.println("-------------");
//            return resp;
//
//        } catch (CosServiceException e) {
//            throw new CustException(e.getMessage());
//        }
//    }
//    // -- 上传分块 ------------------------------------------------------------------------------------
//    private MultipartUploadRespDTO uploadPart(COSClient cosClient, MultipartUploadDTO multipartUploadDTO) {
//        System.out.println("-- uploadPart --");
//        try {
//            InputStream inputStream = multipartUploadDTO.getFile().getInputStream();
//
//            UploadPartRequest uploadPartRequest = new UploadPartRequest();
//            uploadPartRequest.setBucketName(bucketName);
//            uploadPartRequest.setKey(multipartUploadDTO.getKey());
//            uploadPartRequest.setUploadId(multipartUploadDTO.getUpload_id());
//            uploadPartRequest.setInputStream(inputStream);
//
//            // 设置分块的长度 (分块文件大小)
//            uploadPartRequest.setPartSize(multipartUploadDTO.getFile().getSize());
//            // 设置要上传的分块编号，从1开始
//            uploadPartRequest.setPartNumber(multipartUploadDTO.getIndex());
//
//            // [COS] 上传分块
//            UploadPartResult uploadPartResult = cosClient.uploadPart(uploadPartRequest);
//            PartETag partETag = uploadPartResult.getPartETag();
//
//            MultipartUploadRespDTO resp = new MultipartUploadRespDTO();
//            resp.setUpload_id(multipartUploadDTO.getUpload_id());
//            resp.setKey(multipartUploadDTO.getKey());
//            resp.setIndex(partETag.getPartNumber());
//            resp.setCount(multipartUploadDTO.getCount());
//            resp.setChunk_md5(partETag.getETag());
//            resp.setChunk_size(uploadPartRequest.getPartSize());
//            return resp;
//
//        } catch (CosServiceException e) {
//            if ("NoSuchUpload".equals(e.getErrorCode())) {
//                throw new CustException("找不到相关的 upload_id 或 key 信息，任务已终止或已完成");
//            }
//            throw new CustException(e.getMessage());
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
//    }
//    // -- 查询已上传的分块 ------------------------------------------------------------------------------
//    public List<PartETag> listParts(String uploadId, String key) {
//        COSClient cosClient = getClient(secretId, secretKey);
//
//        // 用于保存已上传的分片信息
//        List<PartETag> partETags = new LinkedList<>();
//
//        PartListing partListing = null;
//        ListPartsRequest listPartsRequest = new ListPartsRequest(bucketName, key, uploadId);
//        do {
//            try {
//                partListing = cosClient.listParts(listPartsRequest);
//            } catch (CosServiceException e) {
//                throw e;
//            }
//            for (PartSummary partSummary : partListing.getParts()) {
//                partETags.add(new PartETag(partSummary.getPartNumber(), partSummary.getETag()));
//            }
//            listPartsRequest.setPartNumberMarker(partListing.getNextPartNumberMarker());
//        } while (partListing.isTruncated());
//        return partETags;
//    }
//    // -- 合并分块上传 ---------------------------------------------------------------------------------
//    private MultipartUploadRespDTO completeMultipartUpload(COSClient cosClient, MultipartUploadDTO multipartUploadDTO) {
//        System.out.println("-- completeMultipartUpload --");
//        // 保存已上传的分片信息，实际情况里，这里的内容是从上传分块接口中获取到的
//        List<PartETag> partETags = listParts(multipartUploadDTO.getUpload_id(), multipartUploadDTO.getKey());
//
//        // 分片上传结束后，调用 complete 完成分片上传
//        CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, multipartUploadDTO.getKey(), multipartUploadDTO.getUpload_id(), partETags);
//        try {
//            CompleteMultipartUploadResult completeResult = cosClient.completeMultipartUpload(completeMultipartUploadRequest);
//
//            MultipartUploadRespDTO resp = new MultipartUploadRespDTO();
//            resp.setRequest_id(completeResult.getRequestId());
//            resp.setPath(accessibleDomain + "/" + multipartUploadDTO.getKey());
//            return resp;
//
//        } catch (CosServiceException e) {
//
//            // -- [DB] 删除 uploadId 对应的分块信息 ---------------------
//            sysFileMapper.deleteFile(multipartUploadDTO.getKey());
//
//            // 终止任务
//            abortMultipartUpload(multipartUploadDTO);
//
//            System.out.println("getErrorCode:");
//            System.out.println(e.getErrorCode());
//            System.out.println(e.getMessage());
//            throw new CustException("合并失败，无效的分块，请重新上传", 6001, "(" + e.getErrorCode() + ") " + e.getMessage());
//        }
//    }
////    // -- 获得分类 CategoryID (通过文件后缀名) -----------------------------------------------------------
////    private Long getCategoryIdBySuffix(String suffix) {
////        SysFileCategoryDTO sysFileCategoryDTO = new SysFileCategoryDTO();
////        List<SysFileCategoryDTO> categoryList = sysFileCategoryMapper.queryFileCategoryList(sysFileCategoryDTO);
////        Optional<Long> firstMatchedId = categoryList.stream().filter(dto -> dto.getSuffix().toLowerCase().contains(suffix)).map(SysFileCategoryDTO::getId).findFirst();
////        Long id = firstMatchedId.orElse(null);
////        return id;
////    }
//    // -----------------------------------------------------------------------------------------------
//
//    /**
//     * 分块上传 (1判断是否存在，2初始化，3上传分块，4合并分块)
//     * - https://cloud.tencent.com/document/product/436/65935
//     * - https://cloud.tencent.com/document/product/436/14112
//     * @param multipartUploadDTO
//     * @return
//     */
//    public MultipartUploadRespDTO multipartUpload(MultipartUploadDTO multipartUploadDTO) {
//
//        // -- 参数校验 ------------------------------------------------------
//        if (multipartUploadDTO.getFile() == null || multipartUploadDTO.getFile().isEmpty()) throw new CustException("file 上传文件不能为空");
//
//        // 当存在 uploadId 时，即是分块上传阶段
//        if (multipartUploadDTO.getUpload_id() != null) {
//            if (multipartUploadDTO.getKey() == null) throw new CustException("key 不能为空");
//            if (multipartUploadDTO.getIndex() == null) throw new CustException("index 不能为空");
//            if (multipartUploadDTO.getCount() == null) throw new CustException("count 不能为空");
//            if (multipartUploadDTO.getIndex() > multipartUploadDTO.getCount()) {
//                throw new CustException("index 不能大于 count");
//            }
//        }
//        // -----------------------------------------------------------------
//
//        COSClient cosClient = getClient(secretId, secretKey);
//
//        MultipartUploadRespDTO resp = new MultipartUploadRespDTO();
//
//        // [秒传] 检查对象是否存在，存在即 "秒传"
//        try {
//
//            String md5 = DigestUtil.md5Hex(multipartUploadDTO.getFile().getInputStream());
//            String suffix = FileUtil.extName(multipartUploadDTO.getFile().getOriginalFilename()).toLowerCase();
//            String filename = md5 + "." + suffix;
//            String key = "temp/" + filename;
//
//            // 检查对象是否存在于存储桶中
//            if (checkObjectExist(cosClient, key)) {
//
//                String remotePath = accessibleDomain + "/" + key;
//
//                // -- [COS] 对象存在时，直接返回路径 -------------------------------
//                resp.setKey(key);
//                resp.setPath(remotePath);
//                resp.setIs_fast_upload(true);
//
//                // -- [DB] 查询数据 ---------------------------------------------
////                SysFileDTO fileDetail = sysFileMapper.queryFileByKey(key);
////                // 如果表中没有数据，则添加数据
////                if (fileDetail == null) {
//                    // -- [DB] 插入数据 -----------------------------------------
//                    SysFileDTO fileDTO = new SysFileDTO();
//                    fileDTO.setUser_id(httpRequestAspect.getUserId());
//                    fileDTO.setName(filename);
//                    fileDTO.setFile_key(key);
//                    fileDTO.setFile_remote_path(remotePath);
////                    fileDTO.setCategory_id(getCategoryIdBySuffix(suffix));
//
//                    // 获得文件大小
//                    fileDTO.setSize(CommonUtil.getRemoteFileSize(remotePath));
//
//                    fileDTO.setMd5(md5);
//                    sysFileMapper.insertFile(fileDTO);
////                }
//                // -------------------------------------------------------------
//
//            } else {
//
//                // 对象不存在时，走创建流程
//                String uploadId = multipartUploadDTO.getUpload_id();
//                if (uploadId == null || uploadId.isEmpty()) {
//
//                    // -- [DB] 查询数据 (By 文件Key) ------------------------------
//                    SysFileDTO fileDetail = sysFileMapper.queryFileByKey(key);
//                    if (fileDetail != null) {
//                        resp.setUpload_id(fileDetail.getChunk_upload_id());
//                        resp.setKey(fileDetail.getFile_key());
//                        resp.setSize(fileDetail.getSize());
//                        resp.setFilename(filename);
////                        resp.setIndex(fileDetail.getChunk_current_index());
////                        resp.setCount(fileDetail.getChunk_count());
//                        // 返回 { uploadId, key }
//                        // 缺少 { chunk_md5, chunk_size }
//                    } else {
//
////                        // 上传要求每个分块大小必须大于 1MB，所以整个文件也不能小于 1MB
////                        if (multipartUploadDTO.getFile().getSize() < 1 * 1024 * 1024) {
////                            throw new CustException("上传文件不能小于1MB");
////                        }
//
//                        // -- [初始化] 第一次上传，无 uploadId ---------------------
//                        resp = initiateMultipartUpload(cosClient, multipartUploadDTO.getFile(), key, filename);
//                        // 返回 { uploadId, key }
//
//                        // -- [DB] 插入数据 -------------------------------------
//                        SysFileDTO fileDTO = new SysFileDTO();
//                        fileDTO.setUser_id(httpRequestAspect.getUserId());
//                        fileDTO.setName(filename);
//                        fileDTO.setFile_key(key);
//                        fileDTO.setChunk_upload_id(resp.getUpload_id());
//                        fileDTO.setSize(resp.getSize());
//                        fileDTO.setMd5(md5);
////                        fileDTO.setCategory_id(getCategoryIdBySuffix(suffix));
//                        sysFileMapper.insertFile(fileDTO);
//                    }
//                    // ---------------------------------------------------------
//
//                } else {
//
//                    // 分块上传要求每个分块大小必须大于 1MB (最后一个例外)
//                    if (resp.getIndex() != resp.getCount()) {
//                        if (multipartUploadDTO.getFile().getSize() < 1 * 1024 * 1024) {
//                            throw new CustException("分块文件不能小于1MB");
//                        }
//                    }
//
//                    // -- [DB] 查询数据 (By 直传Key) ------------------------------
//                    SysFileDTO fileDetail = sysFileMapper.queryFileByKey(multipartUploadDTO.getKey());
//
//                    // -- [分块上传] 带着 uploadId 来，开始上传分块 ------------------
//                    resp = uploadPart(cosClient, multipartUploadDTO);
//                    // 返回 { uploadId, key, index, part_etag, size }
//
//                    // -- [合并分块] 当最后一块上传完成时，合并分块 --------------------
//                    if (resp.getIndex() != null && resp.getCount() != null && resp.getIndex() == resp.getCount()) {
//
//                        MultipartUploadRespDTO complete = completeMultipartUpload(cosClient, multipartUploadDTO);
//
//                        // 附上最后一次上传的分块信息
//                        complete.setUpload_id(resp.getUpload_id());
//                        complete.setKey(resp.getKey());
//                        complete.setSize(resp.getSize());
//                        complete.setIndex(resp.getIndex());
//                        complete.setCount(resp.getCount());
//                        complete.setChunk_md5(resp.getChunk_md5());
//                        complete.setChunk_size(resp.getChunk_size());
//                        // 以及返回 { request_id, path }
//                        resp = complete;
//
//                    }
//
//
//                    if (fileDetail != null) {
//
//                        System.out.println("fileDetail != null !");
//                        // -- [DB] 更新数据 -------------------------------------
//
//                        SysFileDTO fileDTO = new SysFileDTO();
//                        fileDTO.setUser_id(httpRequestAspect.getUserId());
//                        fileDTO.setFile_key(resp.getKey());
//
//                        fileDTO.setChunk_current_index(resp.getIndex());
//                        fileDTO.setChunk_count(resp.getCount());
//
//                        // 没有完成分块时
//                        if (resp.getPath() != null) {
//
//                            Long size = fileDetail.getSize() != null ? fileDetail.getSize() : 0;
//                            fileDTO.setSize(size + resp.getChunk_size());
//
//                            fileDTO.setFile_remote_path(resp.getPath());
//                            fileDTO.setChunk_current_index(null);
//                            fileDTO.setChunk_count(null);
//                            fileDTO.setChunk_upload_id("");
//                        }
//
//                        sysFileMapper.updateFile(fileDTO);
//                    }
//
//                    // ------------------------------------------------------
//                }
//            }
//
//        } catch (IOException e) {
//            throw new CustException(e.getMessage());
//        } finally {
//            if (cosClient != null) {
//                cosClient.shutdown();
//            }
//        }
//
//        return resp;
//    }
//
//    /**
//     * 终止分块上传任务
//     */
//    public Map<String, Object> abortMultipartUpload(MultipartUploadDTO multipartUploadDTO){
//
//        COSClient cosClient = getClient(secretId, secretKey);
//
//        String key = multipartUploadDTO.getKey();
//        String uploadId = multipartUploadDTO.getUpload_id();
//
//        AbortMultipartUploadRequest abortMultipartUploadRequest = new AbortMultipartUploadRequest(bucketName, key, uploadId);
//        try {
//            cosClient.abortMultipartUpload(abortMultipartUploadRequest);
//        } catch (CosServiceException e) {
//            System.out.println("Service");
//            System.out.println(e.getErrorCode());
//            System.out.println(e.getMessage());
//            if ("NoSuchUpload".equals(e.getErrorCode())) {
//                throw new CustException("找不到相关的 upload_id 或 key 信息，任务已终止或已完成");
//            }
//            throw new CustException(e.getMessage());
//        }
//
//        // 确保不再使用 cosClient 之后，关闭即可
//        cosClient.shutdown();
//
//        // -- [DB] 删除数据 --------------------------------------------------
//        sysFileMapper.deleteFile(key);
//
//        Map<String, Object> resp = new LinkedHashMap<>();
//        resp.put("key", key);
//        resp.put("upload_id", uploadId);
//
//        return resp;
//    }
//
//
//
//    /**
//     * 删除文件
//     */
//    public Map<String, Object> removeUploadFile(SysFileDTO sysFileDTO) {
//
//        String name = sysFileDTO.getName();
//        String suffix = StringUtils.getFilenameExtension(name.toLowerCase());
//
//        String nameThumb = name.replaceAll("." + suffix, "-thumb." + suffix);
//
//        String key = "temp/" + name;
//        String keyThumb = "temp/" + nameThumb;
//
//        // -- [COS] 删除对象 --------------------------------------
//        COSClient cosClient = getClient(secretId, secretKey);
//        cosClient.deleteObject(bucketName, key);
//        cosClient.deleteObject(bucketName, keyThumb);
//        cosClient.shutdown();
//
//        // -- [DB] 判断记录是否存在 --------------------------------
//        SysFileDTO fileDetail = sysFileMapper.queryFileByKey(key);
//        if (fileDetail == null) throw new CustException("文件记录不存在");
//
//        // -- [DB] 删除存储桶中的文件 -------------------------------
//        // -- 创建异步上传任务 ---------------------------------------------------------------------
//        CompletableFuture.runAsync(() -> {
//            sysFileMapper.deleteFile(key);
//            sysFileMapper.deleteFile(keyThumb);
//        });
//
//        return Map.of("key", key);
//    }

}
