package com.rj.service;

import com.rj.config.MinIOConfig;
import com.rj.utils.MinIOUtil;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.mock.web.MockMultipartFile;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

/**
 * MinIOService 测试类
 * 包含单元测试和集成测试
 * 
 * @author rj
 * @date 2025-10-02
 */
@ExtendWith(MockitoExtension.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@DisplayName("MinIOService 测试")
class MinIOServiceTest {

    @Mock
    private MinIOUtil minioUtil;

    @Mock
    private MinIOConfig minioConfig;

    @InjectMocks
    private MinIOService minioService;

    private static final String DEFAULT_BUCKET = "test-bucket";
    private static final String DEFAULT_ENDPOINT = "http://localhost:9000";
    private static final String TEST_FILE_NAME = "test-file.txt";
    private static final String TEST_CONTENT = "这是测试文件内容";

    @BeforeEach
    void setUp() {
        // 设置默认的Mock行为
        when(minioConfig.getBucketName()).thenReturn(DEFAULT_BUCKET);
        when(minioConfig.getEndpoint()).thenReturn(DEFAULT_ENDPOINT);
        when(minioConfig.getFileUrlPrefix()).thenReturn(DEFAULT_ENDPOINT + "/" + DEFAULT_BUCKET + "/");
    }

    @Nested
    @DisplayName("文件上传测试")
    class UploadTests {

        @Test
        @Order(1)
        @DisplayName("上传文件到默认存储桶")
        void testUploadFileToDefaultBucket() {
            // 准备测试数据
            MockMultipartFile file = new MockMultipartFile(
                    "file", 
                    "test.txt", 
                    "text/plain", 
                    TEST_CONTENT.getBytes()
            );

            // Mock行为
            when(minioUtil.uploadFile(file, DEFAULT_BUCKET)).thenReturn(TEST_FILE_NAME);

            // 执行测试
            String result = minioService.uploadFile(file);

            // 验证结果
            assertNotNull(result);
            assertTrue(result.contains(TEST_FILE_NAME));
            assertEquals(DEFAULT_ENDPOINT + "/" + DEFAULT_BUCKET + "/" + TEST_FILE_NAME, result);

            // 验证调用
            verify(minioUtil).uploadFile(file, DEFAULT_BUCKET);
            verify(minioConfig).getBucketName();
            verify(minioConfig).getFileUrlPrefix();
        }

        @Test
        @Order(2)
        @DisplayName("上传文件到指定存储桶")
        void testUploadFileToSpecificBucket() {
            // 准备测试数据
            String customBucket = "custom-bucket";
            MockMultipartFile file = new MockMultipartFile(
                    "file", 
                    "test.txt", 
                    "text/plain", 
                    TEST_CONTENT.getBytes()
            );

            // Mock行为
            when(minioUtil.uploadFile(file, customBucket)).thenReturn(TEST_FILE_NAME);

            // 执行测试
            String result = minioService.uploadFile(file, customBucket);

            // 验证结果
            assertNotNull(result);
            assertTrue(result.contains(TEST_FILE_NAME));
            assertEquals(DEFAULT_ENDPOINT + "/" + customBucket + "/" + TEST_FILE_NAME, result);

            // 验证调用
            verify(minioUtil).uploadFile(file, customBucket);
            verify(minioConfig).getEndpoint();
        }

        @Test
        @Order(3)
        @DisplayName("上传文件并指定文件名")
        void testUploadFileWithCustomName() {
            // 准备测试数据
            String customFileName = "custom-name.txt";
            MockMultipartFile file = new MockMultipartFile(
                    "file", 
                    "original.txt", 
                    "text/plain", 
                    TEST_CONTENT.getBytes()
            );

            // Mock行为
            when(minioUtil.uploadFile(file, DEFAULT_BUCKET, customFileName)).thenReturn(customFileName);

            // 执行测试
            String result = minioService.uploadFileWithName(file, customFileName);

            // 验证结果
            assertNotNull(result);
            assertTrue(result.contains(customFileName));

            // 验证调用
            verify(minioUtil).uploadFile(file, DEFAULT_BUCKET, customFileName);
        }

        @Test
        @Order(4)
        @DisplayName("上传文件流")
        void testUploadInputStream() {
            // 准备测试数据
            InputStream inputStream = new ByteArrayInputStream(TEST_CONTENT.getBytes());
            String contentType = "text/plain";

            // Mock行为
            when(minioUtil.uploadFile(inputStream, DEFAULT_BUCKET, TEST_FILE_NAME, contentType)).thenReturn(true);

            // 执行测试
            String result = minioService.uploadFile(inputStream, TEST_FILE_NAME, contentType);

            // 验证结果
            assertNotNull(result);
            assertTrue(result.contains(TEST_FILE_NAME));

            // 验证调用
            verify(minioUtil).uploadFile(inputStream, DEFAULT_BUCKET, TEST_FILE_NAME, contentType);
        }

        @Test
        @Order(5)
        @DisplayName("上传文件流失败")
        void testUploadInputStreamFailure() {
            // 准备测试数据
            InputStream inputStream = new ByteArrayInputStream(TEST_CONTENT.getBytes());
            String contentType = "text/plain";

            // Mock行为 - 返回失败
            when(minioUtil.uploadFile(inputStream, DEFAULT_BUCKET, TEST_FILE_NAME, contentType)).thenReturn(false);

            // 执行测试并验证异常
            RuntimeException exception = assertThrows(RuntimeException.class, () -> {
                minioService.uploadFile(inputStream, TEST_FILE_NAME, contentType);
            });

            assertEquals("文件上传失败", exception.getMessage());
        }
    }

    @Nested
    @DisplayName("文件下载测试")
    class DownloadTests {

        @Test
        @Order(6)
        @DisplayName("从默认存储桶下载文件")
        void testDownloadFileFromDefaultBucket() {
            // 准备测试数据
            InputStream expectedStream = new ByteArrayInputStream(TEST_CONTENT.getBytes());

            // Mock行为
            when(minioUtil.downloadFile(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(expectedStream);

            // 执行测试
            InputStream result = minioService.downloadFile(TEST_FILE_NAME);

            // 验证结果
            assertNotNull(result);
            assertEquals(expectedStream, result);

            // 验证调用
            verify(minioUtil).downloadFile(DEFAULT_BUCKET, TEST_FILE_NAME);
        }

        @Test
        @Order(7)
        @DisplayName("从指定存储桶下载文件")
        void testDownloadFileFromSpecificBucket() {
            // 准备测试数据
            String customBucket = "custom-bucket";
            InputStream expectedStream = new ByteArrayInputStream(TEST_CONTENT.getBytes());

            // Mock行为
            when(minioUtil.downloadFile(customBucket, TEST_FILE_NAME)).thenReturn(expectedStream);

            // 执行测试
            InputStream result = minioService.downloadFile(customBucket, TEST_FILE_NAME);

            // 验证结果
            assertNotNull(result);
            assertEquals(expectedStream, result);

            // 验证调用
            verify(minioUtil).downloadFile(customBucket, TEST_FILE_NAME);
        }
    }

    @Nested
    @DisplayName("文件删除测试")
    class DeleteTests {

        @Test
        @Order(8)
        @DisplayName("删除默认存储桶中的文件")
        void testDeleteFileFromDefaultBucket() {
            // Mock行为
            when(minioUtil.removeFile(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(true);

            // 执行测试
            boolean result = minioService.deleteFile(TEST_FILE_NAME);

            // 验证结果
            assertTrue(result);

            // 验证调用
            verify(minioUtil).removeFile(DEFAULT_BUCKET, TEST_FILE_NAME);
        }

        @Test
        @Order(9)
        @DisplayName("删除指定存储桶中的文件")
        void testDeleteFileFromSpecificBucket() {
            // 准备测试数据
            String customBucket = "custom-bucket";

            // Mock行为
            when(minioUtil.removeFile(customBucket, TEST_FILE_NAME)).thenReturn(true);

            // 执行测试
            boolean result = minioService.deleteFile(customBucket, TEST_FILE_NAME);

            // 验证结果
            assertTrue(result);

            // 验证调用
            verify(minioUtil).removeFile(customBucket, TEST_FILE_NAME);
        }

        @Test
        @Order(10)
        @DisplayName("删除文件失败")
        void testDeleteFileFailure() {
            // Mock行为 - 返回失败
            when(minioUtil.removeFile(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(false);

            // 执行测试
            boolean result = minioService.deleteFile(TEST_FILE_NAME);

            // 验证结果
            assertFalse(result);
        }
    }

    @Nested
    @DisplayName("文件列表测试")
    class ListTests {

        @Test
        @Order(11)
        @DisplayName("获取默认存储桶文件列表")
        void testListFilesFromDefaultBucket() {
            // 准备测试数据
            List<String> expectedFiles = Arrays.asList("file1.txt", "file2.txt", "file3.txt");

            // Mock行为
            when(minioUtil.listFiles(DEFAULT_BUCKET)).thenReturn(expectedFiles);

            // 执行测试
            List<String> result = minioService.listFiles();

            // 验证结果
            assertNotNull(result);
            assertEquals(3, result.size());
            assertEquals(expectedFiles, result);

            // 验证调用
            verify(minioUtil).listFiles(DEFAULT_BUCKET);
        }

        @Test
        @Order(12)
        @DisplayName("获取指定存储桶文件列表")
        void testListFilesFromSpecificBucket() {
            // 准备测试数据
            String customBucket = "custom-bucket";
            List<String> expectedFiles = Arrays.asList("custom1.txt", "custom2.txt");

            // Mock行为
            when(minioUtil.listFiles(customBucket)).thenReturn(expectedFiles);

            // 执行测试
            List<String> result = minioService.listFiles(customBucket);

            // 验证结果
            assertNotNull(result);
            assertEquals(2, result.size());
            assertEquals(expectedFiles, result);

            // 验证调用
            verify(minioUtil).listFiles(customBucket);
        }

        @Test
        @Order(13)
        @DisplayName("获取文件详细信息列表")
        void testListFileDetails() {
            // 准备测试数据
            MinIOUtil.FileInfo fileInfo1 = new MinIOUtil.FileInfo();
            fileInfo1.setObjectName("file1.txt");
            fileInfo1.setSize(100L);
            fileInfo1.setLastModified(ZonedDateTime.now());
            fileInfo1.setEtag("etag1");

            MinIOUtil.FileInfo fileInfo2 = new MinIOUtil.FileInfo();
            fileInfo2.setObjectName("file2.txt");
            fileInfo2.setSize(200L);
            fileInfo2.setLastModified(ZonedDateTime.now());
            fileInfo2.setEtag("etag2");

            List<MinIOUtil.FileInfo> expectedFiles = Arrays.asList(fileInfo1, fileInfo2);

            // Mock行为
            when(minioUtil.listFileDetails(DEFAULT_BUCKET)).thenReturn(expectedFiles);

            // 执行测试
            List<MinIOUtil.FileInfo> result = minioService.listFileDetails();

            // 验证结果
            assertNotNull(result);
            assertEquals(2, result.size());
            assertEquals(expectedFiles, result);

            // 验证调用
            verify(minioUtil).listFileDetails(DEFAULT_BUCKET);
        }
    }

    @Nested
    @DisplayName("预签名URL测试")
    class PresignedUrlTests {

        @Test
        @Order(14)
        @DisplayName("获取预签名URL（指定过期时间）")
        void testGetPresignedUrlWithExpires() {
            // 准备测试数据
            int expires = 3600; // 1小时
            String expectedUrl = "http://localhost:9000/test-bucket/test-file.txt?X-Amz-Expires=3600";

            // Mock行为
            when(minioUtil.getPresignedObjectUrl(DEFAULT_BUCKET, TEST_FILE_NAME, expires)).thenReturn(expectedUrl);

            // 执行测试
            String result = minioService.getPresignedUrl(TEST_FILE_NAME, expires);

            // 验证结果
            assertNotNull(result);
            assertEquals(expectedUrl, result);

            // 验证调用
            verify(minioUtil).getPresignedObjectUrl(DEFAULT_BUCKET, TEST_FILE_NAME, expires);
        }

        @Test
        @Order(15)
        @DisplayName("获取预签名URL（默认过期时间）")
        void testGetPresignedUrlWithDefaultExpires() {
            // 准备测试数据
            String expectedUrl = "http://localhost:9000/test-bucket/test-file.txt?X-Amz-Expires=604800";

            // Mock行为
            when(minioUtil.getPresignedObjectUrl(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(expectedUrl);

            // 执行测试
            String result = minioService.getPresignedUrl(TEST_FILE_NAME);

            // 验证结果
            assertNotNull(result);
            assertEquals(expectedUrl, result);

            // 验证调用
            verify(minioUtil).getPresignedObjectUrl(DEFAULT_BUCKET, TEST_FILE_NAME);
        }
    }

    @Nested
    @DisplayName("文件操作测试")
    class FileOperationTests {

        @Test
        @Order(16)
        @DisplayName("复制文件（同存储桶）")
        void testCopyFileInSameBucket() {
            // 准备测试数据
            String sourceFileName = "source.txt";
            String targetFileName = "target.txt";

            // Mock行为
            when(minioUtil.copyFile(DEFAULT_BUCKET, sourceFileName, DEFAULT_BUCKET, targetFileName)).thenReturn(true);

            // 执行测试
            boolean result = minioService.copyFile(sourceFileName, targetFileName);

            // 验证结果
            assertTrue(result);

            // 验证调用
            verify(minioUtil).copyFile(DEFAULT_BUCKET, sourceFileName, DEFAULT_BUCKET, targetFileName);
        }

        @Test
        @Order(17)
        @DisplayName("复制文件到其他存储桶")
        void testCopyFileToOtherBucket() {
            // 准备测试数据
            String sourceFileName = "source.txt";
            String targetBucket = "target-bucket";
            String targetFileName = "target.txt";

            // Mock行为
            when(minioUtil.copyFile(DEFAULT_BUCKET, sourceFileName, targetBucket, targetFileName)).thenReturn(true);

            // 执行测试
            boolean result = minioService.copyFile(sourceFileName, targetBucket, targetFileName);

            // 验证结果
            assertTrue(result);

            // 验证调用
            verify(minioUtil).copyFile(DEFAULT_BUCKET, sourceFileName, targetBucket, targetFileName);
        }

        @Test
        @Order(18)
        @DisplayName("检查文件是否存在")
        void testFileExists() {
            // Mock行为 - 文件存在
            when(minioUtil.getFileInfo(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(mock(io.minio.StatObjectResponse.class));

            // 执行测试
            boolean result = minioService.fileExists(TEST_FILE_NAME);

            // 验证结果
            assertTrue(result);

            // 验证调用
            verify(minioUtil).getFileInfo(DEFAULT_BUCKET, TEST_FILE_NAME);
        }

        @Test
        @Order(19)
        @DisplayName("检查文件不存在")
        void testFileNotExists() {
            // Mock行为 - 文件不存在
            when(minioUtil.getFileInfo(DEFAULT_BUCKET, TEST_FILE_NAME)).thenReturn(null);

            // 执行测试
            boolean result = minioService.fileExists(TEST_FILE_NAME);

            // 验证结果
            assertFalse(result);

            // 验证调用
            verify(minioUtil).getFileInfo(DEFAULT_BUCKET, TEST_FILE_NAME);
        }
    }

    @Nested
    @DisplayName("存储桶管理测试")
    class BucketManagementTests {

        @Test
        @Order(20)
        @DisplayName("创建存储桶")
        void testCreateBucket() {
            // 准备测试数据
            String bucketName = "new-bucket";

            // Mock行为
            when(minioUtil.createBucket(bucketName)).thenReturn(true);

            // 执行测试
            boolean result = minioService.createBucket(bucketName);

            // 验证结果
            assertTrue(result);

            // 验证调用
            verify(minioUtil).createBucket(bucketName);
        }

        @Test
        @Order(21)
        @DisplayName("删除存储桶")
        void testDeleteBucket() {
            // 准备测试数据
            String bucketName = "old-bucket";

            // Mock行为
            when(minioUtil.removeBucket(bucketName)).thenReturn(true);

            // 执行测试
            boolean result = minioService.deleteBucket(bucketName);

            // 验证结果
            assertTrue(result);

            // 验证调用
            verify(minioUtil).removeBucket(bucketName);
        }

        @Test
        @Order(22)
        @DisplayName("获取所有存储桶")
        void testGetAllBuckets() {
            // 准备测试数据
            List<String> expectedBuckets = Arrays.asList("bucket1", "bucket2", "bucket3");

            // Mock行为
            when(minioUtil.getAllBuckets()).thenReturn(expectedBuckets);

            // 执行测试
            List<String> result = minioService.getAllBuckets();

            // 验证结果
            assertNotNull(result);
            assertEquals(3, result.size());
            assertEquals(expectedBuckets, result);

            // 验证调用
            verify(minioUtil).getAllBuckets();
        }
    }

    @Nested
    @DisplayName("URL生成测试")
    class UrlGenerationTests {

        @Test
        @Order(23)
        @DisplayName("获取文件URL（默认存储桶）")
        void testGetFileUrlFromDefaultBucket() {
            // 执行测试
            String result = minioService.getFileUrl(TEST_FILE_NAME);

            // 验证结果
            assertNotNull(result);
            assertEquals(DEFAULT_ENDPOINT + "/" + DEFAULT_BUCKET + "/" + TEST_FILE_NAME, result);

            // 验证调用
            verify(minioConfig).getFileUrlPrefix();
        }

        @Test
        @Order(24)
        @DisplayName("获取文件URL（指定存储桶）")
        void testGetFileUrlFromSpecificBucket() {
            // 准备测试数据
            String customBucket = "custom-bucket";

            // 执行测试
            String result = minioService.getFileUrl(customBucket, TEST_FILE_NAME);

            // 验证结果
            assertNotNull(result);
            assertEquals(DEFAULT_ENDPOINT + "/" + customBucket + "/" + TEST_FILE_NAME, result);

            // 验证调用
            verify(minioConfig).getEndpoint();
        }
    }

    @Test
    @Order(25)
    @DisplayName("验证所有Mock对象的交互")
    void testAllMockInteractions() {
        // 这个测试确保我们没有遗漏任何重要的Mock验证
        // 在实际项目中，这通常不是必需的，但有助于确保测试的完整性
        
        // 执行一个简单的操作来触发Mock交互
        minioService.listFiles();
        
        // 验证基本的Mock交互
        verify(minioConfig, atLeastOnce()).getBucketName();
        verify(minioUtil, atLeastOnce()).listFiles(anyString());
    }
}
