package com.rj.utils;

import com.rj.config.MinIOConfig;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * MinIO 工具类
 * 提供文件上传、下载、删除等常用操作
 * 
 * @author rj
 * @date 2025-10-02
 */
@Slf4j
@Component
public class MinIOUtil {

    @Autowired
    private MinioClient minioClient;
    
    @Autowired
    private MinIOConfig minioConfig;

    /**
     * 检查存储桶是否存在
     * 
     * @param bucketName 存储桶名称
     * @return 是否存在
     */
    public boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            log.error("检查存储桶是否存在失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 创建存储桶
     * 
     * @param bucketName 存储桶名称
     * @return 是否创建成功
     */
    public boolean createBucket(String bucketName) {
        try {
            if (!bucketExists(bucketName)) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                log.info("存储桶创建成功: {}", bucketName);
                return true;
            }
            log.info("存储桶已存在: {}", bucketName);
            return true;
        } catch (Exception e) {
            log.error("创建存储桶失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取所有存储桶
     * 
     * @return 存储桶列表
     */
    public List<String> getAllBuckets() {
        try {
            List<Bucket> buckets = minioClient.listBuckets();
            List<String> bucketNames = new ArrayList<>();
            for (Bucket bucket : buckets) {
                bucketNames.add(bucket.name());
            }
            return bucketNames;
        } catch (Exception e) {
            log.error("获取存储桶列表失败: {}", e.getMessage());
            return new ArrayList<>();
        }
    }

    /**
     * 删除存储桶
     * 
     * @param bucketName 存储桶名称
     * @return 是否删除成功
     */
    public boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            log.info("存储桶删除成功: {}", bucketName);
            return true;
        } catch (Exception e) {
            log.error("删除存储桶失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 上传文件
     * 
     * @param file 文件
     * @param bucketName 存储桶名称
     * @return 文件名
     */
    public String uploadFile(MultipartFile file, String bucketName) {
        return uploadFile(file, bucketName, null);
    }

    /**
     * 上传文件
     * 
     * @param file 文件
     * @param bucketName 存储桶名称
     * @param objectName 对象名称（文件名），如果为null则自动生成
     * @return 文件名
     */
    public String uploadFile(MultipartFile file, String bucketName, String objectName) {
        try {
            // 确保存储桶存在
            createBucket(bucketName);

            // 生成文件名
            if (objectName == null || objectName.trim().isEmpty()) {
                objectName = generateFileName(file.getOriginalFilename());
            }

            // 上传文件
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build()
            );

            log.info("文件上传成功: {}/{}", bucketName, objectName);
            return objectName;

        } catch (Exception e) {
            log.error("文件上传失败: {}", e.getMessage());
            throw new RuntimeException("文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 上传文件流
     * 
     * @param inputStream 文件流
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param contentType 内容类型
     * @return 是否上传成功
     */
    public boolean uploadFile(InputStream inputStream, String bucketName, String objectName, String contentType) {
        try {
            createBucket(bucketName);

            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(inputStream, -1, 10485760) // 10MB
                            .contentType(contentType)
                            .build()
            );

            log.info("文件流上传成功: {}/{}", bucketName, objectName);
            return true;

        } catch (Exception e) {
            log.error("文件流上传失败: {}", e.getMessage());
            return false;
        }
    }
    
    /**
     * 上传字节数组
     * 
     * @param data 字节数组
     * @param objectName 对象名称
     * @param contentType 内容类型
     * @return 对象名称
     */
    public String uploadFile(byte[] data, String objectName, String contentType) {
        try {
            // 使用配置中的默认存储桶
            String bucketName = minioConfig.getBucketName();
            
            // 确保存储桶存在
            createBucket(bucketName);
            
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .stream(new java.io.ByteArrayInputStream(data), data.length, -1)
                            .contentType(contentType)
                            .build()
            );

            log.info("字节数组上传成功: {}/{}, 大小: {} bytes", bucketName, objectName, data.length);
            return objectName;

        } catch (Exception e) {
            log.error("字节数组上传失败: {}", e.getMessage());
            throw new RuntimeException("字节数组上传失败: " + e.getMessage());
        }
    }

    /**
     * 下载文件
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 文件流
     */
    public InputStream downloadFile(String bucketName, String objectName) {
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
        } catch (Exception e) {
            log.error("文件下载失败: {}", e.getMessage());
            throw new RuntimeException("文件下载失败: " + e.getMessage());
        }
    }

    /**
     * 下载文件到本地
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param fileName 本地文件路径
     * @return 是否下载成功
     */
    public boolean downloadFile(String bucketName, String objectName, String fileName) {
        try {
            minioClient.downloadObject(
                    DownloadObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .filename(fileName)
                            .build()
            );
            log.info("文件下载成功: {}/{} -> {}", bucketName, objectName, fileName);
            return true;
        } catch (Exception e) {
            log.error("文件下载失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 删除文件
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 是否删除成功
     */
    public boolean removeFile(String bucketName, String objectName) {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
            log.info("文件删除成功: {}/{}", bucketName, objectName);
            return true;
        } catch (Exception e) {
            log.error("文件删除失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 获取文件信息
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 文件信息
     */
    public StatObjectResponse getFileInfo(String bucketName, String objectName) {
        try {
            return minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
        } catch (Exception e) {
            log.error("获取文件信息失败: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 获取文件列表
     * 
     * @param bucketName 存储桶名称
     * @return 文件列表
     */
    public List<String> listFiles(String bucketName) {
        return listFiles(bucketName, null);
    }

    /**
     * 获取文件列表
     * 
     * @param bucketName 存储桶名称
     * @param prefix 文件前缀
     * @return 文件列表
     */
    public List<String> listFiles(String bucketName, String prefix) {
        List<String> files = new ArrayList<>();
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(bucketName)
                            .prefix(prefix)
                            .build()
            );

            for (Result<Item> result : results) {
                Item item = result.get();
                files.add(item.objectName());
            }
        } catch (Exception e) {
            log.error("获取文件列表失败: {}", e.getMessage());
        }
        return files;
    }

    /**
     * 获取文件详细列表
     * 
     * @param bucketName 存储桶名称
     * @return 文件详细信息列表
     */
    public List<FileInfo> listFileDetails(String bucketName) {
        return listFileDetails(bucketName, null);
    }

    /**
     * 获取文件详细列表
     * 
     * @param bucketName 存储桶名称
     * @param prefix 文件前缀
     * @return 文件详细信息列表
     */
    public List<FileInfo> listFileDetails(String bucketName, String prefix) {
        List<FileInfo> files = new ArrayList<>();
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(bucketName)
                            .prefix(prefix)
                            .build()
            );

            for (Result<Item> result : results) {
                Item item = result.get();
                FileInfo fileInfo = new FileInfo();
                fileInfo.setObjectName(item.objectName());
                fileInfo.setSize(item.size());
                fileInfo.setLastModified(item.lastModified());
                fileInfo.setEtag(item.etag());
                files.add(fileInfo);
            }
        } catch (Exception e) {
            log.error("获取文件详细列表失败: {}", e.getMessage());
        }
        return files;
    }

    /**
     * 获取文件预签名URL（用于临时访问）
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @param expires 过期时间（秒）
     * @return 预签名URL
     */
    public String getPresignedObjectUrl(String bucketName, String objectName, int expires) {
        try {
            // 检查文件是否存在
            if (!objectExists(bucketName, objectName)) {
                log.error("文件不存在: bucket={}, object={}", bucketName, objectName);
                return null;
            }
            
            log.info("生成预签名URL: bucket={}, object={}, expires={}秒", bucketName, objectName, expires);
            
            String presignedUrl = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(bucketName)
                            .object(objectName)
                            .expiry(expires, TimeUnit.SECONDS)
                            .build()
            );
            
            log.info("生成的预签名URL: {}", presignedUrl);
            return presignedUrl;
        } catch (Exception e) {
            log.error("获取预签名URL失败: bucket={}, object={}, error={}", bucketName, objectName, e.getMessage());
            return null;
        }
    }

    /**
     * 检查对象是否存在
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 是否存在
     */
    public boolean objectExists(String bucketName, String objectName) {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .build()
            );
            return true;
        } catch (Exception e) {
            log.debug("对象不存在: bucket={}, object={}, error={}", bucketName, objectName, e.getMessage());
            return false;
        }
    }

    /**
     * 获取文件预签名URL（默认7天过期）
     * 
     * @param bucketName 存储桶名称
     * @param objectName 对象名称
     * @return 预签名URL
     */
    public String getPresignedObjectUrl(String bucketName, String objectName) {
        return getPresignedObjectUrl(bucketName, objectName, 7 * 24 * 3600); // 7天
    }

    /**
     * 复制文件
     * 
     * @param sourceBucketName 源存储桶名称
     * @param sourceObjectName 源对象名称
     * @param targetBucketName 目标存储桶名称
     * @param targetObjectName 目标对象名称
     * @return 是否复制成功
     */
    public boolean copyFile(String sourceBucketName, String sourceObjectName, 
                           String targetBucketName, String targetObjectName) {
        try {
            createBucket(targetBucketName);
            
            minioClient.copyObject(
                    CopyObjectArgs.builder()
                            .bucket(targetBucketName)
                            .object(targetObjectName)
                            .source(CopySource.builder()
                                    .bucket(sourceBucketName)
                                    .object(sourceObjectName)
                                    .build())
                            .build()
            );
            
            log.info("文件复制成功: {}/{} -> {}/{}", 
                    sourceBucketName, sourceObjectName, targetBucketName, targetObjectName);
            return true;
        } catch (Exception e) {
            log.error("文件复制失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 生成唯一文件名
     * 
     * @param originalFilename 原始文件名
     * @return 唯一文件名
     */
    private String generateFileName(String originalFilename) {
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
        String uuid = UUID.randomUUID().toString().replace("-", "");
        String extension = "";
        
        if (originalFilename != null && originalFilename.contains(".")) {
            extension = originalFilename.substring(originalFilename.lastIndexOf("."));
        }
        
        return timestamp + "_" + uuid + extension;
    }

    /**
     * 文件信息类
     */
    public static class FileInfo {
        private String objectName;
        private Long size;
        private java.time.ZonedDateTime lastModified;
        private String etag;

        // Getters and Setters
        public String getObjectName() {
            return objectName;
        }

        public void setObjectName(String objectName) {
            this.objectName = objectName;
        }

        public Long getSize() {
            return size;
        }

        public void setSize(Long size) {
            this.size = size;
        }

        public java.time.ZonedDateTime getLastModified() {
            return lastModified;
        }

        public void setLastModified(java.time.ZonedDateTime lastModified) {
            this.lastModified = lastModified;
        }

        public String getEtag() {
            return etag;
        }

        public void setEtag(String etag) {
            this.etag = etag;
        }

        @Override
        public String toString() {
            return "FileInfo{" +
                    "objectName='" + objectName + '\'' +
                    ", size=" + size +
                    ", lastModified=" + lastModified +
                    ", etag='" + etag + '\'' +
                    '}';
        }
    }
}
