package com.mosquito.project.dto;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.time.LocalDateTime;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;

@DisplayName("ApiResponse 测试")
class ApiResponseTest {

    @Nested
    @DisplayName("成功响应测试")
    class SuccessResponseTests {

        @Test
        @DisplayName("success(data) 应该创建成功响应")
        void shouldCreateSuccessResponse() {
            // Given
            String data = "test data";

            // When
            ApiResponse<String> response = ApiResponse.success(data);

            // Then
            assertThat(response.getCode()).isEqualTo(200);
            assertThat(response.getMessage()).isEqualTo("success");
            assertThat(response.getData()).isEqualTo(data);
            assertThat(response.getTimestamp()).isNotNull();
            assertThat(response.getError()).isNull();
            assertThat(response.getMeta()).isNull();
        }

        @Test
        @DisplayName("success(data, message) 应该创建带自定义消息的成功响应")
        void shouldCreateSuccessResponseWithCustomMessage() {
            // Given
            String data = "test data";
            String message = "custom success message";

            // When
            ApiResponse<String> response = ApiResponse.success(data, message);

            // Then
            assertThat(response.getCode()).isEqualTo(200);
            assertThat(response.getMessage()).isEqualTo(message);
            assertThat(response.getData()).isEqualTo(data);
            assertThat(response.getTimestamp()).isNotNull();
        }

        @Test
        @DisplayName("paginated() 应该创建分页响应")
        void shouldCreatePaginatedResponse() {
            String data = "test data";
            int page = 0;
            int size = 10;
            long total = 100;

            ApiResponse<String> response = ApiResponse.paginated(data, page, size, total);

            assertThat(response.getCode()).isEqualTo(200);
            assertThat(response.getMessage()).isEqualTo("success");
            assertThat(response.getData()).isEqualTo(data);
            assertThat(response.getMeta()).isNotNull();
            assertThat(response.getMeta().getPagination()).isNotNull();
            assertThat(response.getMeta().getPagination().getPage()).isEqualTo(page);
            assertThat(response.getMeta().getPagination().getSize()).isEqualTo(size);
            assertThat(response.getMeta().getPagination().getTotal()).isEqualTo(total);
        }
    }

    @Nested
    @DisplayName("错误响应测试")
    class ErrorResponseTests {

        @Test
        @DisplayName("error(code, message) 应该创建错误响应")
        void shouldCreateErrorResponse() {
            int code = 400;
            String message = "Bad Request";

            ApiResponse<Void> response = ApiResponse.error(code, message);

            assertThat(response.getCode()).isEqualTo(code);
            assertThat(response.getMessage()).isEqualTo(message);
            assertThat(response.getData()).isNull();
            assertThat(response.getError()).isNotNull();
            assertThat(response.getError().getMessage()).isEqualTo(message);
            assertThat(response.getTimestamp()).isNotNull();
        }

        @Test
        @DisplayName("error(code, message, details) 应该创建带详情的错误响应")
        void shouldCreateErrorResponseWithDetails() {
            int code = 400;
            String message = "Validation Error";
            Map<String, String> details = Map.of("field", "username", "error", "required");

            ApiResponse<Void> response = ApiResponse.error(code, message, details);

            assertThat(response.getCode()).isEqualTo(code);
            assertThat(response.getMessage()).isEqualTo(message);
            assertThat(response.getError()).isNotNull();
            assertThat(response.getError().getMessage()).isEqualTo(message);
            assertThat(response.getError().getDetails()).isEqualTo(details);
        }

        @Test
        @DisplayName("error(code, message, details, traceId) 应该创建带追踪ID的错误响应")
        void shouldCreateErrorResponseWithTraceId() {
            int code = 500;
            String message = "Internal Server Error";
            String details = "Database connection failed";
            String traceId = "trace-123";

            ApiResponse<Void> response = ApiResponse.error(code, message, details, traceId);

            assertThat(response.getCode()).isEqualTo(code);
            assertThat(response.getMessage()).isEqualTo(message);
            assertThat(response.getError()).isNotNull();
            assertThat(response.getError().getMessage()).isEqualTo(message);
            assertThat(response.getError().getDetails()).isEqualTo(details);
            assertThat(response.getTraceId()).isEqualTo(traceId);
        }
    }

    @Nested
    @DisplayName("PaginationMeta 测试")
    class PaginationMetaTests {

        @Test
        @DisplayName("of() 应该正确计算分页信息 - 第一页")
        void shouldCalculatePaginationForFirstPage() {
            ApiResponse.PaginationMeta meta = ApiResponse.PaginationMeta.of(0, 10, 100);

            assertThat(meta.getPage()).isEqualTo(0);
            assertThat(meta.getSize()).isEqualTo(10);
            assertThat(meta.getTotal()).isEqualTo(100);
            assertThat(meta.getTotalPages()).isEqualTo(10);
            assertThat(meta.isHasNext()).isTrue();
            assertThat(meta.isHasPrevious()).isFalse();
        }

        @Test
        @DisplayName("of() 应该正确计算分页信息 - 中间页")
        void shouldCalculatePaginationForMiddlePage() {
            ApiResponse.PaginationMeta meta = ApiResponse.PaginationMeta.of(5, 10, 100);

            assertThat(meta.getPage()).isEqualTo(5);
            assertThat(meta.getTotalPages()).isEqualTo(10);
            assertThat(meta.isHasNext()).isTrue();
            assertThat(meta.isHasPrevious()).isTrue();
        }

        @Test
        @DisplayName("of() 应该正确计算分页信息 - 最后一页")
        void shouldCalculatePaginationForLastPage() {
            ApiResponse.PaginationMeta meta = ApiResponse.PaginationMeta.of(9, 10, 100);

            assertThat(meta.getPage()).isEqualTo(9);
            assertThat(meta.getTotalPages()).isEqualTo(10);
            assertThat(meta.isHasNext()).isFalse();
            assertThat(meta.isHasPrevious()).isTrue();
        }

        @Test
        @DisplayName("of() 应该处理不能整除的总数")
        void shouldHandleNonDivisibleTotal() {
            ApiResponse.PaginationMeta meta = ApiResponse.PaginationMeta.of(0, 10, 95);

            assertThat(meta.getTotalPages()).isEqualTo(10);
            assertThat(meta.isHasNext()).isTrue();
        }

        @Test
        @DisplayName("of() 应该处理空结果")
        void shouldHandleEmptyResults() {
            ApiResponse.PaginationMeta meta = ApiResponse.PaginationMeta.of(0, 10, 0);

            assertThat(meta.getTotalPages()).isEqualTo(0);
            assertThat(meta.isHasNext()).isFalse();
            assertThat(meta.isHasPrevious()).isFalse();
        }

        @Test
        @DisplayName("of() 应该处理单页结果")
        void shouldHandleSinglePageResults() {
            ApiResponse.PaginationMeta meta = ApiResponse.PaginationMeta.of(0, 10, 5);

            assertThat(meta.getTotalPages()).isEqualTo(1);
            assertThat(meta.isHasNext()).isFalse();
            assertThat(meta.isHasPrevious()).isFalse();
        }
    }

    @Nested
    @DisplayName("Meta 测试")
    class MetaTests {

        @Test
        @DisplayName("createPagination() 应该创建带分页信息的Meta")
        void shouldCreateMetaWithPagination() {
            ApiResponse.Meta meta = ApiResponse.Meta.createPagination(0, 10, 100);

            assertThat(meta).isNotNull();
            assertThat(meta.getPagination()).isNotNull();
            assertThat(meta.getPagination().getPage()).isEqualTo(0);
            assertThat(meta.getPagination().getSize()).isEqualTo(10);
            assertThat(meta.getPagination().getTotal()).isEqualTo(100);
        }

        @Test
        @DisplayName("应该支持额外的元数据")
        void shouldSupportExtraMetadata() {
            ApiResponse.Meta meta = new ApiResponse.Meta();
            Map<String, Object> extra = Map.of("key1", "value1", "key2", 123);

            meta.setExtra(extra);

            assertThat(meta.getExtra()).isEqualTo(extra);
        }
    }

    @Nested
    @DisplayName("Error 测试")
    class ErrorTests {

        @Test
        @DisplayName("Error(message) 应该创建简单错误")
        void shouldCreateSimpleError() {
            String message = "Error message";

            ApiResponse.Error error = new ApiResponse.Error(message);

            assertThat(error.getMessage()).isEqualTo(message);
            assertThat(error.getDetails()).isNull();
            assertThat(error.getCode()).isNull();
        }

        @Test
        @DisplayName("Error(message, details) 应该创建带详情的错误")
        void shouldCreateErrorWithDetails() {
            String message = "Validation Error";
            Object details = Map.of("field", "email");

            ApiResponse.Error error = new ApiResponse.Error(message, details);

            assertThat(error.getMessage()).isEqualTo(message);
            assertThat(error.getDetails()).isEqualTo(details);
            assertThat(error.getCode()).isNull();
        }

        @Test
        @DisplayName("Error(message, details, code) 应该创建完整错误")
        void shouldCreateFullError() {
            String message = "Business Error";
            Object details = "Insufficient balance";
            String code = "ERR_001";

            ApiResponse.Error error = new ApiResponse.Error(message, details, code);

            assertThat(error.getMessage()).isEqualTo(message);
            assertThat(error.getDetails()).isEqualTo(details);
            assertThat(error.getCode()).isEqualTo(code);
        }
    }

    @Nested
    @DisplayName("Builder 测试")
    class BuilderTests {

        @Test
        @DisplayName("builder() 应该支持完整构建")
        void shouldSupportFullBuild() {
            LocalDateTime now = LocalDateTime.now();
            ApiResponse.Meta meta = new ApiResponse.Meta();
            ApiResponse.Error error = new ApiResponse.Error("error");

            ApiResponse<String> response = ApiResponse.<String>builder()
                .code(200)
                .message("test")
                .data("data")
                .meta(meta)
                .error(error)
                .timestamp(now)
                .traceId("trace-123")
                .build();

            assertThat(response.getCode()).isEqualTo(200);
            assertThat(response.getMessage()).isEqualTo("test");
            assertThat(response.getData()).isEqualTo("data");
            assertThat(response.getMeta()).isEqualTo(meta);
            assertThat(response.getError()).isEqualTo(error);
            assertThat(response.getTimestamp()).isEqualTo(now);
            assertThat(response.getTraceId()).isEqualTo("trace-123");
        }

        @Test
        @DisplayName("builder() 应该支持部分构建")
        void shouldSupportPartialBuild() {
            ApiResponse<String> response = ApiResponse.<String>builder()
                .code(200)
                .data("data")
                .build();

            assertThat(response.getCode()).isEqualTo(200);
            assertThat(response.getData()).isEqualTo("data");
            assertThat(response.getMessage()).isNull();
            assertThat(response.getMeta()).isNull();
            assertThat(response.getError()).isNull();
        }
    }
}
