package com.cool.store.oss;

import cn.hutool.core.date.DateUtil;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.oss.event.ProgressListener;
import com.aliyun.oss.model.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.Date;

/**
 * Created by gavin on 15/8/5.
 * 阿里云OSS客户端
 */
@Slf4j
@Component
public class OssClientService {

    @Value("${oss.endpoint}")
    private String endpoint;
    @Value("${oss.accessKeyId}")
    private String accessKeyId;
    @Value("${oss.accessKeySecret}")
    private String accessKeySecret;
    @Value("${oss.bucket}")
    private String bucketName;

    @Value("${oss.host}")
    private String ossHost;
    @Value("${cdn.url:null}")
    private String cdnUrl;
    @Value("${oss.file.dir:null}")
    private String dir;


    private OSSClient client = null;

    private OSSClient getClient() {
        if(client==null) {
            synchronized(this){
                client= new OSSClient(endpoint, accessKeyId, accessKeySecret);
            }
        }
        return client;
    }


    /**OSSClient
     * 获取上传进度回调
     */
    class PutObjectProgressListener implements ProgressListener {

        private long bytesWritten = 0;
        private long totalBytes = -1;
        private boolean succeed = false;

        @Override
        public void progressChanged(ProgressEvent progressEvent) {
            long bytes = progressEvent.getBytes();
            ProgressEventType eventType = progressEvent.getEventType();
            switch (eventType) {
                case TRANSFER_STARTED_EVENT:
                    log.info("Start to upload......");
                    break;

                case REQUEST_CONTENT_LENGTH_EVENT:
                    this.totalBytes = bytes;
                    log.info(this.totalBytes + " bytes in total will be uploaded to OSS");
                    break;

                case REQUEST_BYTE_TRANSFER_EVENT:
                    this.bytesWritten += bytes;
                    if (this.totalBytes != -1) {
                        int percent = (int) (this.bytesWritten * 100.0 / this.totalBytes);
                        log.info(bytes + " bytes have been written at this time, upload progress: " +
                                percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
                    } else {
                        log.info(bytes + " bytes have been written at this time, upload ratio: unknown" +
                                "(" + this.bytesWritten + "/...)");
                    }
                    break;

                case TRANSFER_COMPLETED_EVENT:
                    this.succeed = true;
                    log.info("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
                    break;

                case TRANSFER_FAILED_EVENT:
                    log.info("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
                    break;

                default:
                    break;
            }
        }

        public boolean isSucceed() {
            return succeed;
        }
    }

    public ObjectMetadata getObject(String key) {
        OSSClient ossClient = getClient();
        ObjectMetadata metadata = ossClient.getObjectMetadata(bucketName, key);
        return metadata;
    }

    public InputStream getFileContent(String key) {
        OSSClient ossClient = getClient();
        InputStream content = ossClient.getObject(bucketName, key).getObjectContent();
        return content;


    }

    public void downloadFile(String key, String filePath, String tempFilePath) {

        OSSClient ossClient = getClient();
        DownloadFileRequest downloadFileRequest = new DownloadFileRequest(bucketName, key);
        downloadFileRequest.setDownloadFile(filePath);
        downloadFileRequest.setPartSize(1 * 1024 * 1024L);
        downloadFileRequest.setTaskNum(10);
        downloadFileRequest.setEnableCheckpoint(true);
        downloadFileRequest.setCheckpointFile(tempFilePath);
        try {
            DownloadFileResult downloadRes = ossClient.downloadFile(downloadFileRequest);
            downloadRes.getObjectMetadata();
        } catch (Throwable throwable) {
            log.error("downloadFile error", throwable);
        }
    }

    /**
     * 上传文件到阿里云OSS
     *
     * @param fileName
     * @param inputStream
     * @param contentLength
     * @param contentType
     * @throws Exception
     */
    @Async("taskExecutor")
    public void putObjectAsync(String fileName, InputStream inputStream, Long contentLength, String contentType) throws Exception {
        putObject(fileName, inputStream, contentLength, contentType);
    }

    public String putObjectWithoutPrefix(String fileName, InputStream inputStream, Long contentLength, String contentType) throws Exception {
        String time = DateUtil.format(new Date(), "yyMM");
        return putObject(dir + time + "/" + fileName, inputStream, contentLength, contentType);
    }

    /**
     * 上传文件到阿里云OSS
     *
     * @param fileName
     * @param inputStream
     * @param contentLength
     * @param contentType
     * @throws Exception
     */
    public String putObject(String fileName, InputStream inputStream, Long contentLength, String contentType) throws Exception {

        OSSClient ossClient = getClient();

        try {
            ObjectMetadata meta = new ObjectMetadata();
            meta.setContentLength(contentLength);

            meta.setContentType(contentType);
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, fileName, inputStream, meta);
            log.info("uploadBaseImage, send...");
            PutObjectResult p = ossClient.putObject(putObjectRequest.withProgressListener(new PutObjectProgressListener()));
            String url = cdnUrl + fileName;
            log.info("uploadBaseImage, send over，url:{}", url);
            return url;

            // return  p.getETag();

        } catch (OSSException oe) {
            log.warn("oss出现问题", 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("Error Message: " + oe.getErrorCode());
            log.error("Error Code:       " + oe.getErrorCode());
            log.error("Request ID:      " + oe.getRequestId());
            log.error("Host ID:           " + oe.getHostId());
            throw oe;
        } catch (ClientException ce) {
            log.warn("oss客户端出现问题", 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("Error Message: " + ce.getMessage());
            throw ce;
        }catch (Exception e){
            log.warn("uploadBaseImage, error");
            throw e;
        }
    }

    /**
     * 上传文件到阿里云OSS
     *
     * @param fileName
     * @param inputStream
     */
    public String putObject(String fileName, InputStream inputStream) {

        OSSClient ossClient = getClient();

        try {
            log.info("uploadBaseImage, send...");
            PutObjectResult p = ossClient.putObject(bucketName, fileName, inputStream);
            log.info("uploadBaseImage, send over");
            String url = ossHost + fileName;
            return url;
            // return  p.getETag();
        } catch (OSSException oe) {
            log.warn("oss出现问题", 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("Error Message: " + oe.getErrorCode());
            log.error("Error Code:       " + oe.getErrorCode());
            log.error("Request ID:      " + oe.getRequestId());
            log.error("Host ID:           " + oe.getHostId());
            throw oe;
        } catch (ClientException ce) {
            log.warn("oss客户端出现问题", 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("Error Message: " + ce.getMessage());
            throw ce;
        }catch (Exception e){
            log.warn("uploadBaseImage, error");
            throw e;
        }
    }


}
