package org.springblade.common.utils;

import com.alibaba.cloud.commons.lang.StringUtils;
import com.obs.services.ObsClient;
import com.obs.services.exception.ObsException;
import com.obs.services.model.*;
import jakarta.activation.MimetypesFileTypeMap;
import lombok.SneakyThrows;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;

import java.io.*;
import java.net.URL;
import java.util.List;

/**
 * @ClassName: OBSUtil
 * @Description:
 * @Author: zhaowei
 * @Date: 2024/8/22
 */
public class ObsUtil {
    private final static Logger logger = LoggerFactory.getLogger(ObsUtil.class);

    private static MimetypesFileTypeMap fileTypeMap = new MimetypesFileTypeMap();
    /**
     * 华为云OBS
     */
    public static String obsEndpoint;
    public static String obsAccessKeyId;
    public static String obsAccessKeySecret;
    public static String obsBucketName;
    public static String obsRootDirectory;

    @Value("${obs.endpoint}")
    public void setObsEndpoint(String endpoint) {
        ObsUtil.obsEndpoint = endpoint;
    }

    @Value("${obs.access-key-id}")
    public void setObsAccessKeyId(String accessKeyId) {
        ObsUtil.obsAccessKeyId = accessKeyId;
    }

    @Value("${obs.access-key-secret}")
    public void setObsAccessKeySecret(String accessKeySecret) {
        ObsUtil.obsAccessKeySecret = accessKeySecret;
    }

    @Value("${obs.bucket-name}")
    public void setObsBucketName(String bucketName) {
        ObsUtil.obsBucketName = bucketName;
    }

    @Value("${obs.root-directory}")
    public void setObsRootDirectory(String rootDirectory) {
        ObsUtil.obsRootDirectory = rootDirectory;
    }


    // 如果Bucket不存在，则创建它。
    private static void ensureBucket(ObsClient client, String bucketName) throws ObsException {

        if (client.headBucket(bucketName)) {
            return;
        }
        // 创建bucket
        client.createBucket(bucketName);
    }

    // 把Bucket设置为所有人可读
    private static void setBucketPublicReadable(String bucketName) throws ObsException {
        //华为云OBS
        ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
        if (obsClient.doesObjectExist(obsBucketName, obsRootDirectory)) {

        } else {
            obsClient.createBucket(obsBucketName);
        }
        obsClient.setBucketAcl(obsBucketName, com.obs.services.model.AccessControlList.REST_CANNED_PUBLIC_READ_WRITE);
    }

    /**
     * 上传一个Object
     *
     * @param key
     * @param content
     * @throws FileNotFoundException
     */
    public static String putObject(String key, InputStream content) throws IOException {
        //华为云OBS
        ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
        try {
            obsClient.putObject(obsBucketName, key, content);
        } catch (Exception e) {
            logger.error("obs put object error", e);
            throw new RuntimeException("obs eror" + e.getMessage());
        } finally {
            obsClient.close();
        }
        return getExpireUrl(key);
    }


    /**
     * 下载文件
     * @param objectKey 上传到OBS起的名
     * @param filename  文件下载到本地保存的路径
     * @throws ObsException
     * @throws IOException
     */
    public static void downloadFile(String objectKey, String filename) throws ObsException, IOException {
        //华为云OBS
        ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
        try {
            obsClient.getObject(obsBucketName, objectKey);
        } finally {
            obsClient.close();
        }
    }

    /**
     * 删除一个Bucket和其中的Objects
     *
     * @param bucketName Bucket名
     * @throws ObsException
     */
    private static void deleteBucket(String bucketName) throws ObsException {
        ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
        com.obs.services.model.ObjectListing objectListing = obsClient.listObjects(bucketName);
        List<ObsObject> listObject = objectListing.getObjects();
        for (ObsObject o : listObject) {
            obsClient.deleteObject(bucketName, o.getObjectKey());
        }
        obsClient.deleteBucket(bucketName);
    }

    /**
     * 批量删除Object
     * @param objList Object的key(目录路径/文件名)
     */
    public static void delelteObjectList(List<String> objList) {
        ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
        for (String key : objList) {
            obsClient.deleteObject(obsBucketName, key);
        }
    }


    /**
     * 删除Object
     * @param key 目录路径/文件名
     */
    public static void delelteObject(String key) {
        ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
        obsClient.deleteObject(obsBucketName, key);
    }

    /**
     * 通过key获取带签名的url
     */
    public static String getExpireUrl(String key) {
        String fileUrl = "";
        ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
        // URL有效期，3600秒.5分钟
        // long expireSeconds = 3600L;
        // 约等于1年
        long expireSeconds = 31536000L;
        TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, expireSeconds);
        request.setBucketName(obsBucketName);
        request.setObjectKey(key);
        TemporarySignatureResponse response = obsClient.createTemporarySignature(request);
        fileUrl = response.getSignedUrl();
        return fileUrl;
    }

    /**
     * 上传文件至OSS并且返回链接地址
     * @param url      远程路径
     * @param ossDir   OSS目录
     * @param fileName 文件名
     * @return 文件OSS链接
     * @modify
     */
    public static String uploadFile2Oss(URL url, String ossDir, String fileName) {
        InputStream is;
        String key = ossDir + fileName;
        try {
            is = url.openStream();
            return uploadFile2OSS(is, key);
        } catch (Exception ex) {
            logger.error("上传失败！", ex);
        }
        return StringUtils.EMPTY;
    }

    /**
     * 上传到OSS服务器  如果同名文件会覆盖服务器上的
     *
     * @param inputStream 文件流
     * @return 返回文件访问路径
     */
    @SneakyThrows
    public static String uploadFile2OSS(InputStream inputStream, String key) {
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setCacheControl("no-cache");
        String fileName = FilenameUtils.getName(key);
        objectMetadata.setContentType(fileTypeMap.getContentType(fileName));
        objectMetadata.addUserMetadata("filename", fileName);

        ObsClient obsClient = new ObsClient(obsAccessKeyId,
                obsAccessKeySecret, obsEndpoint);
        try {
            obsClient.putObject(obsBucketName, obsRootDirectory + key, inputStream, objectMetadata);
            return obsRootDirectory + "/" + key;
        } finally {
            obsClient.close();
        }
    }

    @SneakyThrows
    public Boolean checkObjectExist(String key) {
        Boolean existsFlag = false;
        ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
        try {
            existsFlag = obsClient.doesObjectExist(obsBucketName, key);
        } finally {
            obsClient.close();
        }
        return existsFlag;
    }


    /**
     * 下载文件
     * @param objectKey 上传到OBS起的名
     * @param filename  文件下载到本地保存的路径
     * @throws ObsException
     * @throws IOException
     */
    public static void downloadFileNew(String objectKey, String filename){
        //华为云OBS
        ObsClient obsClient = new ObsClient(obsAccessKeyId, obsAccessKeySecret, obsEndpoint);
        try {
            ObsObject ossObject = obsClient.getObject(obsBucketName, objectKey);

            OutputStream outputStream = null;
            try {
                outputStream = new BufferedOutputStream(new FileOutputStream(filename));
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = ossObject.getObjectContent().read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            } catch (IOException ex) {
            } finally {
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                    }
                }
            }
        } finally {
            if (obsClient != null) {
                try {
                    obsClient.close();
                } catch (IOException e) {
                }
            }
        }
    }
}
