package com.rj.minio;

import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * MinIO 存储服务测试类
 * 测试MinIO的连接、文件上传、下载、删除等功能
 * 
 * @author rj
 * @date 2025-10-02
 */
@SpringBootTest
@TestPropertySource(properties = {
    "minio.endpoint=http://101.35.52.237:19005",
    "minio.access-key=minioadmin",
    "minio.secret-key=minioadmin",
    "minio.bucket-name=test-bucket"
})
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MinIOServiceTest {

    private static final String ENDPOINT = "http://101.35.52.237:19005";
    private static final String ACCESS_KEY = "minioadmin";
    private static final String SECRET_KEY = "minioadmin";
    private static final String BUCKET_NAME = "car";
    private static final String TEST_OBJECT_NAME = "system.txt";
    private static final String TEST_CONTENT = "这是一个MinIO测试文件，用于验证文件存储和读取功能。\nTest content for MinIO file storage.";

    private MinioClient minioClient;

    @BeforeEach
    void setUp() {
        // 初始化MinIO客户端
        minioClient = MinioClient.builder()
                .endpoint(ENDPOINT)
                .credentials(ACCESS_KEY, SECRET_KEY)
                .build();
    }

    @Test
    @Order(1)
    @DisplayName("测试MinIO连接")
    void testConnection() {
        try {
            // 测试连接是否正常
            List<Bucket> buckets = minioClient.listBuckets();
            System.out.println("✅ MinIO连接成功！");
            System.out.println("当前存储桶列表：");
            for (Bucket bucket : buckets) {
                System.out.println("- " + bucket.name() + " (创建时间: " + bucket.creationDate() + ")");
            }
            Assertions.assertNotNull(buckets, "MinIO连接失败，无法获取存储桶列表");
        } catch (Exception e) {
            System.err.println("❌ MinIO连接失败: " + e.getMessage());
            Assertions.fail("MinIO连接测试失败: " + e.getMessage());
        }
    }

    @Test
    @Order(2)
    @DisplayName("创建测试存储桶")
    void testCreateBucket() {
        try {
            // 检查存储桶是否存在
            boolean bucketExists = minioClient.bucketExists(
                    BucketExistsArgs.builder().bucket(BUCKET_NAME).build()
            );

            if (!bucketExists) {
                // 创建存储桶
                minioClient.makeBucket(
                        MakeBucketArgs.builder().bucket(BUCKET_NAME).build()
                );
                System.out.println("✅ 成功创建存储桶: " + BUCKET_NAME);
            } else {
                System.out.println("✅ 存储桶已存在: " + BUCKET_NAME);
            }

            // 验证存储桶是否存在
            boolean exists = minioClient.bucketExists(
                    BucketExistsArgs.builder().bucket(BUCKET_NAME).build()
            );
            Assertions.assertTrue(exists, "存储桶创建失败或不存在");

        } catch (Exception e) {
            System.err.println("❌ 创建存储桶失败: " + e.getMessage());
            Assertions.fail("创建存储桶测试失败: " + e.getMessage());
        }
    }

    @Test
    @Order(3)
    @DisplayName("测试文件上传")
    void testUploadFile() {
        try {
            // 创建测试文件内容
            ByteArrayInputStream inputStream = new ByteArrayInputStream(
                    TEST_CONTENT.getBytes(StandardCharsets.UTF_8)
            );

            // 上传文件
            ObjectWriteResponse response = minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(BUCKET_NAME)
                            .object(TEST_OBJECT_NAME)
                            .stream(inputStream, TEST_CONTENT.getBytes(StandardCharsets.UTF_8).length, -1)
                            .contentType("text/plain")
                            .build()
            );

            System.out.println("✅ 文件上传成功!");
            System.out.println("文件名: " + TEST_OBJECT_NAME);
            System.out.println("存储桶: " + response.bucket());
            System.out.println("ETag: " + response.etag());

            Assertions.assertNotNull(response.etag(), "文件上传失败，未获得ETag");

        } catch (Exception e) {
            System.err.println("❌ 文件上传失败: " + e.getMessage());
            Assertions.fail("文件上传测试失败: " + e.getMessage());
        }
    }

    @Test
    @Order(4)
    @DisplayName("测试文件下载和内容验证")
    void testDownloadFile() {
        try {
            // 下载文件
            GetObjectResponse response = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(BUCKET_NAME)
                            .object(TEST_OBJECT_NAME)
                            .build()
            );

            // 读取文件内容
            StringBuilder content = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(response, StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    content.append(line).append("\n");
                }
            }

            String downloadedContent = content.toString().trim();
            System.out.println("✅ 文件下载成功!");
            System.out.println("文件内容: " + downloadedContent);

            // 验证文件内容是否正确
            Assertions.assertEquals(TEST_CONTENT, downloadedContent, "下载的文件内容与上传的内容不匹配");

        } catch (Exception e) {
            System.err.println("❌ 文件下载失败: " + e.getMessage());
            Assertions.fail("文件下载测试失败: " + e.getMessage());
        }
    }

    @Test
    @Order(5)
    @DisplayName("测试文件元数据获取")
    void testGetFileMetadata() {
        try {
            // 获取文件元数据
            StatObjectResponse stat = minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(BUCKET_NAME)
                            .object(TEST_OBJECT_NAME)
                            .build()
            );

            System.out.println("✅ 文件元数据获取成功!");
            System.out.println("文件名: " + stat.object());
            System.out.println("文件大小: " + stat.size() + " 字节");
            System.out.println("内容类型: " + stat.contentType());
            System.out.println("最后修改时间: " + stat.lastModified());
            System.out.println("ETag: " + stat.etag());

            Assertions.assertNotNull(stat, "无法获取文件元数据");
            Assertions.assertTrue(stat.size() > 0, "文件大小应该大于0");

        } catch (Exception e) {
            System.err.println("❌ 获取文件元数据失败: " + e.getMessage());
            Assertions.fail("获取文件元数据测试失败: " + e.getMessage());
        }
    }

    @Test
    @Order(6)
    @DisplayName("测试文件列表")
    void testListFiles() {
        try {
            // 列出存储桶中的所有文件
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(BUCKET_NAME)
                            .build()
            );

            System.out.println("✅ 文件列表获取成功!");
            System.out.println("存储桶 " + BUCKET_NAME + " 中的文件:");
            
            boolean foundTestFile = false;
            for (Result<Item> result : results) {
                Item item = result.get();
                System.out.println("- " + item.objectName() + " (大小: " + item.size() + " 字节, 修改时间: " + item.lastModified() + ")");
                if (TEST_OBJECT_NAME.equals(item.objectName())) {
                    foundTestFile = true;
                }
            }

            Assertions.assertTrue(foundTestFile, "在文件列表中未找到测试文件");

        } catch (Exception e) {
            System.err.println("❌ 获取文件列表失败: " + e.getMessage());
            Assertions.fail("获取文件列表测试失败: " + e.getMessage());
        }
    }

    @Test
    @Order(7)
    @DisplayName("测试文件删除")
    void testDeleteFile() {
        try {
            // 删除文件
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(BUCKET_NAME)
                            .object(TEST_OBJECT_NAME)
                            .build()
            );

            System.out.println("✅ 文件删除成功: " + TEST_OBJECT_NAME);

            // 验证文件是否已删除
            try {
                minioClient.statObject(
                        StatObjectArgs.builder()
                                .bucket(BUCKET_NAME)
                                .object(TEST_OBJECT_NAME)
                                .build()
                );
                Assertions.fail("文件删除失败，文件仍然存在");
            } catch (ErrorResponseException e) {
                if (e.errorResponse().code().equals("NoSuchKey")) {
                    System.out.println("✅ 验证成功：文件已被删除");
                } else {
                    throw e;
                }
            }

        } catch (Exception e) {
            System.err.println("❌ 文件删除失败: " + e.getMessage());
            Assertions.fail("文件删除测试失败: " + e.getMessage());
        }
    }

    @Test
    @Order(8)
    @DisplayName("测试大文件上传下载")
    void testLargeFileUploadDownload() {
        String largeFileName = "large-test-file.txt";
        try {
            // 创建一个较大的测试文件内容（约1MB）
            StringBuilder largeContent = new StringBuilder();
            String baseContent = "这是大文件测试内容，重复多次以创建较大的文件。Large file test content repeated multiple times.\n";
            for (int i = 0; i < 10000; i++) {
                largeContent.append(baseContent);
            }

            byte[] contentBytes = largeContent.toString().getBytes(StandardCharsets.UTF_8);
            ByteArrayInputStream inputStream = new ByteArrayInputStream(contentBytes);

            // 上传大文件
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(BUCKET_NAME)
                            .object(largeFileName)
                            .stream(inputStream, contentBytes.length, -1)
                            .contentType("text/plain")
                            .build()
            );

            System.out.println("✅ 大文件上传成功!");
            System.out.println("文件大小: " + contentBytes.length + " 字节");

            // 下载并验证大文件
            GetObjectResponse response = minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(BUCKET_NAME)
                            .object(largeFileName)
                            .build()
            );

            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = response.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }

            byte[] downloadedBytes = outputStream.toByteArray();
            System.out.println("✅ 大文件下载成功!");
            System.out.println("下载文件大小: " + downloadedBytes.length + " 字节");

            // 验证文件大小
            Assertions.assertEquals(contentBytes.length, downloadedBytes.length, "大文件上传下载后大小不匹配");

            // 清理大文件
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(BUCKET_NAME)
                            .object(largeFileName)
                            .build()
            );
            System.out.println("✅ 大文件清理完成");

        } catch (Exception e) {
            System.err.println("❌ 大文件测试失败: " + e.getMessage());
            Assertions.fail("大文件测试失败: " + e.getMessage());
        }
    }

    @AfterAll
    static void tearDown() {
        System.out.println("\n=== MinIO 测试完成 ===");
        System.out.println("所有测试已执行完毕，请检查测试结果。");
        System.out.println("MinIO服务器地址: " + ENDPOINT);
        System.out.println("测试存储桶: " + BUCKET_NAME);
    }
}
