package com.css.simulation.resource.common.infra.util;

import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
import lombok.SneakyThrows;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;

public class MinioUtil {


    /**
     * 复制文件
     */
    @SneakyThrows
    public static void copyObject(MinioClient minioClient, String sourceBucket, String sourceObject, String targetBucket, String targetObject) {
        minioClient.copyObject(CopyObjectArgs.builder().source(CopySource.builder()
                        .bucket(sourceBucket).object(sourceObject).build())
                .bucket(targetBucket).object(targetObject)
                .build());
    }

    /**
     * 复制文件
     */
    @SneakyThrows
    public static void moveObject(MinioClient minioClient, String sourceBucket, String sourceObject, String targetBucket, String targetObject) {
        minioClient.copyObject(CopyObjectArgs.builder().source(CopySource.builder()
                        .bucket(sourceBucket).object(sourceObject).build())
                .bucket(targetBucket).object(targetObject)
                .build());
        removeObject(minioClient, sourceBucket, sourceObject);
    }


    /**
     * 获取下一级文件列表
     *
     * @param minioClient minio 客户端
     * @param bucket      bucket 名称
     * @param prefix      目录名
     * @return 文件路径列表
     */
    public static List<String> listObjectsUnRecursive(MinioClient minioClient, String bucket, String prefix) throws Exception {

        if (prefix.startsWith("/")) {
            prefix = prefix.substring(1);
        }
        if (!prefix.endsWith("/")) {
            prefix = prefix + "/";
        }
        int length = prefix.length();
        Iterable<Result<Item>> objects = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucket)
                        .recursive(true)
                        .prefix(prefix)
                        .build()
        );
        Set<String> resultSet = new HashSet<>();
        for (Result<Item> next : objects) {
            Item item = next.get();
            String objectName = item.objectName();
            String substring = objectName.substring(length);
            String[] split = substring.split("/");
            resultSet.add(prefix + split[0]);
        }

        return new ArrayList<>(resultSet);
    }

    /**
     * 判断 bucket 是否存在
     */
    public static boolean isBucketExist(MinioClient minioClient, String bucketName) throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException, io.minio.errors.InternalException {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 创建 bucket
     */
    public static void createBucket(MinioClient minioClient, String bucketName) throws InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException, io.minio.errors.InternalException {
        if (!isBucketExist(minioClient, bucketName)) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 获取文件列表
     *
     * @param minioClient minio 客户端
     * @param bucket      bucket 名称
     * @param prefix      目录名
     * @return 文件路径列表
     */
    public static List<String> listObjects(MinioClient minioClient, String bucket, String prefix) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, io.minio.errors.InternalException {
        Iterable<Result<Item>> objects = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(bucket)
                        .recursive(true)
                        .prefix(prefix)
                        .build()
        );
        List<String> result = new ArrayList<>();
        for (Result<Item> next : objects) {
            Item item = next.get();
            String objectName = item.objectName();
            result.add(objectName);
        }

        return result;
    }


    /**
     * 获取预览路径
     *
     * @return 预览路径
     */
    public static String getPreviewUrl(MinioClient minioClient, Method method, String bucket, String object) throws io.minio.errors.ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, io.minio.errors.InternalException {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                .method(method)
                .bucket(bucket)
                .object(object)
                .expiry(3600)
                .build());
    }


    /**
     * 获取公网预览路径，需要设置 nginx 转发
     *
     * @param minioClient minio客户端
     * @param method      请求方式
     * @param bucket      bucket
     * @param object      object
     * @param privateIp   私网ip
     * @param publicIp    公网ip
     * @return 预览路径
     */
    @SneakyThrows
    public static String getPublicPreviewUrl(
            MinioClient minioClient,
            Method method,
            String bucket,
            String object,
            String privateIp,
            String publicIp
    ) {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        .method(method)
                        .bucket(bucket)
                        .object(object)
                        .build())
                .replace(privateIp, publicIp);
    }

    /**
     * 获取公网预览路径，需要设置 nginx 转发
     *
     * @param minioClient minio客户端
     * @param method      请求方式
     * @param bucket      bucket
     * @param object      object
     * @param privateIp   私网ip
     * @param publicIp    公网ip
     * @return 预览路径
     */
    @SneakyThrows
    public static String getPublicPreviewUrl2(
            MinioClient minioClient,
            Method method,
            String bucket,
            String object,
            Map<String, String> queryParam,
            String privateIp,
            String publicIp
    ) {
        return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        .method(method)
                        .bucket(bucket)
                        .object(object)
                        .expiry(60 * 60)//上传URL有效期
                        .extraQueryParams(queryParam)
                        .build())
                .replace(privateIp, publicIp);
    }

    /**
     * 通过文件路径上传文件上传文件
     */
    public static void uploadFromFile(
            MinioClient minioClient,
            String sourceFilePath,
            String bucketName,
            String objectName
    ) throws IOException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException, io.minio.errors.InternalException {
        minioClient.uploadObject(UploadObjectArgs.builder()
                .filename(sourceFilePath)
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    /**
     * 通过文件路径上传文件上传文件
     *
     * @param partSize 分片最小 5MB
     */
    public static void uploadFromStream(
            MinioClient minioClient,
            InputStream inputStream,
            long objectSize,
            long partSize,
            String bucketName,
            String objectName
    ) throws IOException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException, io.minio.errors.InternalException {
        minioClient.putObject(PutObjectArgs.builder()
                .stream(inputStream, objectSize, partSize)
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    /**
     * 通过文件路径上传文件上传文件
     */
    public static void uploadFromMultipartFile(
            MinioClient minioClient,
            MultipartFile multipartFile,
            String bucketName,
            String objectName
    ) throws IOException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException, io.minio.errors.InternalException {
        InputStream inputStream = multipartFile.getInputStream();
        long objectSize = multipartFile.getSize();
        //long partSize = 5 * 1024 * 1024L; // 分片最小 5M
        long partSize = -1; // 不分片
        minioClient.putObject(PutObjectArgs.builder()
                .stream(inputStream, objectSize, partSize)
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    /**
     * 下载文件
     */
    public static void downloadToFile(
            MinioClient minioClient,
            String bucketName,
            String objectName,
            String targetFilePath
    ) throws IOException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException, io.minio.errors.InternalException {
        minioClient.downloadObject(DownloadObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .filename(targetFilePath)
                .build());
    }

    /**
     * 下载文件
     */
    public static InputStream downloadToStream(
            MinioClient minioClient,
            String bucketName,
            String objectName
    ) throws IOException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException, io.minio.errors.InternalException {
        return minioClient.getObject(GetObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .build());
    }

    /**
     * 删除文件
     *
     * @param minioClient minio 客户端对象
     * @param bucket      桶
     * @param object      文件对象
     */
    @SneakyThrows
    public static void removeObject(MinioClient minioClient, String bucket, String object) {
        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(object).build());
    }
}
