package com.ruoyi.common.utils;

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.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
 * AWS S3 文件上传工具类
 */
@Component
public class AwsS3Util {

    // -------------------------- 配置参数（需根据实际环境修改）--------------------------
    @Value("${aws.accessKey}")
    private String AWS_ACCESS_KEY; // IAM用户AccessKey
    @Value("${aws.secretKey}")
    private String AWS_SECRET_KEY;   // IAM用户SecretKey
    @Value("${aws.endpoint}")
    private String S3_REGION;    // S3区域（如东京：ap-northeast-1，新加坡：ap-southeast-1）
    @Value("${aws.bucketName}")
    private String S3_BUCKET_NAME;    // S3桶名称
    @Value("${aws.domain}")
    private String domain;
    // --------------------------------------------------------------------------------

    /**
     * 获取S3客户端（单例模式，避免重复创建连接）
     */
    private S3Client getS3Client() {
        // 配置AWS凭证（生产环境建议使用环境变量/IAM角色，而非硬编码）
        AwsBasicCredentials credentials = AwsBasicCredentials.create(
                AWS_ACCESS_KEY,
                AWS_SECRET_KEY
        );

        return S3Client.builder()
                .region(Region.of(S3_REGION))
                .credentialsProvider(StaticCredentialsProvider.create(credentials))
                .build();
    }

    /**
     * 上传MultipartFile到S3，返回文件访问地址
     *
     * @param file     前端上传的MultipartFile
     * @param isPublic 是否公开访问（true：返回公开URL；false：返回预签名URL）
     * @return 文件访问地址（URL字符串）
     * @throws IOException 流操作异常
     * @throws S3Exception S3服务异常
     */
    public String uploadMultipartFile(MultipartFile file, boolean isPublic) throws Exception {
        // 1. 校验文件合法性
        if (file.isEmpty()) {
            throw new IllegalArgumentException("上传文件不能为空");
        }

        // 1. MultipartFile 转换为临时 Path（沿用之前的方案1）
        Path tempPath = convertToTempPath(file);

        try {
            // 2. 生成 S3 中的唯一文件键（避免重名）
            String s3Key = generateS3Key(file.getOriginalFilename());

            // 3. 构建 S3 上传请求（无 ACL 配置）
            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                    .bucket(S3_BUCKET_NAME)
                    .key(s3Key)
                    .build();

            S3Client s3Client = getS3Client();
            // 4. 上传文件到 S3
            PutObjectResponse putObjectResponse = s3Client.putObject(putObjectRequest, tempPath);

            // 返回 S3 文件的访问地址（根据 Bucket Policy 决定是否可公开访问）
            return domain + s3Key;
        } finally {
            // 5. 清理临时文件
            Files.deleteIfExists(tempPath);
        }
    }

    /**
     * 将 MultipartFile 转换为临时文件的 Path（自动清理）
     */
    public Path convertToTempPath(MultipartFile file) throws IOException {
        // 1. 校验文件是否为空
        if (file.isEmpty()) {
            throw new IllegalArgumentException("上传文件不能为空");
        }

        // 2. 获取文件原始后缀（如 .txt、.jpg，避免临时文件无后缀）
        String originalFilename = file.getOriginalFilename();
        String suffix = originalFilename != null ?
                originalFilename.substring(originalFilename.lastIndexOf(".")) : ".tmp";

        // 3. 创建临时文件（默认存储在系统临时目录，如 /tmp 或 C:\Users\XXX\AppData\Local\Temp）
        // 参数说明：前缀（temp_）、后缀（.txt）、文件属性（默认 null 即可）
        Path tempFile = Files.createTempFile("temp_", suffix);

        // 4. 将 MultipartFile 的字节内容写入临时文件
        Files.write(tempFile, file.getBytes());

        // 5. （可选）设置临时文件JVM退出时自动删除（增强清理机制）
        tempFile.toFile().deleteOnExit();

        return tempFile;
    }

    /**
     * 获取公开文件URL（适用于PUBLIC_READ权限的文件）
     * 格式：https://桶名.s3.区域.amazonaws.com/文件Key
     */
    private String getPublicFileUrl(String s3FileKey) {
        return String.format("https://%s.s3.%s.amazonaws.com/%s",
                S3_BUCKET_NAME,
                S3_REGION,
                s3FileKey);
    }

    public String uploadFileByUrl(String fileUrl) throws Exception {
        return uploadFileByUrl(fileUrl, true);
    }

    // 通过下载链接上传文件到S3
    public String uploadFileByUrl(String fileUrl, boolean isPublic) throws Exception {
        if (fileUrl == null || fileUrl.trim().isEmpty()) {
            throw new IllegalArgumentException("文件下载链接不能为空");
        }

        // 下载文件到临时路径
        Path tempPath = downloadFileToTemp(fileUrl);

        try {
            String originalFileName = extractFileNameFromUrl(fileUrl);
            String s3Key = generateS3Key(originalFileName);

            PutObjectRequest putObjectRequest = PutObjectRequest.builder()
                    .bucket(S3_BUCKET_NAME)
                    .key(s3Key)
                    .build();

            S3Client s3Client = getS3Client();
            PutObjectResponse response = s3Client.putObject(putObjectRequest, tempPath);

            return domain + s3Key;
        } finally {
            Files.deleteIfExists(tempPath);
        }
    }

    // 辅助方法：生成S3文件键（复用逻辑，提取为独立方法）
    private String generateS3Key(String originalFileName) {
        String uuid = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 8);
        String dateTime = new SimpleDateFormat("yyyy/MM/dd").format(new Date()); // Java 1.8用SimpleDateFormat
        return dateTime + "/" + uuid + "_" + originalFileName;
    }


    // 辅助方法：通过链接下载文件到临时路径（Java 1.8用HttpURLConnection）
    private Path downloadFileToTemp(String fileUrl) throws Exception {
        // 提取文件后缀，用于临时文件
        String suffix = getFileSuffixFromUrl(fileUrl);
        Path tempPath = Files.createTempFile("url-upload-", suffix);

        HttpURLConnection connection = null;
        InputStream in = null;
        OutputStream out = null;

        try {
            URL url = new URL(fileUrl);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(5000); // 连接超时5秒
            connection.setReadTimeout(10000);   // 读取超时10秒

            int responseCode = connection.getResponseCode();
            if (responseCode < 200 || responseCode >= 300) {
                throw new RuntimeException("文件下载失败，状态码：" + responseCode);
            }

            // 读取输入流并写入临时文件
            in = connection.getInputStream();
            out = Files.newOutputStream(tempPath);

            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = in.read(buffer)) != -1) {
                out.write(buffer, 0, bytesRead);
            }

        } finally {
            // 关闭资源（Java 1.8需手动关闭，无try-with-resources也可，但建议显式关闭）
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (connection != null) {
                connection.disconnect();
            }
        }

        return tempPath;
    }

    // 辅助方法：从URL提取文件名
    private String extractFileNameFromUrl(String fileUrl) {
        String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
        // 处理URL中的参数（如 ?xx=xx）
        if (fileName.contains("?")) {
            fileName = fileName.split("\\?")[0];
        }
        return fileName.isEmpty() ? "default_file" : fileName;
    }

    // 辅助方法：从URL提取文件后缀
    private String getFileSuffixFromUrl(String fileUrl) {
        String fileName = extractFileNameFromUrl(fileUrl);
        if (fileName.contains(".")) {
            return fileName.substring(fileName.lastIndexOf("."));
        }
        return ".tmp"; // 无后缀时默认临时文件后缀
    }


}