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 volatile AliOssStorageConfig config;
    private volatile OSS ossClient;
    private final Object clientLock = new Object();
    private final Object configLock = new Object();

    @Override
    public String identity() {
        AliOssStorageConfig currentConfig = this.config;
        if (currentConfig == null) {
            return "ALI_OSS_UNCONFIGURED";
        }
        return currentConfig.identity();
    }

    @Override
    public void loadConfig(Map<String, String> _config) {
        AliOssStorageConfig newConfig = new AliOssStorageConfig();
        newConfig.setAccessKeyId(_config.get("accessKeyId"));
        newConfig.setAccessKeySecret(_config.get("accessKeySecret"));
        newConfig.setBucketName(_config.get("bucketName"));
        newConfig.setEndpoint(_config.get("endpoint"));
        newConfig.setRegion(_config.get("region"));
        newConfig.setUrl(_config.get("url"));
        newConfig.setPrefix(_config.get("prefix"));
        newConfig.checkEverythingOK();
        
        synchronized (configLock) {
            this.config = newConfig;
            // 配置更新后，需要重置客户端连接
            resetClient();
        }
    }

    @Override
    public void setConfig(StorageConfig config) {
        if (config == null) {
            throw new StorageConfigException("配置为空");
        }
        if (!(config instanceof AliOssStorageConfig)) {
            throw new StorageConfigException("配置类型错误，传入的类为：" + config.getClass().getName());
        }
        
        synchronized (configLock) {
            this.config = (AliOssStorageConfig) config;
            // 配置更新后，需要重置客户端连接
            resetClient();
        }
    }

    @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();
            
            // 读取完整内容并计算实际长度
            byte[] bytes = inputStream.readAllBytes();
            metadata.setContentLength(bytes.length);
            
            if (StringUtils.isNotBlank(contentType)) {
                metadata.setContentType(contentType);
            }
            
            // 使用字节数组创建新的 InputStream
            try (InputStream byteInputStream = new java.io.ByteArrayInputStream(bytes)) {
                AliOssStorageConfig currentConfig = this.config;
                if (currentConfig == null) {
                    throw new StorageConfigException("存储适配器未配置");
                }
                PutObjectRequest putObjectRequest = new PutObjectRequest(currentConfig.getBucketName(), fullPath, byteInputStream, metadata);
                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();
            AliOssStorageConfig currentConfig = this.config;
            if (currentConfig == null) {
                return false;
            }
            ossClient.deleteObject(currentConfig.getBucketName(), buildPath(path));
            return true;
        } catch (ClientException e) {
            return false;
        }
    }

    @Override
    public String getUrl(String... path) {
        AliOssStorageConfig currentConfig = this.config;
        if (currentConfig == null) {
            throw new StorageConfigException("存储适配器未配置");
        }
        return currentConfig.getUrl() + "/" + buildPath(path);
    }

    @Override
    public String getUrlForDownload(Date expireDate, String... path) {
        try (OSSWrapper wrapper = getOssClient()) {
            OSS ossClient = wrapper.getOSSClient();
            AliOssStorageConfig currentConfig = this.config;
            if (currentConfig == null) {
                throw new StorageConfigException("存储适配器未配置");
            }
            URL url = ossClient.generatePresignedUrl(currentConfig.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();
            AliOssStorageConfig currentConfig = this.config;
            if (currentConfig == null) {
                throw new StorageConfigException("存储适配器未配置");
            }
            GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(currentConfig.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) {
        AliOssStorageConfig currentConfig = this.config;
        if (currentConfig == null) {
            throw new StorageConfigException("存储适配器未配置");
        }
        ListObjectsV2Request listObjectsV2Request = new ListObjectsV2Request(currentConfig.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;
                AliOssStorageConfig currentConfig = this.config;
                if (currentConfig == null) {
                    return false;
                }
                DeleteObjectsRequest request = new DeleteObjectsRequest(currentConfig.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();
            AliOssStorageConfig currentConfig = this.config;
            if (currentConfig == null) {
                return false;
            }
            ossClient.setObjectAcl(currentConfig.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();
            AliOssStorageConfig currentConfig = this.config;
            if (currentConfig == null) {
                return false;
            }
            return ossClient.doesObjectExist(currentConfig.getBucketName(), buildPath(path));
        }
    }

    private OSSWrapper getOssClient() {
        AliOssStorageConfig currentConfig = this.config;
        if (currentConfig == null) {
            throw new StorageConfigException("存储适配器未配置");
        }
        
        if (ossClient == null) {
            synchronized (clientLock) {
                if (ossClient == null) {
                    // 在同步块内再次检查配置，确保配置一致性
                    currentConfig = this.config;
                    if (currentConfig == null) {
                        throw new StorageConfigException("存储适配器未配置");
                    }
                    ossClient = new OSSClientBuilder().build(
                        currentConfig.getEndpoint(), 
                        currentConfig.getAccessKeyId(), 
                        currentConfig.getAccessKeySecret()
                    );
                }
            }
        }
        return new OSSWrapper(ossClient, false); // false 表示不自动关闭共享客户端
    }
    
    // 重置客户端连接（配置更新时调用）
    private void resetClient() {
        synchronized (clientLock) {
            if (ossClient != null) {
                ossClient.shutdown();
                ossClient = null;
            }
        }
    }
    
    // 手动关闭客户端的方法，用于适配器销毁时
    public void shutdown() {
        synchronized (clientLock) {
            if (ossClient != null) {
                ossClient.shutdown();
                ossClient = null;
            }
        }
    }

    private String buildPath(String ...paths) {
        AliOssStorageConfig currentConfig = this.config;
        if (currentConfig != null && StringUtils.isNotBlank(currentConfig.getPrefix())) {
            return StorageUtil.joinPath(currentConfig.getPrefix(), paths);
        } else {
            return StorageUtil.joinPath(paths);
        }
    }

    private String getRelativePath(String path) {
        AliOssStorageConfig currentConfig = this.config;
        String prefix = currentConfig != null ? currentConfig.getPrefix() : null;
        return StorageUtil.getRelativePath(path, prefix);
    }

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

        public OSSWrapper(OSS ossClient) {
            this(ossClient, true);
        }
        
        public OSSWrapper(OSS ossClient, boolean autoClose) {
            this.ossClient = ossClient;
            this.autoClose = autoClose;
        }

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

        @Override
        public void close() {
            // 只有非共享客户端才自动关闭
            if (autoClose && ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}
