package com.zxdmy.excite.component.oss;

import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.*;
import com.aliyun.oss.model.*;
import com.zxdmy.excite.common.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLDecoder;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

/**
 * 阿里云 OSS 工具类
 */

@Component
public class AliOSSUtils{
    @Autowired//将OSS的配置注入进来，后面需要用到
    private AliOSSProperties aliOSSProperties;
    @Resource
    protected HttpServletRequest request;



    /**
     * oss文件下载
     * @param path 文件下载本地地址
     * @param pathOss oss地址
     * @return
     * @throws IOException
     */
    public Boolean download(String path,String pathOss) throws IOException {

        //获取阿里云OSS参数
        String endpoint = aliOSSProperties.getEndpoint();
        String accessKeyId = aliOSSProperties.getAccessKeyId();
        String accessKeySecret = aliOSSProperties.getAccessKeySecret();
        String bucketName = aliOSSProperties.getBucketName();

        // 创建连接
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);
//        content-disposition
        try {
            // 下载Object到本地文件，并保存到指定的本地路径中。如果指定的本地文件存在会覆盖，不存在则新建。
            // 如果未指定本地路径，则下载后的文件默认保存到示例程序所属项目对应本地路径中。
            ossClient.getObject(new GetObjectRequest(bucketName, pathOss), new File(path));
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("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.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return false;

    }

    /**
     * 删除oss文件
     * @param path 文件地址
     * @return
     * @throws IOException
     */
    public Boolean delete(String path) throws IOException {

        //获取阿里云OSS参数
        String endpoint = aliOSSProperties.getEndpoint();
        String accessKeyId = aliOSSProperties.getAccessKeyId();
        String accessKeySecret = aliOSSProperties.getAccessKeySecret();
        String bucketName = aliOSSProperties.getBucketName();

        // 创建连接
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);

        try {
            // 删除文件。
            // 文件完整路径中不能包含Bucket名称。
            List<String> keys = new ArrayList<String>();
            keys.add(path);

            DeleteObjectsResult deleteObjectsResult = ossClient.deleteObjects(new DeleteObjectsRequest(bucketName).withKeys(keys).withEncodingType("url"));
            List<String> deletedObjects = deleteObjectsResult.getDeletedObjects();
            try {
                for(String obj : deletedObjects) {
                    String deleteObj =  URLDecoder.decode(obj, "UTF-8");
                    System.out.println(deleteObj);
                    return !(ossClient.doesObjectExist(bucketName,path));
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("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.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return false;

    }

    /**
     * 查找oss文件
     * @param path 文件地址
     * @return
     * @throws IOException
     */
    public Boolean query(String path) throws IOException {

        //获取阿里云OSS参数
        String endpoint = aliOSSProperties.getEndpoint();
        String accessKeyId = aliOSSProperties.getAccessKeyId();
        String accessKeySecret = aliOSSProperties.getAccessKeySecret();
        String bucketName = aliOSSProperties.getBucketName();

        // 创建连接
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);

        try {
            // 判断文件是否存在。如果返回值为true，则文件存在，否则存储空间或者文件不存在。
            // 设置是否进行重定向或者镜像回源。默认值为true，表示忽略302重定向和镜像回源；如果设置isINoss为false，则进行302重定向或者镜像回源。
            //boolean isINoss = true;
            boolean found = ossClient.doesObjectExist(bucketName, path);
            //boolean found = ossClient.doesObjectExist(bucketName, objectName, isINoss);
            return found;
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("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.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return false;
    }

    /**
     * 拷贝oss地址文件位置至其他oss地址位置（源Bucket相同）
     * @param path
     * @param newPath
     * @return
     * @throws IOException
     */
    public Boolean copy(String path,String newPath) throws IOException {

        //获取阿里云OSS参数
        String endpoint = aliOSSProperties.getEndpoint();
        String accessKeyId = aliOSSProperties.getAccessKeyId();
        String accessKeySecret = aliOSSProperties.getAccessKeySecret();
        String bucketName = aliOSSProperties.getBucketName();
        String newBucketName = aliOSSProperties.getBucketName();

        // 创建连接
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);

        try {

            ObjectMetadata objectMetadata = ossClient.getObjectMetadata(bucketName, path);
            // 获取被拷贝文件的大小。
            long contentLength = objectMetadata.getContentLength();
            long gb = 1024 * 1024 * 1024;
            //小文件上传
            if(contentLength < gb) {
                // 拷贝文件。
                CopyObjectResult result = ossClient.copyObject(bucketName, path, newBucketName, newPath);
                System.out.println("ETag: " + result.getETag() + " LastModified: " + result.getLastModified());
                // 判断文件是否存在。如果返回值为true，则文件存在，否则存储空间或者文件不存在。
                // 设置是否进行重定向或者镜像回源。默认值为true，表示忽略302重定向和镜像回源；如果设置isINoss为false，则进行302重定向或者镜像回源。

            }
            //大文件上传（分片上传[大于1GB])
            else {
                // 设置分片大小为10 MB。单位为字节。
                long partSize = 1024 * 1024 * 10;

                // 计算分片总数。
                int partCount = (int) (contentLength / partSize);
                if (contentLength % partSize != 0) {
                    partCount++;
                }
                System.out.println("total part count:" + partCount);

                // 初始化拷贝任务。可以通过InitiateMultipartUploadRequest指定目标文件元信息。
                InitiateMultipartUploadRequest initiateMultipartUploadRequest = new InitiateMultipartUploadRequest(newBucketName, newPath);
                // 拷贝源文件ContentType和UserMetadata，分片拷贝默认不拷贝源文件的ContentType和UserMetadata。
                ObjectMetadata metadata = new ObjectMetadata();
                metadata.setContentType(objectMetadata.getContentType());
                metadata.setUserMetadata(objectMetadata.getUserMetadata());
                initiateMultipartUploadRequest.setObjectMetadata(metadata);
                InitiateMultipartUploadResult initiateMultipartUploadResult = ossClient.initiateMultipartUpload(initiateMultipartUploadRequest);
                String uploadId = initiateMultipartUploadResult.getUploadId();

                // 分片拷贝。
                List<PartETag> partETags = new ArrayList<PartETag>();
                for (int i = 0; i < partCount; i++) {
                    // 计算每个分片的大小。
                    long skipBytes = partSize * i;
                    long size = Math.min(partSize, contentLength - skipBytes);

                    // 创建UploadPartCopyRequest。可以通过UploadPartCopyRequest指定限定条件。
                    UploadPartCopyRequest uploadPartCopyRequest =
                            new UploadPartCopyRequest(bucketName, path, newBucketName, newPath);
                    uploadPartCopyRequest.setUploadId(uploadId);
                    uploadPartCopyRequest.setPartSize(size);
                    uploadPartCopyRequest.setBeginIndex(skipBytes);
                    uploadPartCopyRequest.setPartNumber(i + 1);
                    //Map headers = new HashMap();
                    // 指定拷贝的源地址。
                    // headers.put(OSSHeaders.COPY_OBJECT_SOURCE, "/examplebucket/desexampleobject.txt");
                    // 指定源Object的拷贝范围。例如设置bytes=0~1023，表示拷贝1~1024字节的内容。
                    // headers.put(OSSHeaders.COPY_SOURCE_RANGE, "bytes=0~1023");
                    // 如果源Object的ETag值和您提供的ETag相等，则执行拷贝操作，并返回200 OK。
                    // headers.put(OSSHeaders.COPY_OBJECT_SOURCE_IF_MATCH, "5B3C1A2E053D763E1B002CC607C5****");
                    // 如果源Object的ETag值和您提供的ETag不相等，则执行拷贝操作，并返回200 OK。
                    // headers.put(OSSHeaders.COPY_OBJECT_SOURCE_IF_NONE_MATCH, "5B3C1A2E053D763E1B002CC607C5****");
                    // 如果指定的时间等于或者晚于文件实际修改时间，则正常拷贝文件，并返回200 OK。
                    // headers.put(OSSHeaders.COPY_OBJECT_SOURCE_IF_UNMODIFIED_SINCE, "2021-12-09T07:01:56.000Z");
                    // 如果源Object在用户指定的时间以后被修改过，则执行拷贝操作。
                    // headers.put(OSSHeaders.COPY_OBJECT_SOURCE_IF_MODIFIED_SINCE, "2021-12-09T07:01:56.000Z");
                    // uploadPartCopyRequest.setHeaders(headers);
                    UploadPartCopyResult uploadPartCopyResult = ossClient.uploadPartCopy(uploadPartCopyRequest);

                    // 将返回的分片ETag保存到partETags中。
                    partETags.add(uploadPartCopyResult.getPartETag());
                }

                // 提交分片拷贝任务。
                CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(
                        newBucketName, newPath, uploadId, partETags);
                ossClient.completeMultipartUpload(completeMultipartUploadRequest);
            }

            boolean found = ossClient.doesObjectExist(newBucketName, newPath);
            return found;
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("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.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return false;
    }



}
