package com.ai.da.common.utils;

import com.ai.da.common.config.exception.BusinessException;
import com.ai.da.common.constant.CommonConstant;
import io.netty.util.internal.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.S3Configuration;
import software.amazon.awssdk.services.s3.model.*;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;

import javax.annotation.PostConstruct;
import java.io.*;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;


@Slf4j
@Component
public class S3Util {

    @Value("${aws.s3.accessKeyId}")
    private String accessKeyId;

    @Value("${aws.s3.secretKey}")
    private String secretKey;

    @Value("${aws.s3.regionName}")
    private String region;

    public static String S3_ACCESS_KEY_ID = null;

    public static String S3_SECRET_KEY = null;

    public static String S3_REGION = null;


    @PostConstruct
    public void init() {
        S3_ACCESS_KEY_ID = accessKeyId;
        S3_SECRET_KEY = secretKey;
        S3_REGION = region;
    }

    private static S3Client s3Client;

    private static S3Presigner s3Presigner;

    /**
     * @return software.amazon.awssdk.services.s3.S3Client
     * @description: 获取S3客户端对象
     */
    public synchronized S3Client getS3Client() {
        if (null == s3Client) {
            s3Client = S3Client.builder()
                    .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(S3_ACCESS_KEY_ID, S3_SECRET_KEY)))
//                    .endpointOverride(URI.create(S3_URI))
                    .serviceConfiguration(item -> item.pathStyleAccessEnabled(true).checksumValidationEnabled(false))
                    .region(Region.of(S3_REGION))
                    .build();
        }

        return s3Client;
    }

    /**
     * @return software.amazon.awssdk.services.s3.presigner.S3Presigner
     * @description: 获取预签名对象
     */
    public synchronized S3Presigner getS3PreSigner() {
        if (null == s3Presigner) {
            s3Presigner = S3Presigner.builder()
                    .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create(S3_ACCESS_KEY_ID, S3_SECRET_KEY)))
//                    .endpointOverride(URI.create(S3_URI))
                    .serviceConfiguration(S3Configuration.builder()
                            .checksumValidationEnabled(false)
                            .pathStyleAccessEnabled(true)
                            .build())
                    .region(Region.of(S3_REGION))
                    .build();
        }
        return s3Presigner;
    }

    public String uploadImageFromBase64(String bucketName, String base64Image, String imageType) {
        S3Client s3Client = getS3Client();
        byte[] imageBytes = Base64.getDecoder().decode(base64Image);
        String fileName = UUID.randomUUID() + "." + imageType; // or any other image format

        try (InputStream in = new ByteArrayInputStream(imageBytes)) {
            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .contentType("image/png")
                    .contentLength((long) in.available())
                    .key(fileName)
                    .acl(ObjectCannedACL.PUBLIC_READ)
                    .build();
            s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(in, in.available()));

            log.info("uploadImageFromBase64 上传的位置：桶 - {}，路径 - {}", bucketName, fileName);
            return bucketName + "/" + fileName;
        } catch (Exception e) {
            e.printStackTrace();
            return null; // or throw an exception
        }
    }

    public String upload(String bucketName, String path, MultipartFile file) {
        S3Client s3Client = getS3Client();
        try {
            String fileName = file.getOriginalFilename();
            assert fileName != null;
            String[] split = fileName.split("\\.");
            if (split.length > 1) {
                fileName = path + "/" + UUID.randomUUID() + "." + split[split.length - 1];
            } else {
                fileName = path + "/" + UUID.randomUUID();
            }
            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .contentType(file.getContentType())
                    .contentLength(file.getSize())
                    .key(fileName)
                    .acl(ObjectCannedACL.PUBLIC_READ)
                    .build();
            s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(file.getInputStream(), file.getSize()));

            log.info("upload 上传的位置：桶 - {}，路径 - {}", bucketName, fileName);
            return bucketName + "/" + fileName;
        } catch (Exception e) {
            log.error("上传文件到S3失败 异常：{}", e.getMessage());
            return null;
        }
    }

    // todo
    public String upload(String bucketName, String path, MultipartFile file, String copy) {
        S3Client s3Client = getS3Client();
        InputStream in = null;
        try {
            in = file.getInputStream();

            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .contentType(file.getContentType())
                    .contentLength(file.getSize())
                    .key(path)
                    .acl(ObjectCannedACL.PUBLIC_READ)
                    .build();
            s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(file.getInputStream(), file.getSize()));
            log.info("upload-copy 上传的位置：桶 - {}，路径 - {}", bucketName, path);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return bucketName + "/" + path;
    }

    public InputStream download(String path) {
        if (!path.contains("/")) {
            throw new BusinessException("the.path.is.error");
        }
        int index = path.indexOf("/");
        String bucketName = path.substring(0, index);
        String objectName = path.substring(index + 1);
        return download(bucketName, objectName);
    }

    public InputStream download(String bucketName, String objectName) {
        try {
            S3Client s3Client = getS3Client();
            GetObjectRequest objectRequest = GetObjectRequest
                    .builder()
                    .key(objectName)
                    .bucket(bucketName)
                    .build();

//            ResponseBytes<GetObjectResponse> objectBytes = s3.getObjectAsBytes(objectRequest);
            ResponseBytes<GetObjectResponse> objectAsBytes = s3Client.getObjectAsBytes(objectRequest);
            byte[] data = objectAsBytes.asByteArray();
            log.info("download 下载图片位置：桶 - {}，路径 - {}", bucketName, objectName);
            return new ByteArrayInputStream(data);

            /*// Write the data to a local file.
            File myFile = new File("files/images.png");
            OutputStream os = new FileOutputStream(myFile);
            os.write(data);
            System.out.println("Successfully obtained bytes from an S3 object");
            os.close();
            return null;*/
//            return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            log.error("");
            throw new BusinessException("");
        }
    }

    public void deleteObject(String path) {
        if (!path.contains("/")) {
            throw new BusinessException("the.path.is.error");
        }
        int index = path.indexOf("/");
        String bucketName = path.substring(0, index);
        String objectName = path.substring(index + 1);
        deleteObject(bucketName, objectName);
    }

    public void deleteObject(String bucketName, String objectName) {
        try {
            S3Client s3Client = getS3Client();
            DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest
                    .builder()
                    .key(objectName)
                    .bucket(bucketName)
                    .build();
            s3Client.deleteObject(deleteObjectRequest);
            log.info("Object " + objectName + " successfully removed from bucket " + bucketName);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("Error while removing object " + objectName + " from bucket " + bucketName + ": " + e.getMessage());
        }
    }

    public String getPreSignedUrl(String path, int expiry) {
        if (LocalCacheUtils.getPresignedUrlCache(path) != null) {
            return LocalCacheUtils.getPresignedUrlCache(path);
        } else {
            if (!path.contains("/")) {
                throw new BusinessException("the.path.is.error");
            }
            int index = path.indexOf("/");
            String bucketName = path.substring(0, index);
            String fileName = path.substring(index + 1);
            String preSignedUrl = getPreSignatureUrl(bucketName, fileName, expiry);
            LocalCacheUtils.setPresignedUrlCache(path, preSignedUrl);
            return preSignedUrl;
        }
    }

    public String getPreSignedUrl(String path, int expiry, boolean resetCache) {
        if (resetCache || LocalCacheUtils.getPresignedUrlCache(path) == null) {
            if (!path.contains("/")) {
                throw new BusinessException("the.path.is.error");
            }
            int index = path.indexOf("/");
            String bucketName = path.substring(0, index);
            String fileName = path.substring(index + 1);
            String presignedUrl = getPreSignatureUrl(bucketName, fileName, expiry);
            LocalCacheUtils.setPresignedUrlCache(path, presignedUrl);
            return presignedUrl;
        } else {
            return LocalCacheUtils.getPresignedUrlCache(path);
        }
    }

    /**
     * @param keyName               key名称: test/2022/06/123.pdf
     * @param signatureDurationTime 有效期 单位：秒
     * @return java.lang.String
     * @description: 生成预签名URL
     */
    public String getPreSignatureUrl(String bucket, String keyName, Integer signatureDurationTime) {
        String preSignatureUrl = "";
        try {
            S3Presigner s3PreSigner = getS3PreSigner();
            S3Client s3Client = getS3Client();
            setObjectAcl(s3Client, bucket, keyName, ObjectCannedACL.PUBLIC_READ);

            GetObjectRequest getObjectRequest = GetObjectRequest.builder()
                    .bucket(bucket)
                    .key(keyName)
                    .build();

            //设置预签名URL可访问时间
            signatureDurationTime = Optional.ofNullable(signatureDurationTime)
                    .map(item -> {
                        if (item.intValue() > CommonConstant.Numbers.NUMBER_10080) {
                            item = CommonConstant.Numbers.NUMBER_10080;
                        }
                        return item;
                    })
                    .orElse(CommonConstant.Numbers.NUMBER_10);
            GetObjectPresignRequest getObjectPresignRequest = GetObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(signatureDurationTime))
                    .getObjectRequest(getObjectRequest)
                    .build();

            PresignedGetObjectRequest presignedGetObjectRequest =
                    s3PreSigner.presignGetObject(getObjectPresignRequest);

            preSignatureUrl = String.valueOf(presignedGetObjectRequest.url());
        } catch (Exception e) {
            log.error("生成预签名URL失败，异常：{}", e.getMessage());
        }
        return preSignatureUrl;
    }

    public static void setObjectAcl(S3Client s3, String bucketName, String keyName, ObjectCannedACL acl) {
        PutObjectAclRequest aclRequest = PutObjectAclRequest.builder()
                .bucket(bucketName)
                .key(keyName)
                .acl(acl)
                .build();
        s3.putObjectAcl(aclRequest);
    }
    public boolean doesObjectExist(String bucketName, String objectName) {
        try {
            S3Client s3Client = getS3Client();
            HeadObjectRequest headObjectRequest = HeadObjectRequest.builder()
                    .bucket(bucketName)
                    .key(objectName)
                    .build();
            HeadObjectResponse headObjectResponse = s3Client.headObject(headObjectRequest);
            return true;
        } catch (Exception e) {
            log.info("指定文件 {}/{} 不存在", bucketName, objectName);
            // 如果发生异常，说明文件不存在或者出现了其他错误
            return false;
        }
    }

    public String base64UploadToPath(String base64, String bucketName, String path) {
        S3Client s3Client = getS3Client();
        String[] parts = base64.split(",");
        String imageType = parts[0].split("/")[1].split(";")[0];
        String base64Data = parts[1];

        byte[] imageBytes = Base64.getDecoder().decode(base64Data);
        String fileName;
        if (!StringUtil.isNullOrEmpty(path)) {
            fileName = path + "." + imageType; // or any other image format
        } else {
            fileName = UUID.randomUUID() + "." + imageType;
        }

        try (InputStream in = new ByteArrayInputStream(imageBytes)) {
            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .contentType("image/" + imageType)
                    .contentLength((long) in.available())
                    .key(fileName)
                    .acl(ObjectCannedACL.PUBLIC_READ)
                    .build();
            s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(in, in.available()));

            log.info("base64UploadToPath 上传的位置：桶 - {}，路径 - {}", bucketName, fileName);
            return bucketName + "/" + fileName;
        } catch (Exception e) {
            e.printStackTrace();
            return null; // or throw an exception
        }
    }


    public List<String> listAllBucket() {
        S3Client s3Client = getS3Client();
        ListBucketsResponse listBucketsResponse = s3Client.listBuckets();
        List<Bucket> buckets = listBucketsResponse.buckets();
        return buckets.stream().map(Bucket::name).collect(Collectors.toList());
    }

}
