package com.bonus.file.utils;

import com.bonus.common.core.domain.R;
import com.bonus.file.config.ObsConfig;
import com.bonus.file.entity.FileDetails;
import com.bonus.system.api.domain.SysFile;
import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @author bonus
 */
@Service
@ConditionalOnProperty(name = "storage.type", havingValue = "obs")
public class ObsUtils {

    @Resource
    private ObsConfig obsConfig;
    /**
     * obs 客户端
     */
    private ObsClient obsClient;

    /**
     * 初始化信息
     * 构建OSS客户端实例
     */
    @PostConstruct
    public void init() {
        obsClient = new ObsClient(obsConfig.getAk(), obsConfig.getSk(), obsConfig.getEndpoint());
    }


    /**
     * 上传文件到OBS
     *
     * @param file 要上传的文件
     * @return 上传结果
     */
    public SysFile uploadFile(String objectKey, File file) {
        if (file.length() < 100 * 1024 * 1024L) {
            obsClient.putObject(obsConfig.getBucket(), objectKey, file);
            return SysFile.builder().url(objectKey).build();
        } else {
            return multipartUploadFile(objectKey, file);
        }
    }


    /**
     * 分段上传文件到OBS
     *
     * @param objectKey 文件在OBS中的键
     * @param file      要上传的文件
     * @return 上传结果
     */
    public SysFile multipartUploadFile(String objectKey, File file) {
        // 创建一个固定大小为10的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        try {
            InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(obsConfig.getBucket(), objectKey);
            InitiateMultipartUploadResult initResult = obsClient.initiateMultipartUpload(initRequest);
            String uploadId = initResult.getUploadId();
            List<PartEtag> partETags = new ArrayList<>();
            // 每个分段大小为5MB
            long partSize = 5 * 1024 * 1024L;
            long fileLength = file.length();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }

            List<Future<UploadPartResult>> futures = new ArrayList<>();

            for (int i = 0; i < partCount; i++) {
                final int partNumber = i + 1;
                final long offset = partSize * i;
                final long size = Math.min(partSize, fileLength - offset);
                Future<com.obs.services.model.UploadPartResult> future = executorService.submit(() -> {
                    UploadPartRequest uploadPartRequest = new UploadPartRequest(
                            obsConfig.getBucket(),
                            objectKey,
                            size,
                            offset,
                            file
                    );
                    uploadPartRequest.setUploadId(uploadId);
                    uploadPartRequest.setPartNumber(partNumber);

                    return obsClient.uploadPart(uploadPartRequest);
                });
                futures.add(future);
            }

            for (Future<com.obs.services.model.UploadPartResult> future : futures) {
                com.obs.services.model.UploadPartResult uploadPartResult = future.get();
                partETags.add(new PartEtag(uploadPartResult.getEtag(), uploadPartResult.getPartNumber()));
            }

            executorService.shutdown();
            executorService.awaitTermination(1, TimeUnit.HOURS);

            // 完成分段上传
            CompleteMultipartUploadRequest completeRequest = new CompleteMultipartUploadRequest(
                    obsConfig.getBucket(),
                    objectKey,
                    uploadId,
                    partETags
            );
            obsClient.completeMultipartUpload(completeRequest);

            return SysFile.builder().url(objectKey).build();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            // 关闭线程池
            executorService.shutdown();
        }
    }

    /**
     * 下载文件从OBS
     *
     * @param objectKey 文件在OBS中的键
     */
    public R<ObsObject> downloadFile(String objectKey) {
        if (doesObjectExist(objectKey)) {
            return R.fail("文件不存在");
        }
        return R.ok(obsClient.getObject(new GetObjectRequest(obsConfig.getBucket(), objectKey)));
    }

    /**
     * 删除文件从OBS
     *
     * @param objectKey 文件在OBS中的键
     * @return 如果删除成功则返回true，否则返回false
     */
    public boolean deleteFile(String objectKey) throws Exception {
        if (doesObjectExist(objectKey)) {
            throw new Exception("文件不存在");
        }
        obsClient.deleteObject(obsConfig.getBucket(), objectKey);
        return true;
    }

    /**
     * 判断文件是否存在
     *
     * @param objectKey 文件在OBS中的键
     * @return 如果文件存在则返回true，否则返回false
     */
    public boolean doesObjectExist(String objectKey) {
        try {
            return !obsClient.doesObjectExist(obsConfig.getBucket(), objectKey);
        } catch (Exception e) {
            return true;
        }
    }

    /**
     * 删除指定路径的所有对象，从而删除整个文件夹及其子文件夹。
     *
     * @param folderPath 文件夹路径（例如 "folder1/subfolder/"）
     */
    public void deleteFolderAndContents(String folderPath) {
        try {
            ListObjectsRequest request = new ListObjectsRequest(obsConfig.getBucket());
            request.setPrefix(folderPath); // 设置前缀为文件夹路径
            request.setDelimiter("/"); // 使用分隔符来获取文件夹内容

            ObjectListing objectListing;
            do {
                // 列出对象
                objectListing = obsClient.listObjects(request);
                List<ObsObject> objects = objectListing.getObjects();

                // 遍历对象并删除它们
                for (ObsObject object : objects) {
                    String objectKey = object.getObjectKey();
                    DeleteObjectResult result = obsClient.deleteObject(obsConfig.getBucket(), objectKey);
                    if (result.isDeleteMarker()) {
                        System.out.println("Deleted object: " + objectKey);
                    }
                }

                // 获取下一页的 marker，并在 request 中设置，继续下一次分页
                request.setMarker(objectListing.getNextMarker());
            } while (objectListing.isTruncated()); // isTruncated 为 true 表示有下一页

            System.out.println("Folder and all subfolders deleted successfully at path: " + folderPath);
        } catch (ObsException e) {
            System.err.println("Error deleting folder and its contents at path: " + folderPath);
            e.printStackTrace();
        }
    }


    /**
     * 在 OBS 中创建文件夹。
     *
     * @param folderPath 文件夹路径（例如 "folder1/subfolder/"）
     */
    public void createFolder(String folderPath) {
        try {
            // 确保 folderPath 以 "/" 结尾，这样 OBS 会将其视为文件夹
            if (!folderPath.endsWith("/")) {
                folderPath += "/";
            }

            // 创建一个空的字节流，作为文件夹占位文件的内容
            ByteArrayInputStream emptyContent = new ByteArrayInputStream(new byte[0]);

            // 构建上传请求
            PutObjectRequest request = new PutObjectRequest(obsConfig.getBucket(), folderPath, emptyContent);

            // 上传空文件，创建文件夹
            obsClient.putObject(request);
            System.out.println("Folder created successfully at path: " + folderPath);
        } catch (ObsException e) {
            System.err.println("Error creating folder at path: " + folderPath);
            e.printStackTrace();
        }
    }


    /**
     * 获取指定文件夹下所有文件夹和文件的详细信息。
     *
     * @param folderPath 要获取文件夹和文件的父文件夹路径（例如 "folder1/"）
     * @return 返回包含所有文件夹和文件详细信息的集合
     */
    public Set<FileDetails> getFilesAndFolders(String folderPath) {
        Set<FileDetails> filesAndFolders = new HashSet<>();
        try {
            ListObjectsRequest request = new ListObjectsRequest(obsConfig.getBucket());
            request.setPrefix(folderPath);
            request.setDelimiter("/");

            ObjectListing objectListing;
            do {
                objectListing = obsClient.listObjects(request);

                // 处理文件夹
                for (String commonPrefix : objectListing.getCommonPrefixes()) {
                    filesAndFolders.add(new FileDetails(
                            commonPrefix,
                            null,
                            0,
                            null,
                            null,
                            null,
                            true // isFolder
                    ));
                }

                // 处理文件
                for (ObsObject object : objectListing.getObjects()) {
                    filesAndFolders.add(new FileDetails(
                            object.getObjectKey().substring(object.getObjectKey().lastIndexOf("/") + 1),
                            object.getObjectKey(),
                            object.getMetadata().getContentLength(),
                            Date.from(object.getMetadata().getLastModified().toInstant()),
                            object.getMetadata().getEtag(),
                            object.getMetadata().getContentType(),
                            false // isFolder
                    ));
                }

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

        } catch (ObsException e) {
            System.err.println("Error listing files and folders at path: " + folderPath);
            e.printStackTrace();
        }
        return filesAndFolders;
    }





}
