package com.ho.business.common;

import com.ho.business.constant.DeviceTypeConstant;
import com.ho.common.tools.exception.BaseResponseCode;
import com.ho.common.tools.exception.BusinessException;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @Author yule
 * @Date 2023/1/3 14:15
 */
@Slf4j
@Component
public class MinioNewUtil {

    @Autowired
    MinioClient minioClient;

    @Value("${minio.endpoint}")
    String endpoint;

    @Value("${minio.prefixUrl}")
    String prefixUrl;

    
    /**
     * Minio文件上传
     *
     * @param file       文件实体
     * @param fileName   修饰过的文件名 非源文件名
     * @param bucketName 所存文件夹（桶名）
     * @return
     */
    public String minioUpload(MultipartFile file, String fileName, String bucketName) {
        String url = "";
        try {
            //桶使用固定minio
            String filestore="filestore";
            InputStream inputStream = file.getInputStream();
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(filestore).object(bucketName + "/" + fileName)
                    .stream(inputStream, file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
            //获取文件的存储路径
            url = getPreviewFileUrl(bucketName, fileName);
        } catch (Exception e) {
            log.error("minioUpload Error:" +e.getMessage());
            throw new BusinessException(BaseResponseCode.FILE_UPLOAD_ERROR);
        }
        return url;
    }

    /**
     * Minio文件上传
     *
     * @param bytes       文件实体
     * @param fileName   修饰过的文件名 非源文件名
     * @param bucketName 所存文件夹（桶名）
     * @return
     */
    public String minioUploadByByteStream (byte[] bytes, String fileName, String bucketName) {
        String url = "";
        try {
            //桶使用固定minio
            String filestore="filestore";
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(filestore).object(bucketName + "/" + fileName)
                    .stream(in, bytes.length, -1).contentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document").build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
            //获取文件的存储路径
            url = getPreviewFileUrl(bucketName, fileName);
        } catch (Exception e) {
            log.error("minioUpload Error:" +e.getMessage());
            throw new BusinessException(BaseResponseCode.FILE_UPLOAD_ERROR);
        }
        return url;
    }


    /**
     * 上传文件到minio文件服务系统
     * @param file 文件实体
     * @param bucketName
     * @param filePathName 当filePathName 为空时，默认上传路径位 /station 目录下
     * @return
     */
    public String commonUploadMinio(MultipartFile file, String bucketName ,String filePathName) {
        //判断该集团的bucket是否存在,不存在就创建
        boolean flag = bucketExists(bucketName);
        if (!flag) {
            createBucketName(bucketName);
        }
        if(filePathName==null){
            filePathName = DeviceTypeConstant.STATION + "/" + file.getOriginalFilename();
        }
        String url = minioUpload(file, filePathName, bucketName);
        return url;
    }
    /**
     * 检查存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return
     */
    public boolean bucketExists(String bucketName) {
        boolean flag = false;
        try {
            flag =  minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (flag) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }
 
    /**
     * 获取文件流
     *
     * @param fileName   文件名
     * @param bucketName 桶名（文件夹）
     * @return
     */
    public InputStream getFileInputStream(String fileName, String bucketName) {
        try {
            //MinioClient minioClient = MinioClientConfig.getMinioClient();
            return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return null;
    }
 
 
    /**
     * @param bucketName:
     * @author
     * @description: 创建桶
     */
    public void createBucketName(String bucketName) {
        try {
            if (StringUtils.isBlank(bucketName)) {
                return;
            }
            //MinioClient minioClient = MinioClientConfig.getMinioClient();
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (isExist) {
                log.info("Bucket {} already exists.", bucketName);
            } else {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
               setPolicy(bucketName);
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }

    /**
     * 配置桶的权限
     * @param bucket 桶名称
     */
    private void setPolicy(String bucket) {
        //判断该集团的bucket是否存在,不存在就创建
        boolean flag = bucketExists(bucket);
        String policy = "{\n" +
                "    \"Version\": \"2012-10-17\",\n" +
                "    \"Statement\": [\n" +
                "        {\n" +
                "            \"Effect\": \"Allow\",\n" +
                "            \"Principal\": {\n" +
                "                \"AWS\": [\n" +
                "                    \"*\"\n" +
                "                ]\n" +
                "            },\n" +
                "            \"Action\": [\n" +
                "                \"s3:GetBucketLocation\",\n" +
                "                \"s3:ListBucket\",\n" +
                "                \"s3:ListBucketMultipartUploads\"\n" +
                "            ],\n" +
                "            \"Resource\": [\n" +
                "                \"arn:aws:s3:::"+bucket+"\"\n" +
                "            ]\n" +
                "        },\n" +
                "        {\n" +
                "            \"Effect\": \"Allow\",\n" +
                "            \"Principal\": {\n" +
                "                \"AWS\": [\n" +
                "                    \"*\"\n" +
                "                ]\n" +
                "            },\n" +
                "            \"Action\": [\n" +
                "                \"s3:AbortMultipartUpload\",\n" +
                "                \"s3:DeleteObject\",\n" +
                "                \"s3:GetObject\",\n" +
                "                \"s3:ListMultipartUploadParts\",\n" +
                "                \"s3:PutObject\"\n" +
                "            ],\n" +
                "            \"Resource\": [\n" +
                "                \"arn:aws:s3:::"+bucket+"/*\"\n" +
                "            ]\n" +
                "        }\n" +
                "    ]\n" +
                "}";

        if (flag){
            try {
                SetBucketPolicyArgs bucketPolicyArgs = SetBucketPolicyArgs.builder().bucket(bucket).config(policy).build();
                //MinioClientConfig.getMinioClient().setBucketPolicy(bucketPolicyArgs);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
    /**
     * 下载文件
     *
     * @param originalName 文件路径
     */
    public InputStream downloadFile(String bucketName, String originalName, HttpServletResponse response) {
        try {
            //MinioClient minioClient = MinioClientConfig.getMinioClient();
            InputStream file = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(originalName).build());
            String filename = new String(originalName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
            if (StringUtils.isNotBlank(originalName)) {
                filename = originalName;
            }
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            ServletOutputStream servletOutputStream = response.getOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = file.read(buffer)) > 0) {
                servletOutputStream.write(buffer, 0, len);
            }
            servletOutputStream.flush();
            file.close();
            servletOutputStream.close();
            return file;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @param bucketName:
     * @description: 删除桶
     * @date 2022/8/16 14:36
     */
    public void deleteBucketName(String bucketName) {
        try {
            if (StringUtils.isBlank(bucketName)) {
                return;
            }
            //MinioClient minioClient = MinioClientConfig.getMinioClient();
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (isExist) {
                minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }

    public boolean deleteFile(String bucketName,String fileName) {
        boolean flag = false;
        try {
            //MinioClient minioClient = MinioClientConfig.getMinioClient();
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (isExist) {
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
                 flag = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return flag;
    }
    /**
     * @param bucketName:
     * @description: 删除桶下面所有文件
     * @date 2022/8/16 14:36
     */
    public void deleteBucketFile(String bucketName) {
        try {
            if (StringUtils.isBlank(bucketName)) {
                return;
            }
            //MinioClient minioClient = MinioClientConfig.getMinioClient();
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (isExist) {
                minioClient.deleteBucketEncryption(DeleteBucketEncryptionArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }

    @Value("${minio.endpoint}")
    String url;
    /**
     * 根据文件路径得到预览文件绝对地址
     *
     * @param bucketName
     * @param fileName
     * @return
     */
    public String getPreviewFileUrl(String bucketName, String fileName) {
        //MinioClient minioClient = MinioClientConfig.getMinioClient();
        String urlPath = "";
        try {
             //urlPath = minioClient.g(bucketName, fileName);
            //urlPath = endpoint+"/filestore/"+bucketName+"/"+fileName;
            urlPath = prefixUrl+"/filestore/"+bucketName+"/"+fileName;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return urlPath;
    }


    /**
     * 清空bucket(删除桶下所有文件)
     * @param bucketName 桶名称
     */
    public void clearBucket(String bucketName){
        //MinioClient minioClient = MinioClientConfig.getMinioClient();
        boolean flag = bucketExists(bucketName);
        if (flag) {
            try {
                // 递归列举某个bucket下的所有文件，然后循环删除
                Iterable<Result<Item>> iterable = minioClient.listObjects(ListObjectsArgs.builder()
                        .bucket(bucketName)
                        .recursive(true)
                        .build());
                for (Result<Item> itemResult : iterable) {
                    String decode = URLDecoder.decode(itemResult.get().objectName(), "utf-8");
                    deleteFile(bucketName,decode);
                }
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
    }
}
