package io.wdd.func.oss.service;


import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.*;
import io.wdd.common.handler.MyRuntimeException;
import io.wdd.func.oss.config.OctopusObjectSummary;
import io.wdd.func.oss.config.OssConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

import static io.wdd.func.oss.config.OSSConfiguration.ALL_OSS_CONFIG;

/**
 * https://docs.oracle.com/en-us/iaas/Content/Object/Tasks/s3compatibleapi.htm#usingAPI
 */
@Service
@Slf4j
public class OSSCoreServiceImpl implements OSSCoreService {


    @Override
    public List<List<Bucket>> listAllBucket() {

        return ALL_OSS_CONFIG
                .stream()
                .map(
                        ossConfig -> listBucketList(ossConfig)
                )
                .collect(Collectors.toList());
    }

    @Override
    public List<Bucket> listBucketList(OssConfig ossConfig) {
        AmazonS3 client = ossConfig.getClient();

        return client.listBuckets();
    }

    @Override
    public ObjectListing listBucketObjects(OssConfig ossConfig, String bucketName) {

        AmazonS3 client = ossConfig.getClient();

        return client.listObjects(bucketName);
    }

    @Override
    public void headBucket(OssConfig ossConfig, String bucketName) {
        AmazonS3 client = ossConfig.getClient();


        HeadBucketResult headBucketResult = client.headBucket(
                new HeadBucketRequest(bucketName)
        );


    }

    @Override
    public Bucket createBucket(OssConfig ossConfig, String bucketName) {
        AmazonS3 client = ossConfig.getClient();

        Bucket bucket = client.createBucket(
                bucketName
        );


        return bucket;
    }

    @Override
    public boolean deleteBucket(OssConfig ossConfig, String bucketName) {

        AmazonS3 client = ossConfig.getClient();

        try {
            client.deleteBucket(bucketName);

        } catch (Exception e) {
            log.error(
                    "删除桶错误 ！ ossConfig is {}， bucketName is {}",
                    ossConfig,
                    bucketName
            );
            return false;
        }

        return true;
    }

    @Override
    public void getBucketLocation(OssConfig ossConfig, String bucketName) {

    }

    @Override
    public OctopusObjectSummary getObjectSimple(OssConfig ossConfig, String bucketName, String objectName) {
        return buildOctopusObjectSummary(
                ossConfig,
                bucketName,
                objectName
        );
    }

    @Override
    public S3Object getObject(OssConfig ossConfig, String bucketName, String objectName) {

        AmazonS3 client = ossConfig.getClient();

        try {
            return client.getObject(
                    bucketName,
                    objectName
            );
        } catch (SdkClientException e) {
            log.error(
                    "出查询单个对象 出现错误, 错误原因为 => {}",
                    e.getMessage()
            );
            throw new MyRuntimeException(e.getMessage());

        }

    }

    @Override
    public boolean deleteObject(OssConfig ossConfig, String bucketName, String objectName) {
        AmazonS3 client = ossConfig.getClient();

        try {
            client.deleteObject(
                    bucketName,
                    objectName
            );
        } catch (SdkClientException e) {
            log.error(
                    "删除对象错误 ！ ossConfig is {}， bucketName is {}, objectName is {}",
                    ossConfig,
                    bucketName,
                    objectName
            );
            return false;
        }

        return true;
    }

    @Override
    public ObjectMetadata headObject(OssConfig ossConfig, String bucketName, String objectName) {

        AmazonS3 client = ossConfig.getClient();


        try {

            return client.getObjectMetadata(
                    bucketName,
                    objectName
            );

        } catch (Exception e) {
            log.error(
                    "查询对象的Head信息错误, 错误原因 => {}",
                    e.getMessage()
            );

            throw new MyRuntimeException(e.getMessage());
        }

    }

    @Override
    public HashMap<String, String> createObject(OssConfig ossConfig, String bucketName, String objectName, MultipartFile file) {
        AmazonS3 client = ossConfig.getClient();

        try {

            ObjectMetadata metadata = new ObjectMetadata();

            long contentLength = Math.max(
                    file.getBytes().length,
                    file.getSize()
            );
            metadata.setContentLength(contentLength);

            PutObjectResult putObjectResult = client.putObject(
                    bucketName,
                    objectName,
                    file.getInputStream(),
                    metadata
            );


            HashMap<String, String> map = new HashMap<>();
            // 需要返回下载地址
            S3Object s3Object = this.getObject(
                    ossConfig,
                    bucketName,
                    objectName
            );

            URI uri = s3Object
                    .getObjectContent()
                    .getHttpRequest()
                    .getURI();
            map.put(
                    "origin",
                    String.valueOf(uri)
            );


            return map;

        } catch (IOException e) {
            log.error(
                    "文件-创建一个对象失败，原因为 => {}",
                    e.getMessage()
            );
            throw new RuntimeException(e);
        }
    }

    @Override
    public OctopusObjectSummary createObject(OssConfig ossConfig, String objectName, File file) {

        AmazonS3 client = ossConfig.getClient();
        List<Bucket> bucketList = client.listBuckets(
        );

        Bucket randomBucket = bucketList
                .stream()
                // 理论上此处应该增加筛选公开的桶的策略
                .findAny()
                .get();

        Assert.notNull(
                randomBucket,
                "[对象]-创建失败！ 未获取到有效的 bucketName"
        );

        return createObject(
                ossConfig,
                randomBucket.getName(),
                objectName,
                file
        );
    }

    @Override
    public OctopusObjectSummary createObject(OssConfig ossConfig, String bucketName, String objectName, File file) {

        AmazonS3 client = ossConfig.getClient();

        log.info(
                "[对象]-创建新的对象，OSS后端为 [ {} ], 桶名称为 [ {} ], 文件名称为 [ {} ]",
                ossConfig.getName() + "-" + ossConfig.getRegion(),
                bucketName,
                objectName
        );

        try {

            // 调用sdk上传文件
            client.putObject(
                    bucketName,
                    objectName,
                    file
            );

        } catch (SdkClientException e) {
            log.error(
                    "[对象]-创建对象失败！ OSS后端为 [ {} ], 桶名称为 [ {} ], 文件名称为 [ {} ] 原因为 {}",
                    ossConfig.getName() + "-" + ossConfig.getRegion(),
                    bucketName,
                    objectName,
                    e.getMessage()
            );
            throw new MyRuntimeException(e);
        }

        // 查询对象的相关信息返回不一样的内容
        return buildOctopusObjectSummary(
                ossConfig,
                bucketName,
                objectName
        );

    }

    private OctopusObjectSummary buildOctopusObjectSummary(OssConfig ossConfig, String bucketName, String objectName) {

        S3Object s3Object = this.getObject(
                ossConfig,
                bucketName,
                objectName
        );

        return convertOOSFromS3Object(s3Object);
    }

    private OctopusObjectSummary convertOOSFromS3Object(S3Object s3Object) {
        S3ObjectInputStream objectContent = s3Object.getObjectContent();
        ObjectMetadata objectMetadata = s3Object.getObjectMetadata();

        OctopusObjectSummary.AccessDTO accessDTO = OctopusObjectSummary.AccessDTO
                .builder()
                .originUrl(objectContent
                                   .getHttpRequest()
                                   .getURI())
                .build();

        return OctopusObjectSummary
                .builder()
                .key(s3Object.getKey())
                .size(objectMetadata.getContentLength())
                .lastModified(objectMetadata.getLastModified())
                .contentType(objectMetadata.getContentType())
                .eTag(objectMetadata.getETag())
                .bucketName(s3Object.getBucketName())
                .access(accessDTO)
                .build();
    }

    @Override
    public String restoreObject(OssConfig ossConfig, String bucketName, String objectName) {
        AmazonS3 client = ossConfig.getClient();


        RestoreObjectResult restoreObjectResult = client.restoreObjectV2(
                new RestoreObjectRequest(
                        bucketName,
                        objectName
                )
        );

        return restoreObjectResult.getRestoreOutputPath();
    }
}
