package io.renren.common.utils;

import io.minio.MinioClient;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import io.renren.modules.sys.entity.algs.FileTest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Author: Ivan Q
 * @Date: 2021/6/10 19:49
 */
@Slf4j
public class MinIoUtils {
    private static MinioClient minioClient;
    private static String endpoint;
    private static String accessKey;
    private static String secretKey;

    public MinIoUtils() {
    }
    public MinIoUtils(String endpoint,String accessKey,String secretKey) {
        MinIoUtils.endpoint=endpoint;
        MinIoUtils.accessKey=accessKey;
        MinIoUtils.secretKey=secretKey;
        createMinioClient();
    }

    /**
     * Description 创建minIOClient
     */
    public void createMinioClient(){
        try {
            minioClient=new MinioClient(endpoint,accessKey,secretKey);
        } catch (InvalidEndpointException e) {
            e.printStackTrace();
        } catch (InvalidPortException e) {
            e.printStackTrace();
        }
    }

    /**
     * Description 根据存储桶名称查看该桶是否已经存在
     * @param bucketName
     * @return
     */
    public static boolean isBucketExists(String bucketName)
            throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        return minioClient.bucketExists(bucketName);
    }

    /**
     * Description 创建存储桶
     * @param bucketName 存储桶名称
     */
    public static void createBucket(String bucketName) throws IOException, InvalidKeyException, InvalidResponseException, RegionConflictException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        if(!minioClient.bucketExists(bucketName))   minioClient.makeBucket(bucketName);
    }

    /**
     * Description 获取目标文件url
     * @param bucket 文件所在桶名称
     * @param fileName 文件名(包括所在文件夹名)
     * @return
     */
    public static String getFileUrl(String bucket,String fileName){
        String objectUrl="";
        try {
            objectUrl= minioClient.getPresignedObjectUrl(Method.GET,bucket,fileName,1000,null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return objectUrl;
    }

    public static String getFileContent(String bucket,String fileName) {
        try {
            InputStream inputStream = minioClient.getObject(bucket, fileName);
            StringWriter writer = new StringWriter();
            IOUtils.copy(inputStream, writer, StandardCharsets.UTF_8);
            return writer.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * Description 下载目标文件夹并打包成ZIP文件
     *
     * @param bucket 文件所在桶名称
     * @param folderName 文件夹名
     * @return ZIP文件的URL
     */
    public static byte[] downloadAndZipFolder(String bucket, String folderName) {
        // 创建一个内存中的ZIP输出流
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
        try {
            Iterable<Result<Item>> minioObjects = minioClient.listObjects(bucket, folderName);
            for (Result<Item> result : minioObjects) {
                Item item = result.get();
                String fileName = item.objectName();
                // 创建ZIP条目
                ZipEntry zipEntry = new ZipEntry(fileName);
                zipOutputStream.putNextEntry(zipEntry);
                // 下载文件内容到ZIP输出流
                InputStream inputStream = minioClient.getObject(bucket, fileName);
                byte[] buffer = new byte[1024 * 10];
                BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream, 1024 * 10);
                int bytesRead;
                while ((bytesRead = bufferedInputStream.read(buffer)) != -1) {
                    zipOutputStream.write(buffer, 0, bytesRead);
                }
                bufferedInputStream.close();
                zipOutputStream.closeEntry();
            }
            // 关闭ZIP输出流
            zipOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return outputStream.toByteArray();
    }

    /**
     * Description:获取指定文件的InputStream
     * @param bucketName
     * @param objectName
     * @return
     */
    public static InputStream getFileInputStream(String bucketName,String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        return minioClient.getObject(bucketName,objectName);

    }

    /**
     * 测试从minio下载文件到指定本地目录
     * @param bucketName minio桶名
     * @param objectName minio文件名
     * @param fileName 本地地址
     */
    public static void downloadFile(String bucketName,String objectName,String fileName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        minioClient.getObject(bucketName,objectName,fileName);
    }


    /**
     * Description 上传本地文件到minio
     * @param bucketName 上传文件所在存储桶
     * @param minioFileName 上传到minio的文件名
     * @param localFileName 本地文件名称，即路径名/文件名
     * @param options 上传选项
     */
    public static void uploadLocalFile(String bucketName, String minioFileName, String localFileName, PutObjectOptions options)
            throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        minioClient.putObject(bucketName,minioFileName,localFileName,options);
    }


    /**
     * Description 上传MultipartFile种类文件到minio，若存在同名文件会被覆盖
     * @param file MultipartFile文件对象
     * @param bucketName 存储桶名称
     */
    public static void uploadMultipartFile(MultipartFile file,String bucketName,String objectName)
            throws IOException, NoSuchAlgorithmException, InsufficientDataException, InternalException, InvalidResponseException, InvalidKeyException, InvalidBucketNameException, XmlParserException, ErrorResponseException {
        PutObjectOptions putObjectOptions = new PutObjectOptions(file.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);
        putObjectOptions.setContentType(file.getContentType());
        minioClient.putObject(bucketName,objectName,file.getInputStream(),putObjectOptions);
    }

    /**
     * Description 通过InputSteam上传
     * @param inputStream
     * @param bucketName 存储桶名称
     * @param objectName 上传到minio的文件名
     */
    public static void uploadFileByInputStream(InputStream inputStream,Long fileSize,String bucketName,String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        PutObjectOptions putObjectOptions = new PutObjectOptions(fileSize, PutObjectOptions.MIN_MULTIPART_SIZE);
        minioClient.putObject(bucketName,objectName,inputStream,putObjectOptions);
    }

    /**
     * Description 根据桶名和前缀（即文件夹名）查询文件
     * @param bucketName
     * @param prefix
     * @return List<FileTest>
     */
    public static List<FileTest> listFiles(String bucketName,String prefix) throws XmlParserException, NoSuchAlgorithmException, InsufficientDataException, InternalException, InvalidResponseException, InvalidKeyException, InvalidBucketNameException, ErrorResponseException, IOException {
        Iterable<Result<Item>> minioObjects=minioClient.listObjects(bucketName,prefix);
        List<FileTest> fileList=new ArrayList<>();
        for(Result<Item> result:minioObjects){
            Item item=result.get();
            FileTest fileTest=new FileTest();
            // fileTest.setFilename(item.objectName().substring(prefix.length()+1));
            fileTest.setFilename(item.objectName());
            //设置大小显示
            DecimalFormat df=new DecimalFormat("#.00");
            if (item.size()>=1024*1024*1024){
                double size=item.size()/(1024.0*1024.0*1024.0);
                fileTest.setFilesize(df.format(size)+"GB");
            }else if(item.size()>=1024*1024){
                double size=item.size()/(1024.0*1024.0);
                fileTest.setFilesize(df.format(size)+"MB");
            }else if(item.size()>=1024){
                double size=item.size()/(1024.0);
                fileTest.setFilesize(df.format(size)+"KB");
            }else {
                fileTest.setFilesize(item.size()+"B");
            }
            DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").withZone(ZoneId.of("Asia/Shanghai"));
            fileTest.setLastModifiedTime(item.lastModified().format(dtf));

            fileList.add(fileTest);
        }
        return fileList;
    }

    /**
     * Description 根据桶名和前缀（即文件夹名）查询文件夹
     * @param bucketName 桶名称
     * @param prefix 前缀（文件夹名）
     * @return List<FileTest> 文件夹列表
     */
    public static List<FileTest> listFolders(String bucketName, String prefix) throws XmlParserException, NoSuchAlgorithmException, InsufficientDataException, InternalException, InvalidResponseException, InvalidKeyException, InvalidBucketNameException, ErrorResponseException, IOException {
        Iterable<Result<Item>> minioObjects = minioClient.listObjects(bucketName, prefix, false);
        Set<FileTest> fileSet = new HashSet<>();
        for (Result<Item> result : minioObjects) {
            Item item = result.get();
            String folder = getFolderFromObjectName(item.objectName(), prefix);
            if (StringUtils.isNotEmpty(folder)) {
                FileTest fileTest=new FileTest();
                fileTest.setFilename(prefix);
                fileSet.add(fileTest);
            }
        }
        return new ArrayList<>(fileSet);
    }

    /**
     * 从对象名称中提取文件夹名
     * @param objectName 对象名称
     * @param prefix 前缀（文件夹名）
     * @return 文件夹名
     */
    private static String getFolderFromObjectName(String objectName, String prefix) {
        int index = objectName.indexOf('/', prefix.length()); // 搜索第一个目录分隔符
        if (index > 0) {
            return objectName.substring(0, prefix.length());
        }
        return "";
    }

    /**
     * Description 复制文件
     * @param bucketName 存放复制文件的存储桶
     * @param objectName 复制文件名称
     * @param srcBucketName 源文件所在桶
     * @param srcObjectName 源文件名称
     */
    public static void copyFile(String bucketName,String objectName,String srcBucketName,String srcObjectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        minioClient.copyObject(bucketName,objectName,null,null,srcBucketName,srcObjectName,null,null);
    }


    /**
     * Description 复制文件夹
     *
     * @param bucketName 存放复制文件夹的存储桶
     * @param folderName 复制文件夹名称
     * @param srcBucketName 源文件夹所在桶
     * @param srcFolderName 源文件夹名称
     */
    public static void copyFolder(String bucketName,String folderName,String srcBucketName,String srcFolderName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        if (StringUtils.isEmpty(folderName)) {
            folderName = "";
        }
        Iterable<Result<Item>> results = minioClient.listObjects(srcBucketName, srcFolderName, false);
        for (Result<Item> result : results) {
            Item item = result.get();
            if (item.isDir()) {
                copyFolder(bucketName, folderName, srcBucketName, item.objectName());
            } else {
                copyFile(bucketName, folderName + item.objectName(), srcBucketName, item.objectName());
            }
        }
    }

    /**
     * Description 删除指定文件
     * @param bucketName
     * @param objectName
     */
    public static void deleteFile(String bucketName,String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        minioClient.removeObject(bucketName,objectName);

    }

    /**
     * Description 删除一个文件夹(包括其中的所有文件)
     * @param bucketName 存储桶名称
     * @param dirName 文件夹路径名/文件夹名
     */
    public static void deleteDir(String bucketName, String dirName) throws InvalidBucketNameException, InvalidResponseException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, IOException, XmlParserException, ErrorResponseException, InternalException {
        Iterable<Result<Item>> myObjects = minioClient.listObjects(bucketName, dirName, true);
        for (Result<Item> result : myObjects) {
            Item item = result.get();
            minioClient.removeObject(bucketName, item.objectName());
        }
    }

    /**
     * Description 批量删除指定前缀的文件
     * @param bucketName
     * @param prefix
     */
    public static void deleteFilesByPrefix(String bucketName,String prefix) throws XmlParserException, NoSuchAlgorithmException, InsufficientDataException, InternalException, InvalidResponseException, InvalidKeyException, InvalidBucketNameException, ErrorResponseException, IOException {
        Iterable<Result<Item>> minioObjects=minioClient.listObjects(bucketName,prefix);
        for(Result<Item> result:minioObjects){
            Item item=result.get();
            minioClient.removeObject(bucketName,item.objectName());
        }
    }

    /**
     * Description:显示存储桶中根路径下的所有文件，上面有一个listFiles方法，但如果文件直接存在存储桶中，会把首字母截掉，所以要用这个方法
     * @param bucketName
     * @return
     */
    public static List<FileTest> listBucketFiles(String bucketName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, InternalException, XmlParserException, InvalidBucketNameException, ErrorResponseException {
        Iterable<Result<Item>> minioObjects=minioClient.listObjects(bucketName);
        List<FileTest> fileList=new ArrayList<>();
        for(Result<Item> result:minioObjects){
            Item item=result.get();
            FileTest fileTest=new FileTest();
            fileTest.setFilename(item.objectName());
            //设置大小显示
            DecimalFormat df=new DecimalFormat("#.00");
            if (item.size()>=1024*1024*1024){
                double size=item.size()/(1024.0*1024.0*1024.0);
                fileTest.setFilesize(df.format(size)+"GB");
            }else if(item.size()>=1024*1024){
                double size=item.size()/(1024.0*1024.0);
                fileTest.setFilesize(df.format(size)+"MB");
            }else if(item.size()>=1024){
                double size=item.size()/(1024.0);
                fileTest.setFilesize(df.format(size)+"KB");
            }else {
                fileTest.setFilesize(item.size()+"B");
            }
            DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm").withZone(ZoneId.of("Asia/Shanghai"));
            fileTest.setLastModifiedTime(item.lastModified().format(dtf));

            fileList.add(fileTest);
        }
        return fileList;
    }




}
