package com.chushang.oss.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.StrUtil;
import com.chushang.common.core.exception.ResultException;
import com.chushang.common.core.util.FileUtils;
import com.chushang.common.core.util.StringUtils;
import com.chushang.oss.config.UploadConfig;
import com.chushang.oss.entity.FileSourceInfo;
import com.chushang.oss.service.OssService;
import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @auther: zhao
 * @date: 2024/4/28 19:52
 */
@Slf4j
@Service
@ConditionalOnExpression("'${config.oss.storage}'.equals('minio')")
public class MinioServiceImpl implements OssService {

    private MinioClient minio;
    @Resource
    private UploadConfig config;
    @PostConstruct
    public void init() {
        minio = MinioClient.builder().endpoint(config.getEndPoint())
                .credentials(config.getAccessKey(), config.getSecretKey()).build();
        try {
            if (!minio.bucketExists(BucketExistsArgs.builder().bucket(config.getBucketName()).build())) {
                log.info("createBucket {}", config.getBucketName());
                createBucket();
            }
        } catch (Exception e) {
            log.error("MinIO Storage Connection Fail");
        }
        log.info("createFont ");
    }

    /**
     * 创建minio bucket
     */
    @SneakyThrows
    private void createBucket()
    {
        String bucketName = config.getBucketName();

        // 注意 minio通过nginx代理后无法判断桶是否存在 需要通过查询所有桶的形式
        List<Bucket> buckets = minio.listBuckets();

        if (buckets != null) {
            Bucket bucket = buckets.stream().filter(b -> config.getBucketName().equals(b.name()))
                    .findAny().orElse(null);
            if (bucket != null) {
                log.info("------------- 初始化 minio 配置 -------------");
                return;
            }
        }

        log.warn("【{}】桶不存在,开始创建", bucketName);

        minio.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());

        // 设置默认桶策略
        String policy = StrUtil.format("{\n" +
                "    \"Version\": \"2012-10-17\",\n" +
                "    \"Statement\": [\n" +
                "        {\n" +
                "            \"Effect\": \"Allow\",\n" +
                "            \"Principal\": {\n" +
                "                \"AWS\": [\n" +
                "                    \"*\"\n" +
                "                ]\n" +
                "            },\n" +
                "            \"Action\": [\n" +
                "                \"s3:GetBucketLocation\"\n" +
                "            ],\n" +
                "            \"Resource\": [\n" +
                "                \"arn:aws:s3:::{}\"\n" +
                "            ]\n" +
                "        },\n" +
                "        {\n" +
                "            \"Effect\": \"Allow\",\n" +
                "            \"Principal\": {\n" +
                "                \"AWS\": [\n" +
                "                    \"*\"\n" +
                "                ]\n" +
                "            },\n" +
                "            \"Action\": [\n" +
                "                \"s3:ListBucket\"\n" +
                "            ],\n" +
                "            \"Resource\": [\n" +
                "                \"arn:aws:s3:::{}\"\n" +
                "            ],\n" +
                "            \"Condition\": {\n" +
                "                \"StringEquals\": {\n" +
                "                    \"s3:prefix\": [\n" +
                "                        \"*\"\n" +
                "                    ]\n" +
                "                }\n" +
                "            }\n" +
                "        },\n" +
                "        {\n" +
                "            \"Effect\": \"Allow\",\n" +
                "            \"Principal\": {\n" +
                "                \"AWS\": [\n" +
                "                    \"*\"\n" +
                "                ]\n" +
                "            },\n" +
                "            \"Action\": [\n" +
                "                \"s3:GetObject\"\n" +
                "            ],\n" +
                "            \"Resource\": [\n" +
                "                \"arn:aws:s3:::{}/**\"\n" +
                "            ]\n" +
                "        }\n" +
                "    ]\n" +
                "}", bucketName, bucketName, bucketName);

        SetBucketPolicyArgs bucketPolicyArgs = SetBucketPolicyArgs.builder().bucket(bucketName).config(policy).build();

        minio.setBucketPolicy(bucketPolicyArgs);

        log.warn("【{}】桶创建完成", bucketName);

        log.info("------------- 初始化 minio 配置 -------------");
    }


    @Override
    public String getPrefixPath(String prefix, String filename) {
        String minioPrefix = config.getPrefix();
        if (StringUtils.isNotEmpty(minioPrefix)) {
            prefix = minioPrefix + "/" + prefix + "/" + filename;
        } else {
            prefix = prefix + "/" + filename;
        }
        return prefix;
    }

    @Override
    public String upload(InputStream inputStream, FileSourceInfo info) {
        try {
            minio.putObject(PutObjectArgs.builder().bucket(config.getBucketName())
                    // 路径 object 为路径地址
                    .object(info.getPath())
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(info.getMimeType())
                    .build());
        } catch (Exception e) {
            log.error("上传MINio 异常", e);
        }
        // 需要添加上 bucket
        return config.getEndPoint() + "/" + config.getBucketName() + "/" + info.getPath();
    }

    @Override
    public void delFile(String fid) {
        try {
            minio.removeObject(RemoveObjectArgs.builder()
                    .bucket(config.getBucketName())
                    .object(fid)
                    .build());
        } catch (Exception ig) {
            log.error("删除失败");
        }
    }

    @Override
    public List<String> delFileBatch(List<String> keys) {
        Iterable<Result<DeleteError>> results
                = minio.removeObjects(RemoveObjectsArgs.builder()
                        .bucket(config.getBucketName())
                        .objects(keys.stream().map(DeleteObject::new).toList())
                        .build());
        List<String> resultKey = new ArrayList<>();
        results.forEach(result -> {
            try {
                DeleteError deleteError = result.get();
                String s = deleteError.objectName();
                resultKey.add(s);
            } catch (Exception e) {
                log.error("删除result error", e);
            }
        });
        return resultKey;
    }

//    @Override
//    public InputStream getFileStream(FileSourceInfo info) {
//        try {
//            GetObjectResponse object = minio.getObject(GetObjectArgs.builder()
//                    .bucket(config.getBucketName())
//                    .object(info.getPath())
//                    .build());
//            return new ByteArrayInputStream(object.readAllBytes());
//        } catch (Exception e) {
//            log.error("获取minio object error", e);
//            throw new ResultException("获取文件失败");
//        }
//    }

    @Override
    public byte[] getFileBytes(FileSourceInfo info) {
        try {
            GetObjectResponse object = minio.getObject(GetObjectArgs.builder()
                    .bucket(config.getBucketName())
                    .object(info.getPath())
                    .build());
            return object.readAllBytes();
        } catch (Exception e) {
            log.error("获取minio object error", e);
            throw new ResultException("获取文件失败");
        }
    }
}
