package com.yfd.platform.modules.storage.service.base;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.HttpMethod;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import com.amazonaws.services.s3.transfer.Copy;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.TransferManagerBuilder;
import com.yfd.platform.constant.ZFileConstant;
import com.yfd.platform.exception.StorageSourceAutoConfigCorsException;
import com.yfd.platform.modules.config.service.SystemConfigService;
import com.yfd.platform.modules.storage.model.enums.FileTypeEnum;
import com.yfd.platform.modules.storage.model.param.S3BaseParam;
import com.yfd.platform.modules.storage.model.result.FileItemResult;
import com.yfd.platform.utils.StringUtils;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import javax.rmi.CORBA.Util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;

/**
 * @author zhengsl
 */
@Slf4j
public abstract class AbstractS3BaseFileService<P extends S3BaseParam> extends AbstractBaseFileService<P> {

    private static final Logger LOGGER = LoggerFactory.getLogger(ChannelInboundHandlerAdapter.class);


    protected AmazonS3 s3Client;

    public static final InputStream EMPTY_INPUT_STREAM = new ByteArrayInputStream(new byte[0]);

    @Resource
    private SystemConfigService systemConfigService;

    @Override
    public List<FileItemResult> fileList(String folderPath) {
        return s3FileList(folderPath);
    }

    @Override
    public List<FileItemResult> fileLists(String folderPath) {
        return s3FileLists(folderPath);
    }
    @Override
    public List<FileItemResult> fileListData(String path, String name ) {
        return s3FileListData(path,name);
    }
    /**
     * 默认 S3 获取对象下载链接的方法, 如果指定了域名, 则替换为自定义域名.
     *
     * @return S3 对象访问地址
     */
    @Override
    public String getDownloadUrl(String pathAndName) {
        String bucketName = param.getBucketName();
        String domain = param.getDomain();

        String fullPath = StringUtils.concatTrimStartSlashes(param.getBasePath() + pathAndName);

        // 如果不是私有空间, 且指定了加速域名, 则直接返回下载地址.
        if (BooleanUtil.isFalse(param.isPrivate()) && StrUtil.isNotEmpty(domain)) {
            return StringUtils.concat(domain, StringUtils.encodeAllIgnoreSlashes(fullPath));
        }

        Integer tokenTime = param.getTokenTime();
        if (param.getTokenTime() == null || param.getTokenTime() < 1) {
            tokenTime = 1800;
        }

        Date expirationDate = new Date(System.currentTimeMillis() + tokenTime * 1000);

        GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, fullPath, HttpMethod.GET);
        generatePresignedUrlRequest.setExpiration(expirationDate);
        URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);

        String defaultUrl = url.toExternalForm();
        if (StrUtil.isNotEmpty(domain)) {
            defaultUrl = StringUtils.concat(domain, url.getFile());
        }
        return defaultUrl;
    }


    /**
     * 获取 S3 指定目录下的对象列表
     *
     * @param path 路径
     * @return 指定目录下的对象列表
     */
    public List<FileItemResult> s3FileList(String path) {
        String bucketName = param.getBucketName();
        path = StringUtils.trimStartSlashes(path);
        String fullPath = StringUtils.trimStartSlashes(StringUtils.concat(param.getBasePath(), path, ZFileConstant.PATH_SEPARATOR));

        List<FileItemResult> fileItemList = new ArrayList<>();

        ListObjectsRequest listObjectsRequest = new ListObjectsRequest()
                .withBucketName(bucketName)
                .withPrefix(fullPath)
                .withMaxKeys(1000)
                .withDelimiter("/");
        ObjectListing objectListing = s3Client.listObjects(listObjectsRequest);

        boolean isFirstWhile = true;

        do {
            if (!isFirstWhile) {
                objectListing = s3Client.listNextBatchOfObjects(objectListing);
            }

            for (S3ObjectSummary s : objectListing.getObjectSummaries()) {
                FileItemResult fileItemResult = new FileItemResult();
                if (s.getKey().equals(fullPath)) {
                    continue;
                }
                fileItemResult.setName(s.getKey().substring(fullPath.length()));
                fileItemResult.setSize(s.getSize());
                fileItemResult.setTime(s.getLastModified());
                fileItemResult.setType(FileTypeEnum.FILE);
                fileItemResult.setPath(path);

                String fullPathAndName = StringUtils.concat(path, fileItemResult.getName());
                fileItemResult.setUrl(getDownloadUrl(fullPathAndName));

                fileItemList.add(fileItemResult);
            }

            for (String commonPrefix : objectListing.getCommonPrefixes()) {
                FileItemResult fileItemResult = new FileItemResult();
                fileItemResult.setName(commonPrefix.substring(fullPath.length(), commonPrefix.length() - 1));
                String name = fileItemResult.getName();
                if (StrUtil.isEmpty(name) || StrUtil.equals(name, StringUtils.DELIMITER_STR)) {
                    continue;
                }

                fileItemResult.setType(FileTypeEnum.FOLDER);
                fileItemResult.setPath(path);
                fileItemList.add(fileItemResult);
            }
            isFirstWhile = false;
        } while (objectListing.isTruncated());

        return fileItemList;
    }

    public List<FileItemResult> s3FileListData(String path, String name) {
        List<FileItemResult> fileItemList = new ArrayList<>();
        String bucketName = param.getBucketName();
        if (path == null || StringUtils.isEmpty(path)) return fileItemList;

        // 规范路径格式
        path = ensurePathWithSlash(path);

        // 明确判断是否为文件夹（仅根据 name 是否以斜杠结尾）
        boolean isFolder = name.endsWith("/");
        String targetPath = buildTargetPath(path, name, isFolder);

        if (isFolder) {
            // 处理文件夹时，先添加文件夹自身
            FileItemResult folderItem = createFolderItemSelf(targetPath);
            if (folderItem != null) {
                fileItemList.add(folderItem);
            }

            // 列出文件夹内容（包含子目录）
            String fullPrefix = StringUtils.trimStartSlashes(
                    StringUtils.concat(param.getBasePath(), targetPath)
            );
            listFolderContents(bucketName, fullPrefix, targetPath, fileItemList);
        } else {
            // 处理文件
            FileItemResult fileItem = getFileItem(bucketName, targetPath);
            if (fileItem != null) fileItemList.add(fileItem);
        }
        return fileItemList;
    }

    /**
     * 构建目标路径（确保文件夹以斜杠结尾）
     */
    private String buildTargetPath(String path, String name, boolean isFolder) {
        String targetPath = path + name;
        // 如果是文件夹且未以斜杠结尾，则补全
        if (isFolder && !targetPath.endsWith("/")) {
            targetPath += "/";
        }
        return targetPath;
    }

    /**
     * 获取单个文件信息
     */
    private FileItemResult getFileItem(String bucketName, String targetPath) {
        String fullKey = StringUtils.trimStartSlashes(
                StringUtils.concat(param.getBasePath(), targetPath)
        );

        try {
            // 精确匹配文件键
            S3ObjectSummary objectSummary = s3Client.listObjects(bucketName, fullKey)
                    .getObjectSummaries()
                    .stream()
                    .filter(s -> s.getKey().equals(fullKey))
                    .findFirst()
                    .orElse(null);

            if (objectSummary == null) return null;

            FileItemResult item = new FileItemResult();
            item.setName(extractFileName(targetPath));
            item.setSize(objectSummary.getSize());
            item.setTime(objectSummary.getLastModified());
            item.setType(FileTypeEnum.FILE);
            item.setPath(extractParentPatha(targetPath));
            item.setUrl(getDownloadUrl(targetPath));
            return item;
        } catch (Exception e) {
            LOGGER.error("获取文件信息失败: {}", fullKey, e);
            return null;
        }
    }

    /**
     * 创建目标文件夹自身的条目
     */
    private FileItemResult createFolderItemSelf(String targetPath) {
        if (targetPath.equals("/") || targetPath.isEmpty()) {
            return null; // 根目录不单独添加
        }

        String parentPath = extractParentPatha(targetPath);
        String folderName = extractFolderName(targetPath);

        FileItemResult item = new FileItemResult();
        item.setName(folderName);
        item.setType(FileTypeEnum.FOLDER);
        item.setPath(parentPath);
        item.setTime(new Date()); // S3 无文件夹实体，时间设为当前时间或留空
        return item;
    }

    /**
     * 从路径中提取父路径（如 /1/2/3/ → /1/2/）
     */
    private String extractParentPatha(String path) {
        path = StringUtils.trimEndSlashes(path);
        int lastSlashIndex = path.lastIndexOf('/');
        return (lastSlashIndex > 0) ? path.substring(0, lastSlashIndex + 1) : "/";
    }

    /**
     * 从路径中提取文件夹名称（如 /1/2/3/ → 3）
     */
    private String extractFolderName(String path) {
        path = StringUtils.trimEndSlashes(path);
        int lastSlashIndex = path.lastIndexOf('/');
        return (lastSlashIndex > 0) ? path.substring(lastSlashIndex + 1) : path;
    }
    /**
     * 列出文件夹内容（包括子目录，递归时不重复添加自身）
     */
    private void listFolderContents(
            String bucketName,
            String prefix,
            String currentPath,
            List<FileItemResult> results
    ) {
        ListObjectsRequest request = new ListObjectsRequest()
                .withBucketName(bucketName)
                .withPrefix(prefix)
                .withDelimiter("/")  // 使用分隔符获取子目录
                .withMaxKeys(1000);

        ObjectListing listing;
        do {
            listing = s3Client.listObjects(request);

            // 处理文件
            listing.getObjectSummaries().stream()
                    .filter(s -> !s.getKey().endsWith("/"))  // 排除目录标记
                    .forEach(s -> {
                        String fileName = s.getKey().substring(prefix.length());
                        results.add(createFileItem(fileName, s, currentPath));
                    });

            // 处理子文件夹（通过递归添加）
            listing.getCommonPrefixes().forEach(commonPrefix -> {
                String subFolderPrefix = commonPrefix;
                String folderName = subFolderPrefix.substring(prefix.length(), subFolderPrefix.length() - 1);
                String subCurrentPath = currentPath + folderName + "/";

                // 递归调用以处理子目录（子目录的自身条目会在递归时添加）
                listFolderContents(bucketName, subFolderPrefix, subCurrentPath, results);
            });

            request.setMarker(listing.getNextMarker());
        } while (listing.isTruncated());
    }

    // 辅助方法
    private FileItemResult createFileItem(String name, S3ObjectSummary summary, String path) {
        FileItemResult item = new FileItemResult();
        item.setName(name);
        item.setSize(summary.getSize());
        item.setTime(summary.getLastModified());
        item.setType(FileTypeEnum.FILE);
        item.setPath(path);
        item.setUrl(getDownloadUrl(path + name));
        return item;
    }

    private FileItemResult createFolderItem(String name, String path) {
        FileItemResult item = new FileItemResult();
        item.setName(name);
        item.setType(FileTypeEnum.FOLDER);
        item.setPath(path);
        return item;
    }

    private String extractFileName(String path) {
        return path.contains("/") ?
                path.substring(path.lastIndexOf('/') + 1) : path;
    }

    private String extractParentPath(String path) {
        return path.contains("/") ?
                path.substring(0, path.lastIndexOf('/') + 1) : "/";
    }

    private String ensurePathWithSlash(String path) {
        return path.startsWith("/") ? path : "/" + path;
    }
    //这个方法有用  获取指定路径下的所有的文件以及文件夹
    public List<FileItemResult> s3FileLists(String path) {
        String bucketName = param.getBucketName();
        path = StringUtils.trimStartSlashes(path); // 去掉路径开头的斜杠
        String fullPath = StringUtils.trimStartSlashes(StringUtils.concat(param.getBasePath(), path, ZFileConstant.PATH_SEPARATOR));

        List<FileItemResult> fileItemList = new ArrayList<>();

        // 调用递归方法获取文件列表
        listFilesInDirectory(bucketName, fullPath, path, fileItemList);

        return fileItemList;
    }

    private void listFilesInDirectory(String bucketName, String fullPath, String path, List<FileItemResult> fileItemList) {
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest()
                .withBucketName(bucketName)
                .withPrefix(fullPath) // 设置前缀为当前路径
                .withMaxKeys(1000)    // 每次最多返回 1000 个对象
                .withDelimiter("/");  // 使用 "/" 作为分隔符

        ObjectListing objectListing = s3Client.listObjects(listObjectsRequest);
        boolean isFirstWhile = true;

        do {
            if (!isFirstWhile) {
                objectListing = s3Client.listNextBatchOfObjects(objectListing); // 处理分页
            }

            // 处理文件
            for (S3ObjectSummary s : objectListing.getObjectSummaries()) {
                FileItemResult fileItemResult = new FileItemResult();

                // 跳过当前目录本身
                if (s.getKey().equals(fullPath)) {
                    continue;
                }

                // 获取文件名并去除前导斜杠
                String fileName = s.getKey().substring(fullPath.length());
                if (fileName.startsWith(ZFileConstant.PATH_SEPARATOR)) {
                    fileName = fileName.substring(1); // 去掉开头的斜杠
                }

                fileItemResult.setName(fileName);
                fileItemResult.setSize(s.getSize());
                fileItemResult.setTime(s.getLastModified());
                fileItemResult.setType(FileTypeEnum.FILE);
                fileItemResult.setPath(path); // 当前路径

                // 构造完整路径并生成下载 URL
                String fullPathAndName = StringUtils.concat(path, fileItemResult.getName());
                fileItemResult.setUrl(getDownloadUrl(fullPathAndName));

                fileItemList.add(fileItemResult);
            }

            // 处理文件夹
            for (String commonPrefix : objectListing.getCommonPrefixes()) {
                FileItemResult fileItemResult = new FileItemResult();

                // 获取文件夹名称，去掉前导路径并修正末尾斜杠
                String folderName = commonPrefix.substring(fullPath.length(), commonPrefix.length() - 1);
                if (StrUtil.isEmpty(folderName) || StrUtil.equals(folderName, StringUtils.DELIMITER_STR)) {
                    continue; // 跳过无效的文件夹名称
                }

                fileItemResult.setName(folderName);
                fileItemResult.setType(FileTypeEnum.FOLDER);
                fileItemResult.setPath(path); // 当前路径

                fileItemList.add(fileItemResult);

                // 递归处理子文件夹
                String subFolderPath = path + folderName + ZFileConstant.PATH_SEPARATOR; // 修正路径拼接
                String subFolderFullPath = commonPrefix; // 子文件夹的完整路径
                listFilesInDirectory(bucketName, subFolderFullPath, subFolderPath, fileItemList);
            }

            isFirstWhile = false;
        } while (objectListing.isTruncated()); // 处理分页
    }


    @Override
    public FileItemResult getFileItem(String pathAndName) {
        try {
            String fileName = FileUtil.getName(pathAndName);
            String parentPath = StringUtils.getParentPath(pathAndName);
            String trimStartPath = StringUtils.concatTrimStartSlashes(param.getBasePath(), pathAndName);

            // 可能抛出异常的代码包裹在try块中
            ObjectMetadata objectMetadata;
            try {
                objectMetadata = s3Client.getObjectMetadata(param.getBucketName(), trimStartPath);
            } catch (Exception e) { // 捕获所有类型的异常
                return null; // 任何异常都返回null
            }

            FileItemResult fileItemResult = new FileItemResult();
            fileItemResult.setName(fileName);
            fileItemResult.setSize(objectMetadata.getInstanceLength());
            fileItemResult.setTime(objectMetadata.getLastModified());
            fileItemResult.setType(FileTypeEnum.FILE);
            fileItemResult.setPath(parentPath);
            fileItemResult.setUrl(getDownloadUrl(pathAndName));
            return fileItemResult;
        } catch (Exception e) {
            return null; // 其他代码异常也返回null
        }
    }

    @Override
    public boolean newFolder(String path, String name) {
        name = StringUtils.trimSlashes(name);
        String fullPath = StringUtils.concat(param.getBasePath(), path, name, ZFileConstant.PATH_SEPARATOR);
        fullPath = StringUtils.trimStartSlashes(fullPath);
        PutObjectRequest putObjectRequest = new PutObjectRequest(param.getBucketName(), fullPath, EMPTY_INPUT_STREAM, null);
        PutObjectResult putObjectResult = s3Client.putObject(putObjectRequest);
        return putObjectResult != null;
    }

    @Override
    public boolean deleteFile(String path, String name) {
        String fullPath = StringUtils.concat(param.getBasePath(), path, name);
        fullPath = StringUtils.trimStartSlashes(fullPath);
        s3Client.deleteObject(param.getBucketName(), fullPath);
        return true;
    }

//    @Override
//    public boolean deleteFolder(String path, String name) {
//        String fullPath = StringUtils.concat(param.getBasePath(), path, name);
//        fullPath = StringUtils.trimStartSlashes(fullPath);
//        s3Client.deleteObject(param.getBucketName(), fullPath + '/');
//        return true;
//    }

    //todo  改造以后的删除文件夹 上面是改造之前原本的
    /**
     * 删除文件夹
     *
     * @param path 文件夹路径
     * @param name 文件夹名称
     * @return 是否删除成功
     */
    @Override
    public boolean deleteFolder(String path, String name) {
        // 构造完整路径
        String fullPath = StringUtils.concat(param.getBasePath(), path, name);
        fullPath = StringUtils.trimStartSlashes(fullPath);
        if (!fullPath.endsWith("/")) {
            fullPath += "/"; // 确保路径以 / 结尾
        }
        try {
            // 删除本地文件夹及其内容
            deleteLocalFolder(fullPath);
            // 删除 Minio 文件夹及其内容
            deleteMinioFolder(param.getBucketName(), fullPath);
            return true;
        } catch (Exception e) {
            LOGGER.error("删除文件夹失败: " + e.getMessage(), e);
            return false;
        }
    }


    // 递归删除本地文件夹及其内容
    private void deleteLocalFolder(String fullPath) {
        File folder = new File(fullPath);
        if (folder.exists() && folder.isDirectory()) {
            try {
                deleteLocalFolderRecursive(folder);
                LOGGER.info("本地文件夹删除成功: " + fullPath);
            } catch (IOException e) {
                LOGGER.error("本地文件夹删除失败: " + e.getMessage());
            }
        } else {
            LOGGER.error("本地文件夹不存在，跳过删除: " + fullPath);
        }
    }

    // 递归删除本地文件夹及其内容
    private void deleteLocalFolderRecursive(File folder) throws IOException {
        File[] files = folder.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteLocalFolderRecursive(file); // 递归删除子文件夹
                } else {
                    if (!file.delete()) {
                        throw new IOException("无法删除文件: " + file.getAbsolutePath());
                    }
                }
            }
        }
        if (!folder.delete()) {
            throw new IOException("无法删除文件夹: " + folder.getAbsolutePath());
        }
    }
    // 删除 Minio 文件夹及其内容
    private void deleteMinioFolder(String bucketName, String folderPath) {
        try {
            // 列出文件夹下的所有对象
            ListObjectsV2Request listRequest = new ListObjectsV2Request()
                    .withBucketName(bucketName)
                    .withPrefix(folderPath); // 前缀匹配文件夹路径

            ListObjectsV2Result listResult;
            List<DeleteObjectsRequest.KeyVersion> objectsToDelete = new ArrayList<>();

            do {
                listResult = s3Client.listObjectsV2(listRequest);

                // 收集需要删除的对象
                for (S3ObjectSummary objectSummary : listResult.getObjectSummaries()) {
                    objectsToDelete.add(new DeleteObjectsRequest.KeyVersion(objectSummary.getKey()));
                }

                // 如果对象数量超过单次请求限制，继续分页列出
                listRequest.setContinuationToken(listResult.getNextContinuationToken());
            } while (listResult.isTruncated());

            // 批量删除对象
            if (!objectsToDelete.isEmpty()) {
                DeleteObjectsRequest deleteRequest = new DeleteObjectsRequest(bucketName)
                        .withKeys(objectsToDelete);
                s3Client.deleteObjects(deleteRequest);
                LOGGER.info("Minio 文件夹删除成功: " + folderPath);
            } else {
                LOGGER.error("Minio 文件夹不存在，跳过删除: " + folderPath);
            }
        } catch (Exception e) {
            LOGGER.error("Minio 文件夹删除失败: " + e.getMessage());
        }
    }


    /**
     *
     * @param   path
     *          文件路径
     *
     * @param   name
     *          桶名称
     *
     * @param   zipFile
     *          新文件名称
     *
     * @return
     */
    @Override
    public boolean UploadFiles(String name, String path, File zipFile) {
        PutObjectResult putObjectResult = s3Client.putObject( name,  path,  zipFile);
        return putObjectResult != null;
    }

    /**
     * 创建新文件夹
     *
     * @param   bucketName
     *          桶名称
     *
     * @param   key
     *          路径＋文件夹名称
     *
     * @return  ObjectMetadata对象
     */
    @Override
    public ObjectMetadata getObjectMetadata(String bucketName, String key) {
        ObjectMetadata objectMetadata = s3Client.getObjectMetadata(bucketName,key);
        return objectMetadata;
    }


    /**
     *
     *
     * @param   bucketName
     *          桶名称
     *
     * @param   key
     *          路径＋文件夹名称
     *
     * @return  S3Object对象
     */
    @Override
    public S3Object getObject(String bucketName, String key) {
        S3Object s3Object = s3Client.getObject(bucketName,key);
        return s3Object;
    }




    @Override
    public boolean renameFile(String path, String name, String newName) {
        String srcKey = StringUtils.concatTrimStartSlashes(param.getBasePath(), path, name);
        String destKey = StringUtils.concatTrimStartSlashes(param.getBasePath(), path, newName);

        TransferManager transferManager = TransferManagerBuilder.standard()
                .withS3Client(s3Client)
                .build();

        try {
            // 异步复制（适合大文件）
            Copy copy = transferManager.copy(
                    param.getBucketName(), srcKey,
                    param.getBucketName(), destKey
            );
            copy.waitForCompletion(); // 等待复制完成
            s3Client.deleteObject(param.getBucketName(), srcKey);
            return true;
        } catch (InterruptedException | AmazonServiceException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Rename failed", e);
        } finally {
            transferManager.shutdownNow();
        }
    }

    public boolean renameFileWithTransferManager(String path, String name, String newName) {
        String srcKey = StringUtils.concatTrimStartSlashes(param.getBasePath(), path, name);
        String destKey = StringUtils.concatTrimStartSlashes(param.getBasePath(), path, newName);

        TransferManager transferManager = TransferManagerBuilder.standard()
                .withS3Client(s3Client)
                .build();

        try {
            // 异步复制（适合大文件）
            Copy copy = transferManager.copy(
                    param.getBucketName(), srcKey,
                    param.getBucketName(), destKey
            );
            copy.waitForCompletion(); // 等待复制完成
            s3Client.deleteObject(param.getBucketName(), srcKey);
            return true;
        } catch (InterruptedException | AmazonServiceException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Rename failed", e);
        } finally {
            transferManager.shutdownNow();
        }
    }


    //todo 改造之前的重命名
//    @Override
//    public boolean renameFolder(String path, String name, String newName) {
//        throw new UnsupportedOperationException("该存储类型不支持此操作");
//    }

    @Override
    public boolean renameFolder(String path, String name, String newName) {
        try {
            // 规范化路径
            String oldPrefix = null;
            String newPrefix = null;
            if (StringUtils.isEmpty(path)) {
                oldPrefix = path + name;
                newPrefix = path + newName;
            } else {
                oldPrefix = path + "/" + name;
                newPrefix = path + "/" + newName;
            }

            String bucketName = param.getBucketName();
            // 打印路径，检查是否正确
            System.out.println("Old Prefix: " + oldPrefix);
            System.out.println("New Prefix: " + newPrefix);
            // 列出旧路径下的所有对象
            ListObjectsV2Request request = new ListObjectsV2Request()
                    .withBucketName(bucketName)
                    .withPrefix(oldPrefix)
                    .withMaxKeys(500); // 每次查询最多返回 500 个对象
            ListObjectsV2Result result;
            do {
                result = s3Client.listObjectsV2(request);
                System.out.println("Object Summaries: " + result.getObjectSummaries());
                // 遍历对象并重命名
                for (S3ObjectSummary objectSummary : result.getObjectSummaries()) {
                    String oldKey = objectSummary.getKey();
                    String newKey = oldKey.replace(oldPrefix, newPrefix);
                    // 打印关键变量
                    System.out.println("Old Key: " + oldKey);
                    System.out.println("New Key: " + newKey);
                    // 获取源对象的元数据
                    ObjectMetadata metadata = s3Client.getObjectMetadata(bucketName, oldKey);
                    // 使用 CopyObjectRequest 复制对象
                    CopyObjectRequest copyObjectRequest = new CopyObjectRequest(bucketName, oldKey, bucketName, newKey)
                            .withNewObjectMetadata(metadata);
                    s3Client.copyObject(copyObjectRequest);
                    // 删除旧对象
                    s3Client.deleteObject(bucketName, oldKey);
                }

                // 如果对象数量超过单次查询限制，继续查询下一页
                request.setContinuationToken(result.getNextContinuationToken());
            } while (result.isTruncated());

            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    @Override
    public String getUploadUrl(String path, String name, Long size) {
        String bucketName = param.getBucketName();
        String uploadToPath = StringUtils.concat(param.getBasePath(), path, name);
        uploadToPath = StringUtils.trimStartSlashes(uploadToPath);

        GeneratePresignedUrlRequest req =
                new GeneratePresignedUrlRequest(bucketName, uploadToPath, HttpMethod.PUT);
        URL url = s3Client.generatePresignedUrl(req);

        return url.toExternalForm();
    }

    protected void setUploadCors() {
        if (param.isAutoConfigCors()) {
            try {
                // 获取历史的 CORS 规则
                BucketCrossOriginConfiguration bucketCrossOriginConfiguration = s3Client.getBucketCrossOriginConfiguration(param.getBucketName());
                if (bucketCrossOriginConfiguration == null) {
                    bucketCrossOriginConfiguration = new BucketCrossOriginConfiguration();
                }
                List<CORSRule> corsRules = bucketCrossOriginConfiguration.getRules();
                if (corsRules == null) {
                    corsRules = new ArrayList<>();
                }


                // 当前要添加的规则
                List<String> allowOrigins = new ArrayList<>();
                if (StrUtil.isNotEmpty(systemConfigService.getDomain())) {
                    allowOrigins.add(systemConfigService.getDomain());
                }
                if (StrUtil.isNotEmpty(systemConfigService.getFrontDomain())) {
                    allowOrigins.add(systemConfigService.getFrontDomain());
                }

                if (allowOrigins.isEmpty()) {
                    throw new IllegalStateException("请先在 \"站点设置\" 中配置站点域名");
                }

                // 从历史规则中查找是否已经存在, 如果存在则不添加.
                boolean presentCorsRules = corsRules.stream().anyMatch(corsRule -> {
                    List<String> origins = corsRule.getAllowedOrigins();
                    return new HashSet<>(origins).containsAll(allowOrigins);
                });

                if (presentCorsRules) {
                    log.info("存储源 {} CORS 规则已经存在，不需要重复添加", storageId);
                    return;
                }

                CORSRule corsRule = new CORSRule();
                corsRule.setAllowedMethods(CORSRule.AllowedMethods.PUT, CORSRule.AllowedMethods.GET);
                corsRule.setAllowedOrigins(allowOrigins);
                corsRules.add(corsRule);
                bucketCrossOriginConfiguration.setRules(corsRules);

                SetBucketCrossOriginConfigurationRequest setBucketCrossOriginConfigurationRequest =
                        new SetBucketCrossOriginConfigurationRequest(param.getBucketName(), bucketCrossOriginConfiguration);
                log.info("{} 设置 CORS 规则: [allowedMethods={}, allowOrigins={}]", getStorageSimpleInfo(), corsRule.getAllowedMethods(), corsRule.getAllowedOrigins());
                s3Client.setBucketCrossOriginConfiguration(setBucketCrossOriginConfigurationRequest);
            } catch (Exception e) {
                throw new StorageSourceAutoConfigCorsException("设置跨域失败，请检查 API 密钥、地域、存储器名称是否正确，或 API 是否有权限设置跨域", e, param);
            }
        }
    }

}
