package com.mosquito.project.dto;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;

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

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

@DisplayName("ApiResponse 完整测试")
class ApiResponseCompleteTest {

    @Test
    @DisplayName("error(int, String, Object) 应该创建带details的错误响应")
    void shouldCreateErrorResponseWithDetails_whenUsingThreeParamError() {
        // Given
        int code = 400;
        String message = "Validation failed";
        Map<String, String> details = new HashMap<>();
        details.put("field1", "error1");
        details.put("field2", "error2");

        // When
        ApiResponse<Object> response = ApiResponse.error(code, message, details);

        // Then
        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.getError().getDetails()).isEqualTo(details);
        assertThat(response.getTimestamp()).isNotNull();
        assertThat(response.getTraceId()).isNull();
    }

    @Test
    @DisplayName("error(int, String, Object, String) 应该创建带traceId的错误响应")
    void shouldCreateErrorResponseWithTraceId_whenUsingFourParamError() {
        // Given
        int code = 500;
        String message = "Internal server error";
        Object details = "Detailed error information";
        String traceId = "trace-12345-abc";

        // When
        ApiResponse<Object> response = ApiResponse.error(code, message, details, traceId);

        // Then
        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);
        assertThat(response.getTimestamp()).isNotNull();
    }

    @Test
    @DisplayName("error(int, String, Object) 应该在details为null时正常工作")
    void shouldHandleNullDetails_whenUsingThreeParamError() {
        // Given
        int code = 404;
        String message = "Not found";

        // When
        ApiResponse<Object> response = ApiResponse.error(code, message, null);

        // Then
        assertThat(response.getCode()).isEqualTo(code);
        assertThat(response.getError().getDetails()).isNull();
    }

    @Test
    @DisplayName("error(int, String, Object, String) 应该在traceId为null时正常工作")
    void shouldHandleNullTraceId_whenUsingFourParamError() {
        // When
        ApiResponse<Object> response = ApiResponse.error(500, "Error", "details", null);

        // Then
        assertThat(response.getTraceId()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "0, 10, 0, 0, false, false",      // 空数据
        "0, 10, 100, 10, true, false",    // 第一页，有下一页
        "9, 10, 100, 10, false, true",    // 最后一页，有上一页
        "5, 10, 100, 10, true, true",     // 中间页，双向都有
        "0, 10, 5, 1, false, false"       // 数据少于每页大小
    })
    @DisplayName("createPagination 应该正确计算分页边界")
    void shouldCalculatePaginationCorrectly_whenUsingVariousInputs(
            int page, int size, long total, int expectedTotalPages, 
            boolean expectedHasNext, boolean expectedHasPrevious) {
        // When
        ApiResponse.Meta meta = ApiResponse.Meta.createPagination(page, size, total);

        // Then
        assertThat(meta).isNotNull();
        assertThat(meta.getPagination()).isNotNull();
        assertThat(meta.getPagination().getPage()).isEqualTo(page);
        assertThat(meta.getPagination().getSize()).isEqualTo(size);
        assertThat(meta.getPagination().getTotal()).isEqualTo(total);
        assertThat(meta.getPagination().getTotalPages()).isEqualTo(expectedTotalPages);
        assertThat(meta.getPagination().isHasNext()).isEqualTo(expectedHasNext);
        assertThat(meta.getPagination().isHasPrevious()).isEqualTo(expectedHasPrevious);
    }

    @Test
    @DisplayName("PaginationMeta.of 应该在size为0时处理除零情况")
    void shouldHandleZeroSize_whenCreatingPagination() {
        // When - size为0会导致除以0，但Math.ceil会处理
        ApiResponse.PaginationMeta pagination = ApiResponse.PaginationMeta.of(0, 0, 100);

        // Then - 实际上size为0会导致Infinity，需要验证边界行为
        assertThat(pagination).isNotNull();
    }

    @Test
    @DisplayName("PaginationMeta 应该在第一页时hasPrevious为false")
    void shouldHaveNoPrevious_whenOnFirstPage() {
        // When
        ApiResponse.PaginationMeta pagination = ApiResponse.PaginationMeta.of(0, 10, 50);

        // Then
        assertThat(pagination.isHasPrevious()).isFalse();
        assertThat(pagination.isHasNext()).isTrue();
    }

    @Test
    @DisplayName("PaginationMeta 应该在最后一页时hasNext为false")
    void shouldHaveNoNext_whenOnLastPage() {
        // Given - total=50, size=10, 共5页，最后一页是第4页(0-indexed)
        // When
        ApiResponse.PaginationMeta pagination = ApiResponse.PaginationMeta.of(4, 10, 50);

        // Then
        assertThat(pagination.isHasNext()).isFalse();
        assertThat(pagination.isHasPrevious()).isTrue();
    }

    @Test
    @DisplayName("PaginationMeta 应该在单页时hasNext和hasPrevious都为false")
    void shouldHaveNoNavigation_whenSinglePage() {
        // When
        ApiResponse.PaginationMeta pagination = ApiResponse.PaginationMeta.of(0, 10, 5);

        // Then
        assertThat(pagination.isHasNext()).isFalse();
        assertThat(pagination.isHasPrevious()).isFalse();
    }

    @Test
    @DisplayName("Error 单参数构造函数应该创建Error对象")
    void shouldCreateErrorWithMessage_whenUsingSingleParamConstructor() {
        // Given
        String message = "Error message";

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

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

    @Test
    @DisplayName("Error 双参数构造函数应该创建带details的Error对象")
    void shouldCreateErrorWithDetails_whenUsingTwoParamConstructor() {
        // Given
        String message = "Error message";
        Object details = Map.of("key", "value");

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

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

    @Test
    @DisplayName("Error 三参数构造函数应该创建完整的Error对象")
    void shouldCreateCompleteError_whenUsingThreeParamConstructor() {
        // Given
        String message = "Error message";
        Object details = Map.of("field", "error");
        String code = "ERR_001";

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

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

    @Test
    @DisplayName("Error 无参构造函数应该创建空Error对象")
    void shouldCreateEmptyError_whenUsingNoArgConstructor() {
        // When
        ApiResponse.Error error = new ApiResponse.Error();

        // Then
        assertThat(error.getMessage()).isNull();
        assertThat(error.getDetails()).isNull();
        assertThat(error.getCode()).isNull();
    }

    @Test
    @DisplayName("Error setter方法应该正常工作")
    void shouldSetErrorProperties_whenUsingSetters() {
        // Given
        ApiResponse.Error error = new ApiResponse.Error();

        // When
        error.setMessage("New message");
        error.setDetails("New details");
        error.setCode("NEW_CODE");

        // Then
        assertThat(error.getMessage()).isEqualTo("New message");
        assertThat(error.getDetails()).isEqualTo("New details");
        assertThat(error.getCode()).isEqualTo("NEW_CODE");
    }

    @Test
    @DisplayName("Meta 无参构造函数应该创建空Meta对象")
    void shouldCreateEmptyMeta_whenUsingNoArgConstructor() {
        // When
        ApiResponse.Meta meta = new ApiResponse.Meta();

        // Then
        assertThat(meta.getPagination()).isNull();
        assertThat(meta.getExtra()).isNull();
    }

    @Test
    @DisplayName("Meta 全参构造函数应该创建完整的Meta对象")
    void shouldCreateCompleteMeta_whenUsingAllArgsConstructor() {
        // Given
        ApiResponse.PaginationMeta pagination = ApiResponse.PaginationMeta.of(0, 10, 100);
        Map<String, Object> extra = Map.of("key", "value");

        // When
        ApiResponse.Meta meta = new ApiResponse.Meta(pagination, extra);

        // Then
        assertThat(meta.getPagination()).isEqualTo(pagination);
        assertThat(meta.getExtra()).isEqualTo(extra);
    }

    @Test
    @DisplayName("Meta setter方法应该正常工作")
    void shouldSetMetaProperties_whenUsingSetters() {
        // Given
        ApiResponse.Meta meta = new ApiResponse.Meta();
        ApiResponse.PaginationMeta pagination = ApiResponse.PaginationMeta.of(1, 20, 200);
        Map<String, Object> extra = new HashMap<>();
        extra.put("custom", "data");

        // When
        meta.setPagination(pagination);
        meta.setExtra(extra);

        // Then
        assertThat(meta.getPagination()).isEqualTo(pagination);
        assertThat(meta.getExtra()).isEqualTo(extra);
    }

    @Test
    @DisplayName("PaginationMeta 无参构造函数应该创建空对象")
    void shouldCreateEmptyPaginationMeta_whenUsingNoArgConstructor() {
        // When
        ApiResponse.PaginationMeta pagination = new ApiResponse.PaginationMeta();

        // Then
        assertThat(pagination.getPage()).isEqualTo(0);
        assertThat(pagination.getSize()).isEqualTo(0);
        assertThat(pagination.getTotal()).isEqualTo(0);
        assertThat(pagination.getTotalPages()).isEqualTo(0);
        assertThat(pagination.isHasNext()).isFalse();
        assertThat(pagination.isHasPrevious()).isFalse();
    }

    @Test
    @DisplayName("PaginationMeta 全参构造函数应该创建完整对象")
    void shouldCreateCompletePaginationMeta_whenUsingAllArgsConstructor() {
        // When
        ApiResponse.PaginationMeta pagination = new ApiResponse.PaginationMeta(2, 15, 150, 10, true, true);

        // Then
        assertThat(pagination.getPage()).isEqualTo(2);
        assertThat(pagination.getSize()).isEqualTo(15);
        assertThat(pagination.getTotal()).isEqualTo(150);
        assertThat(pagination.getTotalPages()).isEqualTo(10);
        assertThat(pagination.isHasNext()).isTrue();
        assertThat(pagination.isHasPrevious()).isTrue();
    }

    @Test
    @DisplayName("PaginationMeta setter方法应该正常工作")
    void shouldSetPaginationProperties_whenUsingSetters() {
        // Given
        ApiResponse.PaginationMeta pagination = new ApiResponse.PaginationMeta();

        // When
        pagination.setPage(3);
        pagination.setSize(25);
        pagination.setTotal(250);
        pagination.setTotalPages(10);
        pagination.setHasNext(false);
        pagination.setHasPrevious(true);

        // Then
        assertThat(pagination.getPage()).isEqualTo(3);
        assertThat(pagination.getSize()).isEqualTo(25);
        assertThat(pagination.getTotal()).isEqualTo(250);
        assertThat(pagination.getTotalPages()).isEqualTo(10);
        assertThat(pagination.isHasNext()).isFalse();
        assertThat(pagination.isHasPrevious()).isTrue();
    }

    @Test
    @DisplayName("error(int, String) 应该创建基本错误响应")
    void shouldCreateBasicErrorResponse_whenUsingTwoParamError() {
        // Given
        int code = 403;
        String message = "Forbidden";

        // When
        ApiResponse<Object> response = ApiResponse.error(code, message);

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

    @ParameterizedTest
    @ValueSource(longs = {0, 1, 99, 100, 101, 1000, Long.MAX_VALUE})
    @DisplayName("PaginationMeta 应该正确处理各种total值")
    void shouldHandleVariousTotalValues_whenCreatingPagination(long total) {
        // When
        ApiResponse.PaginationMeta pagination = ApiResponse.PaginationMeta.of(0, 10, total);

        // Then
        assertThat(pagination).isNotNull();
        assertThat(pagination.getTotal()).isEqualTo(total);
        assertThat(pagination.getTotalPages()).isGreaterThanOrEqualTo(0);
    }

    @Test
    @DisplayName("ApiResponse Builder应该创建完整响应")
    void shouldBuildCompleteResponse_whenUsingBuilder() {
        // Given
        LocalDateTime timestamp = LocalDateTime.now();
        ApiResponse.Error error = new ApiResponse.Error("Test error");
        ApiResponse.Meta meta = ApiResponse.Meta.createPagination(0, 10, 100);

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

        // Then
        assertThat(response.getCode()).isEqualTo(200);
        assertThat(response.getMessage()).isEqualTo("Success");
        assertThat(response.getData()).isEqualTo("Test data");
        assertThat(response.getMeta()).isEqualTo(meta);
        assertThat(response.getError()).isEqualTo(error);
        assertThat(response.getTimestamp()).isEqualTo(timestamp);
        assertThat(response.getTraceId()).isEqualTo("test-trace");
    }

    @Test
    @DisplayName("ApiResponse 无参构造函数应该创建空对象")
    void shouldCreateEmptyResponse_whenUsingNoArgConstructor() {
        // When
        ApiResponse<Object> response = new ApiResponse<>();

        // Then
        assertThat(response.getCode()).isEqualTo(0);
        assertThat(response.getMessage()).isNull();
        assertThat(response.getData()).isNull();
        assertThat(response.getMeta()).isNull();
        assertThat(response.getError()).isNull();
        assertThat(response.getTimestamp()).isNull();
        assertThat(response.getTraceId()).isNull();
    }

    @Test
    @DisplayName("ApiResponse setter方法应该正常工作")
    void shouldSetResponseProperties_whenUsingSetters() {
        // Given
        ApiResponse<String> response = new ApiResponse<>();
        LocalDateTime timestamp = LocalDateTime.now();
        ApiResponse.Error error = new ApiResponse.Error("Error");
        ApiResponse.Meta meta = ApiResponse.Meta.createPagination(1, 20, 200);

        // When
        response.setCode(201);
        response.setMessage("Created");
        response.setData("Data");
        response.setMeta(meta);
        response.setError(error);
        response.setTimestamp(timestamp);
        response.setTraceId("trace-123");

        // Then
        assertThat(response.getCode()).isEqualTo(201);
        assertThat(response.getMessage()).isEqualTo("Created");
        assertThat(response.getData()).isEqualTo("Data");
        assertThat(response.getMeta()).isEqualTo(meta);
        assertThat(response.getError()).isEqualTo(error);
        assertThat(response.getTimestamp()).isEqualTo(timestamp);
        assertThat(response.getTraceId()).isEqualTo("trace-123");
    }

    @Test
    @DisplayName("success(T data, String message) 应该创建自定义消息的成功响应")
    void shouldCreateSuccessWithCustomMessage_whenUsingTwoParamSuccess() {
        // Given
        String data = "Test data";
        String customMessage = "Custom success message";

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

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

    @Test
    @DisplayName("paginated 应该创建带分页元数据的成功响应")
    void shouldCreatePaginatedResponse_whenUsingPaginatedMethod() {
        // Given
        String data = "Page data";
        int page = 2;
        int size = 20;
        long total = 150;

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

        // Then
        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);
        assertThat(response.getTimestamp()).isNotNull();
    }
}
