package com.ycwl.basic.storage.adapters;


import com.aliyun.oss.ClientException;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.DeleteObjectsRequest;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyun.oss.model.ListObjectsV2Request;
import com.aliyun.oss.model.ListObjectsV2Result;
import com.aliyun.oss.model.OSSObjectSummary;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.oss.model.PutObjectRequest;
import com.ycwl.basic.storage.entity.AliOssStorageConfig;
import com.ycwl.basic.storage.entity.StorageConfig;
import com.ycwl.basic.storage.entity.StorageFileObject;
import com.ycwl.basic.storage.enums.StorageAcl;
import com.ycwl.basic.storage.exceptions.StorageConfigException;
import com.ycwl.basic.storage.exceptions.StorageException;
import com.ycwl.basic.storage.exceptions.UploadFileFailedException;
import com.ycwl.basic.storage.utils.StorageUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

final public class AliOssAdapter extends AStorageAdapter {
    private AliOssStorageConfig config;

    @Override
    public void loadConfig(Map<String, String> _config) {
        AliOssStorageConfig config = new AliOssStorageConfig();
        config.setAccessKeyId(_config.get("accessKeyId"));
        config.setAccessKeySecret(_config.get("accessKeySecret"));
        config.setBucketName(_config.get("bucketName"));
        config.setEndpoint(_config.get("endpoint"));
        config.setRegion(_config.get("region"));
        config.setUrl(_config.get("url"));
        config.setPrefix(_config.get("prefix"));
        config.checkEverythingOK();
        this.config = config;
    }

    @Override
    public void setConfig(StorageConfig config) {
        if (config == null) {
            throw new StorageConfigException("配置为空");
        }
        if (config instanceof AliOssStorageConfig) {
            this.config = (AliOssStorageConfig) config;
        } else {
            throw new StorageConfigException("配置类型错误，传入的类为：" + config.getClass().getName());
        }
    }

    @Override
    public String uploadFile(String contentType, InputStream inputStream, String ...path) {
        if (inputStream == null) {
            return null;
        }
        String fullPath = buildPath(path);
        try (OSSWrapper wrapper = getOssClient()) {
            OSS ossClient = wrapper.getOSSClient();
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(inputStream.available());
            if (StringUtils.isNotBlank(contentType)) {
                metadata.setContentType(contentType);
            }
            PutObjectRequest putObjectRequest = new PutObjectRequest(config.getBucketName(), fullPath, inputStream);
            ossClient.putObject(putObjectRequest);
            return getUrl(path);
        } catch (Exception e) {
            throw new UploadFileFailedException("上传文件失败：" + e.getMessage());
        }
    }


    @Override
    public boolean deleteFile(String... path) {
        try (OSSWrapper wrapper = getOssClient()) {
            OSS ossClient = wrapper.getOSSClient();
            ossClient.deleteObject(config.getBucketName(), buildPath(path));
            return true;
        } catch (ClientException e) {
            return false;
        }
    }

    @Override
    public String getUrl(String... path) {
        return config.getUrl() + "/" + buildPath(path);
    }

    @Override
    public String getUrlForDownload(Date expireDate, String... path) {
        try (OSSWrapper wrapper = getOssClient()) {
            OSS ossClient = wrapper.getOSSClient();
            URL url = ossClient.generatePresignedUrl(config.getBucketName(), buildPath(path), expireDate, HttpMethod.GET);
            return url.toString();
        }
    }

    @Override
    public String getUrlForUpload(Date expireDate, String contentType, String... path) {
        try (OSSWrapper wrapper = getOssClient()) {
            OSS ossClient = wrapper.getOSSClient();
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(config.getBucketName(), buildPath(path), HttpMethod.PUT);
            if (StringUtils.isNotBlank(contentType)) {
                request.setContentType(contentType);
            }
            request.setExpiration(expireDate);
            URL url = ossClient.generatePresignedUrl(request);
            return url.toString();
        }
    }

    @Override
    public List<StorageFileObject> listDir(String... path) {
        ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request(config.getBucketName());
        listObjectsV2Request.setPrefix(buildPath(path) + "/");
        listObjectsV2Request.setMaxKeys(1000);
        boolean isTruncated = true;
        String continuationToken = null;
        List<OSSObjectSummary> objectList = new ArrayList<>();
        try (OSSWrapper wrapper = getOssClient()) {
            OSS ossClient = wrapper.getOSSClient();
            while (isTruncated) {
                if (continuationToken != null) {
                    listObjectsV2Request.setContinuationToken(continuationToken);
                }

                // 列举文件。
                ListObjectsV2Result result = ossClient.listObjectsV2(listObjectsV2Request);

                objectList.addAll(result.getObjectSummaries());

                isTruncated = result.isTruncated();
                continuationToken = result.getNextContinuationToken();
            }
            return objectList.stream().map(item -> {
                StorageFileObject object = new StorageFileObject();
                object.setPath(getRelativePath(item.getKey().substring(0, item.getKey().lastIndexOf("/"))));
                object.setName(item.getKey().substring(item.getKey().lastIndexOf("/") + 1));
                object.setSize(item.getSize());
                object.setModifyTime(item.getLastModified());
                object.setRawObject(item);
                return object;
            }).collect(Collectors.toList());
        } catch (OSSException e) {
            throw new StorageException("列举文件失败：" + e.getErrorMessage());
        }
    }

    @Override
    public boolean deleteDir(String... path) {
        List<StorageFileObject> objectList = listDir(buildPath(path));
        if (objectList.isEmpty()) {
            return true;
        }
        try (OSSWrapper wrapper = getOssClient()) {
            OSS ossClient = wrapper.getOSSClient();
            int idx = 0;
            int batchSize = 999;
            while (objectList.size() > idx) {
                if (objectList.size() - idx < batchSize) {
                    batchSize = objectList.size() - idx;
                }
                List<StorageFileObject> subList = objectList.subList(idx, idx + batchSize);
                idx += batchSize;
                DeleteObjectsRequest request = new DeleteObjectsRequest(config.getBucketName());
                request.setKeys(subList.stream().map(StorageFileObject::getFullPath).collect(Collectors.toList()));
                try {
                    ossClient.deleteObjects(request);
                } catch (OSSException e) {
                    return false;
                }
            }
            return true;
        } catch (OSSException e) {
            return false;
        }
    }

    private CannedAccessControlList convertAcl(StorageAcl acl) {
        return switch (acl) {
            case PUBLIC_READ -> CannedAccessControlList.PublicRead;
            case PUBLIC_READ_WRITE -> CannedAccessControlList.PublicReadWrite;
            case PRIVATE -> CannedAccessControlList.Private;
            case AUTHENTICATED_READ -> CannedAccessControlList.AuthenticatedRead;
            default -> CannedAccessControlList.Default;
        };
    }

    @Override
    public boolean setAcl(StorageAcl acl, String... path) {
        try (OSSWrapper wrapper = getOssClient()) {
            OSS ossClient = wrapper.getOSSClient();
            ossClient.setObjectAcl(config.getBucketName(), buildPath(path), convertAcl(acl));
            return true;
        } catch (OSSException e) {
            return false;
        }
    }

    @Override
    public boolean isExists(String ...path) {
        try (OSSWrapper wrapper = getOssClient()) {
            OSS ossClient = wrapper.getOSSClient();
            return ossClient.doesObjectExist(config.getBucketName(), buildPath(path));
        }
    }

    private OSSWrapper getOssClient() {
        OSS ossClient = new OSSClientBuilder().build(config.getEndpoint(), config.getAccessKeyId(), config.getAccessKeySecret());
        return new OSSWrapper(ossClient);
    }

    private String buildPath(String ...paths) {
        if (StringUtils.isNotBlank(config.getPrefix())) {
            return StorageUtil.joinPath(config.getPrefix(), paths);
        } else {
            return StorageUtil.joinPath(paths);
        }
    }

    private String getRelativePath(String path) {
        return StorageUtil.getRelativePath(path, config.getPrefix());
    }

    public static class OSSWrapper implements AutoCloseable {
        private final OSS ossClient;

        public OSSWrapper(OSS ossClient) {
            this.ossClient = ossClient;
        }

        // 提供对原始对象的方法访问
        public OSS getOSSClient() {
            return ossClient;
        }

        @Override
        public void close() {
            // 在此处实现资源关闭逻辑（如调用 OSS 的关闭方法）
            ossClient.shutdown(); // 假设 OSS 提供 shutdown 方法关闭资源
        }
    }
}
