package com.yifu.cloud.plus.v1.yifu.common.core.util;

import cn.hutool.core.io.IoUtil;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.CreateBucketRequest;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.PutObjectRequest;
import com.yifu.cloud.plus.v1.yifu.common.core.constant.CommonConstants;
import com.yifu.cloud.plus.v1.yifu.common.core.util.bean.OSSObjectBean;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author  fang
 * @Description  阿里云 OSS存储操作工具类
 */
@Component
@ConfigurationProperties(prefix = "oss")
@Data
@Slf4j
public class OSSUtil {
    // Endpoint以北京为例，其它Region请按实际情况填写。
    private String endpoint = "http://oss-cn-beijing.aliyuncs.com";
    private String openEndpoint = "http://oss-cn-hangzhou.aliyuncs.com";
    // 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维，请登录 https://ram.console.aliyun.com 创建RAM账号。
    private String accessKeyId = "LTAIDh9goA3jgpun";
    private String accessKeySecret = "eN8EHPAZxglNfcUCMhAT02cy93omLa";
    //存储空间名称
    private String bucketName = "yf-hr-static-source";

    private String errorMessage ="Error Message: ";
    //存储  文件夹/文件名称 或 文件名称
    private OSS ossClient = null;
    //存储  文件夹/文件名称 或 文件名称
    private OSS openOssClient = null; //存储  文件夹/文件名称 或 文件名称

    // OSSClient实例化
    private OSS  initOssClient(int type){
        if(CommonConstants.dingleDigitIntArray[0] == type){
            if(null == ossClient) {
                ossClient = new OSSClientBuilder().build(this.endpoint, this.accessKeyId, this.accessKeySecret);
            }
            return ossClient;
        }else if(CommonConstants.dingleDigitIntArray[1] == type){
            if(null == ossClient) {
                openOssClient = new OSSClientBuilder().build(this.openEndpoint, this.accessKeyId, this.accessKeySecret);
            }
            return openOssClient;
        }
        return null;
    }
    // 创建 Bucket 存储空间 默认 private 写需要身份校验 读不需要
    private void initBucket(String bucketName){
        ossClient.createBucket(bucketName);
        CreateBucketRequest createBucketRequest= new CreateBucketRequest(bucketName);
        createBucketRequest.setCannedACL(CannedAccessControlList.Private);
        ossClient.createBucket(createBucketRequest);
    }

    /**
     * @author fang
     * @param file  传文件
     * @param key 存储文件名称 可包含文件夹 如：文件夹/文件名 或 文件名
     * @param bucketName 存储空间名称 如果不存在新增存储空间 属性 公告读取  不传参默认配置文件的存储空间
     * @return
     * @throws IOException
     */
    public Boolean uploadFileByFile( File file, String key, String bucketName) {
        try {
            OSS client = null;
            //OSSClient实例化
            client = initOssClient(CommonConstants.dingleDigitIntArray[0]);
            //为空默认使用配置的存储空间
            if(!Common.isNotNull(bucketName)) {
                bucketName = this.bucketName;
            }
            //查看Bucket存储空间 是否已存在，不存在 Create a new OSS bucket
            if (!client.doesBucketExist(bucketName)) {
                initBucket(bucketName);
            }
            /*
             * Determine whether an object residents in your bucket  查看存储空间文件是否存在
             */
            boolean exists = client.doesObjectExist(bucketName, key);
            /*
             * Upload an object to your bucket  上传一个文件到 存储空间
             */
            //不存在就存储 存在 直接进行下一步
            if(!exists){
                client.putObject(new PutObjectRequest(bucketName, key, file));
            }
            return true;
        } catch (OSSException oe) {
            ossexceptionlog(oe);
        } catch (ClientException ce) {
            clientExceptionLog(ce);
        } finally {
            /*
             * Do not forget to shut down the client finally to release all allocated resources.
             */
        }
        return false;
    }
    public boolean uploadFileByStream(InputStream inputStream, String key, String bucketName) {
       return uploadFileByStream(inputStream,key,bucketName, CommonConstants.dingleDigitIntArray[0]);
    }
    /**
     * @author fang
     * @param inputStream  传文件流
     * @param key 存储文件名称 可包含文件夹 如：文件夹/文件名 或 文件名
     * @param bucketName 存储空间名称 如果不存在新增存储空间 属性 公告读取  不传参默认配置文件的存储空间
     * @param clientIndex 0人力私有空间客户端 1开发空间的客户端
     * @return
     * @throws IOException
     */
    public boolean uploadFileByStream(InputStream inputStream, String key, String bucketName,int clientIndex) {
        try {

            //OSSClient实例化
            OSS client = initOssClient(clientIndex);
            if(null == client){
                log.error("oss客户端获取错误，无此客户端",clientIndex);
                return Boolean.FALSE;
            }
            //为空默认使用配置的存储空间
            if(!Common.isNotNull(bucketName)) {
                bucketName = this.bucketName;
            }
            //查看Bucket存储空间 是否已存在，不存在 Create a new OSS bucket
            if (!client.doesBucketExist(bucketName)) {
                initBucket(bucketName);
            }
            /*
             * Determine whether an object residents in your bucket  查看存储空间文件是否存在
             */
            boolean exists = client.doesObjectExist(bucketName, key);
            /*
             * Upload an object to your bucket  上传一个文件到 存储空间
             */
            //不存在就存储 存在 直接进行下一步
            if(!exists){
                client.putObject(new PutObjectRequest(bucketName, key, inputStream));
            }
            return true;
        } catch (OSSException oe) {
            ossexceptionlog(oe);
        } catch (ClientException ce) {
            clientExceptionLog(ce);
        } finally {
            /*
             * Do not forget to shut down the client finally to release all allocated resources.
             */
        }
        return false;
    }
    /**
     * @author fang
     * 查看文件是否存在
     * @param bucketName  默认用配置的
     * @param key 不可为空
     * @return
     */
    public Boolean doesObjectExist(String bucketName,String key){
        return doesObjectExist(bucketName,key);
    }
    public Boolean doesObjectExist(String bucketName,String key,int clientIndex){
        try{
            bucketName =  initBucketName(bucketName);
            if(!Common.isNotNull(key)){
                return false;
            }
            //OSSClient实例化
            OSS client = initOssClient(clientIndex);
            if(null == client){
                log.error("oss客户端获取错误，无此客户端",clientIndex);
                return Boolean.FALSE;
            }
            return client.doesObjectExist(bucketName, key);

        } catch (OSSException oe) {
            ossexceptionlog(oe);
        } catch (ClientException ce) {
            clientExceptionLog(ce);
        }
        return  false;
    }

    /**
     * @author fang
     * @param bucketName 默认用配置的
     * @param key 不可为空w 文件名称
     * @return
     * 如果Object是公共读/公共读写权限，那么访问URL的格式为：Bucket名称.Endpoint/Object名称
     * 如果Object是私有权限，则必须进行签名操作。访问URL的格式为：Bucket名称.Endpoint/Object名称？签名参数
     * 此次统一按签名方式返回
     */
    public  URL getObjectUrl(String bucketName,String key){
        try{
            bucketName = initBucketName(bucketName);
            if(!Common.isNotNull(key)){
                return null;
            }
            initOssClient(CommonConstants.dingleDigitIntArray[0]);
            // 生成URL 时效 1小时
            Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
            URL url = ossClient.generatePresignedUrl(bucketName, key, expiration);
            return  url;
        } catch (OSSException oe) {
            ossexceptionlog(oe);
        } catch (ClientException ce) {
            clientExceptionLog(ce);
        }
        return  null;
    }
    public  void getObjectZip(String bucketName, List<OSSObjectBean> srcList, ZipOutputStream zip){
        getObjectZip(bucketName,srcList,zip,CommonConstants.dingleDigitIntArray[0]);
    }
    public  void getObjectZip(String bucketName, List<OSSObjectBean> srcList,ZipOutputStream zip,int clientIndex){
        InputStream inputStream = null;

        try{
            bucketName = initBucketName(bucketName);
            //OSSClient实例化
            OSS client = initOssClient(clientIndex);
            if(null == client){
                log.error("oss客户端获取错误，无此客户端",clientIndex);
            }else{
                OSSObject ossObject = null;
                for(OSSObjectBean key:srcList){
                    // ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
                    ossObject = client.getObject(bucketName, key.getKey());
                    if(null != ossObject){
                        // 读取文件内容。
                        log.info("Object content:");
                        inputStream = ossObject.getObjectContent();
                        //添加到zip
                        zip.putNextEntry(new ZipEntry(key.getName()));
                        byte[] buf = new byte[1024];
                        int len;
                        while ((len = inputStream.read(buf)) != -1){
                            zip.write(buf, 0, len);
                        }
                    }
                    // 数据读取完成后，获取的流必须关闭，否则会造成连接泄漏，导致请求无连接可用，程序无法正常工作。
                    IoUtil.close(inputStream);
                    zip.closeEntry();
                }
            }
           // 关闭OSSClient。
        } catch (OSSException oe) {
            ossexceptionlog(oe);
        } catch (ClientException ce) {
            clientExceptionLog(ce);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IoUtil.close(inputStream);
        }
    }

    /**
     * @author fang
     * @param bucketName 默认用配置的
     * @param key 不可为空
     * @return
     */
    public Boolean deleteObject(String bucketName,String key) {
        return deleteObject(bucketName, key,CommonConstants.dingleDigitIntArray[0]);
    }
    public Boolean deleteObject(String bucketName,String key,int clientIndex) {
        try{
            bucketName = initBucketName(bucketName);
            if (!Common.isNotNull(key)) {
                return false;
            }
            //OSSClient实例化
            OSS client = initOssClient(clientIndex);
            if(null == client){
                log.error("oss客户端获取错误，无此客户端",clientIndex);
                return Boolean.FALSE;
            }
            ossClient.deleteObject(bucketName, key);
            return true;
        } catch (OSSException oe) {
            log.error("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            log.error(errorMessage + oe.getErrorCode());
            log.error("Error Code:       " + oe.getErrorCode());
            log.error("Request ID:      " + oe.getRequestId());
            log.error("Host ID:           " + oe.getHostId());
        } catch (ClientException ce) {
            clientExceptionLog(ce);
        }
        return  false;
    }
    /**
     * @author fang
     * @param bucketName 默认用配置的
     * @return
     */
    private String initBucketName(String bucketName){
        if(!Common.isNotNull(bucketName)){
            return this.bucketName;
        }else{
            return  bucketName;
        }
    }

    private void ossexceptionlog(OSSException oe){
        log.error("Caught an OSSException, which means your request made it to OSS, "
                + "but was rejected with an error response for some reason.");
        log.error(errorMessage + oe.getErrorCode());
        log.error("Error Code:       " + oe.getErrorCode());
        log.error("Request ID:      " + oe.getRequestId());
        log.error("Host ID:           " + oe.getHostId());
    }
    private void clientExceptionLog(ClientException ce){
        log.error("Caught an ClientException, which means the client encountered "
                + "a serious internal problem while trying to communicate with OSS, "
                + "such as not being able to access the network.");
        log.error(errorMessage + ce.getMessage());
    }
}
