package com.mosquito.project.dto;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.Validation;
import jakarta.validation.Validator;
import jakarta.validation.ValidatorFactory;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.junit.jupiter.params.provider.ValueSource;

import java.util.Set;

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

/**
 * UseApiKeyRequest DTO测试
 */
@DisplayName("UseApiKeyRequest DTO测试")
class UseApiKeyRequestTest {

    private Validator validator;
    private UseApiKeyRequest request;
    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        validator = factory.getValidator();
        request = new UseApiKeyRequest();
        objectMapper = new ObjectMapper();
    }

    @Nested
    @DisplayName("验证测试")
    class ValidationTests {

        @Test
        @DisplayName("有效的API密钥应该通过验证")
        void shouldPassValidation_WhenValidApiKey() {
            // Given
            request.setApiKey("valid-api-key-123");

            // When
            Set<ConstraintViolation<UseApiKeyRequest>> violations = validator.validate(request);

            // Then
            assertThat(violations).isEmpty();
        }

        @ParameterizedTest
        @NullAndEmptySource
        @ValueSource(strings = {"   ", "\t", "\n", "\r"})
        @DisplayName("无效API密钥应该失败验证")
        void shouldFailValidation_WhenInvalidApiKey(String apiKey) {
            // Given
            request.setApiKey(apiKey);

            // When
            Set<ConstraintViolation<UseApiKeyRequest>> violations = validator.validate(request);

            // Then
            assertThat(violations).isNotEmpty();
            assertThat(violations).hasSize(1);
            
            ConstraintViolation<UseApiKeyRequest> violation = violations.iterator().next();
            assertThat(violation.getPropertyPath().toString()).isEqualTo("apiKey");
            assertThat(violation.getMessage()).isEqualTo("API密钥不能为空");
        }

        @Test
        @DisplayName("包含空格的API密钥应该通过验证")
        void shouldPassValidation_WhenApiKeyContainsWhitespace() {
            // Given
            request.setApiKey("key with spaces");

            // When
            Set<ConstraintViolation<UseApiKeyRequest>> violations = validator.validate(request);

            // Then
            assertThat(violations).isEmpty();
        }

        @Test
        @DisplayName("特殊字符API密钥应该通过验证")
        void shouldPassValidation_WhenApiKeyContainsSpecialChars() {
            // Given
            request.setApiKey("key-!@#$%^&*()_+");

            // When
            Set<ConstraintViolation<UseApiKeyRequest>> violations = validator.validate(request);

            // Then
            assertThat(violations).isEmpty();
        }

        @Test
        @DisplayName("长API密钥应该通过验证")
        void shouldPassValidation_WhenApiKeyIsLong() {
            // Given
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 1000; i++) {
                sb.append("k");
            }
            request.setApiKey(sb.toString());

            // When
            Set<ConstraintViolation<UseApiKeyRequest>> violations = validator.validate(request);

            // Then
            assertThat(violations).isEmpty();
        }

        @Test
        @DisplayName("Unicode字符API密钥应该通过验证")
        void shouldPassValidation_WhenApiKeyContainsUnicode() {
            // Given
            request.setApiKey("密钥-中文-🔑");

            // When
            Set<ConstraintViolation<UseApiKeyRequest>> violations = validator.validate(request);

            // Then
            assertThat(violations).isEmpty();
        }
    }

    @Nested
    @DisplayName("Getter和Setter测试")
    class GetterSetterTests {

        @Test
        @DisplayName("apiKey字段的getter和setter应该正常工作")
        void shouldWorkCorrectly_ApiKeyGetterSetter() {
            // Given
            String apiKey = "test-api-key";

            // When
            request.setApiKey(apiKey);

            // Then
            assertThat(request.getApiKey()).isEqualTo(apiKey);
        }

        @Test
        @DisplayName("多次设置apiKey应该正确更新")
        void shouldUpdateCorrectly_WhenSettingApiKeyMultipleTimes() {
            // Given
            request.setApiKey("key-1");
            assertThat(request.getApiKey()).isEqualTo("key-1");

            // When
            request.setApiKey("key-2");

            // Then
            assertThat(request.getApiKey()).isEqualTo("key-2");
        }

        @Test
        @DisplayName("设置null值应该正确处理")
        void shouldHandleNullValues_WhenSettingFields() {
            // Given
            request.setApiKey("test-key");
            assertThat(request.getApiKey()).isNotNull();

            // When
            request.setApiKey(null);

            // Then
            assertThat(request.getApiKey()).isNull();
        }

        @Test
        @DisplayName("设置空字符串应该正确处理")
        void shouldHandleEmptyString_WhenSettingFields() {
            // Given
            request.setApiKey("test-key");

            // When
            request.setApiKey("");

            // Then
            assertThat(request.getApiKey()).isEmpty();
        }
    }

    @Nested
    @DisplayName("边界值测试")
    class BoundaryTests {

        @ParameterizedTest
        @ValueSource(strings = {
            "a",  // 单字符
            "AB", // 双字符
            "0123456789", // 数字
            "key-with-dashes", // 带横线
            "key_with_underscores", // 带下划线
            "key.with.dots", // 带点
            "key:with:colons", // 带冒号
            "key/with/slashes", // 带斜杠
            "key+with+plus", // 带加号
            "key=with=equals", // 带等号
            "key?with?question", // 带问号
            "key&with&ampersand", // 带&
        })
        @DisplayName("各种格式API密钥应该正确处理")
        void shouldHandleVariousFormats(String apiKey) {
            // When
            request.setApiKey(apiKey);

            // Then
            assertThat(request.getApiKey()).isEqualTo(apiKey);
        }

        @Test
        @DisplayName("Unicode字符API密钥应该正确处理")
        void shouldHandleUnicodeCharacters() {
            // Given
            String[] unicodeKeys = {
                "密钥-中文测试",
                "ключ-русский",
                "キー-日本語",
                "🔑-emoji-test",
                "مفتاح-عربي",
                "🔐🔑🛡️-多emoji"
            };

            for (String key : unicodeKeys) {
                // When
                request.setApiKey(key);

                // Then
                assertThat(request.getApiKey()).isEqualTo(key);
            }
        }

        @Test
        @DisplayName("极大长度API密钥应该正确处理")
        void shouldHandleExtremelyLongKey() {
            // Given
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 10000; i++) {
                sb.append("A");
            }
            String extremelyLongKey = sb.toString();

            // When
            request.setApiKey(extremelyLongKey);

            // Then
            assertThat(request.getApiKey()).hasSize(10000);
            assertThat(request.getApiKey()).isEqualTo(extremelyLongKey);
        }

        @Test
        @DisplayName("JSON特殊字符API密钥应该正确处理")
        void shouldHandleJsonSpecialCharacters() {
            // Given
            String jsonSpecialKey = "key{with}[brackets]\"quotes\"'apostrophe'";

            // When
            request.setApiKey(jsonSpecialKey);

            // Then
            assertThat(request.getApiKey()).isEqualTo(jsonSpecialKey);
        }

        @Test
        @DisplayName("包含换行符API密钥应该正确处理")
        void shouldHandleNewlines() {
            // Given
            String keyWithNewlines = "line1\nline2\r\nline3\t";

            // When
            request.setApiKey(keyWithNewlines);

            // Then
            assertThat(request.getApiKey()).isEqualTo(keyWithNewlines);
        }

        @Test
        @DisplayName("空白字符组合API密钥应该正确处理")
        void shouldHandleWhitespaceCombinations() {
            // Given
            String[] whitespaceKeys = {
                "key with spaces",
                "key\twith\ttabs",
                "  leading-spaces",
                "trailing-spaces  ",
                "  both-spaces  "
            };

            for (String key : whitespaceKeys) {
                // When
                request.setApiKey(key);

                // Then
                assertThat(request.getApiKey()).isEqualTo(key);
            }
        }
    }

    @Nested
    @DisplayName("JSON序列化测试")
    class JsonSerializationTests {

        @Test
        @DisplayName("完整对象应该正确序列化为JSON")
        void shouldSerializeCorrectly_CompleteObject() throws JsonProcessingException {
            // Given
            request.setApiKey("test-api-key-123");

            // When
            String json = objectMapper.writeValueAsString(request);

            // Then
            assertThat(json).isNotNull();
            assertThat(json).contains("\"apiKey\":\"test-api-key-123\"");
        }

        @Test
        @DisplayName("null值应该正确序列化为JSON")
        void shouldSerializeCorrectly_WithNullValue() throws JsonProcessingException {
            // Given
            request.setApiKey(null);

            // When
            String json = objectMapper.writeValueAsString(request);

            // Then
            assertThat(json).isNotNull();
            assertThat(json).contains("\"apiKey\":null");
        }

        @Test
        @DisplayName("空字符串应该正确序列化为JSON")
        void shouldSerializeCorrectly_WithEmptyString() throws JsonProcessingException {
            // Given
            request.setApiKey("");

            // When
            String json = objectMapper.writeValueAsString(request);

            // Then
            assertThat(json).contains("\"apiKey\":\"\"");
        }

        @Test
        @DisplayName("特殊字符应该正确序列化为JSON")
        void shouldSerializeCorrectly_WithSpecialCharacters() throws JsonProcessingException {
            // Given
            request.setApiKey("key-🔑-测试");

            // When
            String json = objectMapper.writeValueAsString(request);

            // Then
            assertThat(json).isNotNull();
            // 验证反序列化后值相同
            UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class);
            assertThat(deserialized.getApiKey()).isEqualTo("key-🔑-测试");
        }

        @Test
        @DisplayName("JSON转义字符应该正确序列化")
        void shouldSerializeCorrectly_WithJsonEscapes() throws JsonProcessingException {
            // Given
            String keyWithEscapes = "line1\nline2\t\"quoted\"";
            request.setApiKey(keyWithEscapes);

            // When
            String json = objectMapper.writeValueAsString(request);

            // Then
            assertThat(json).isNotNull();
            // 验证反序列化后值相同
            UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class);
            assertThat(deserialized.getApiKey()).isEqualTo(keyWithEscapes);
        }
    }

    @Nested
    @DisplayName("JSON反序列化测试")
    class JsonDeserializationTests {

        @Test
        @DisplayName("完整JSON应该正确反序列化")
        void shouldDeserializeCorrectly_CompleteJson() throws JsonProcessingException {
            // Given
            String json = "{\"apiKey\":\"test-api-key-12345\"}";

            // When
            UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class);

            // Then
            assertThat(deserialized.getApiKey()).isEqualTo("test-api-key-12345");
        }

        @Test
        @DisplayName("null值JSON应该正确反序列化")
        void shouldDeserializeCorrectly_WithNullValue() throws JsonProcessingException {
            // Given
            String json = "{\"apiKey\":null}";

            // When
            UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class);

            // Then
            assertThat(deserialized.getApiKey()).isNull();
        }

        @Test
        @DisplayName("空字符串JSON应该正确反序列化")
        void shouldDeserializeCorrectly_WithEmptyString() throws JsonProcessingException {
            // Given
            String json = "{\"apiKey\":\"\"}";

            // When
            UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class);

            // Then
            assertThat(deserialized.getApiKey()).isEmpty();
        }

        @Test
        @DisplayName("空对象JSON应该正确反序列化")
        void shouldDeserializeCorrectly_EmptyObjectJson() throws JsonProcessingException {
            // Given
            String json = "{}";

            // When
            UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class);

            // Then
            assertThat(deserialized.getApiKey()).isNull();
        }

        @Test
        @DisplayName("特殊字符JSON应该正确反序列化")
        void shouldDeserializeCorrectly_WithSpecialCharacters() throws JsonProcessingException {
            // Given
            String json = "{\"apiKey\":\"key-🔑-测试\"}";

            // When
            UseApiKeyRequest deserialized = objectMapper.readValue(json, UseApiKeyRequest.class);

            // Then
            assertThat(deserialized.getApiKey()).isEqualTo("key-🔑-测试");
        }

        @Test
        @DisplayName("JSON格式错误应该抛出异常")
        void shouldThrowException_WhenJsonIsMalformed() {
            // Given
            String malformedJson = "{\"apiKey\":\"test\""; // 缺少闭合括号

            // When & Then
            assertThatThrownBy(() -> objectMapper.readValue(malformedJson, UseApiKeyRequest.class))
                .isInstanceOf(JsonProcessingException.class);
        }
    }

    @Nested
    @DisplayName("对象行为测试")
    class ObjectBehaviorTests {

        @Test
        @DisplayName("两个相同apiKey的请求应该相等")
        void shouldBeEqual_WhenSameApiKey() {
            // Given
            UseApiKeyRequest request1 = new UseApiKeyRequest();
            UseApiKeyRequest request2 = new UseApiKeyRequest();
            request1.setApiKey("same-key");
            request2.setApiKey("same-key");

            // Then
            assertThat(request1.getApiKey()).isEqualTo(request2.getApiKey());
        }

        @Test
        @DisplayName("两个不同apiKey的请求应该不相等")
        void shouldNotBeEqual_WhenDifferentApiKey() {
            // Given
            UseApiKeyRequest request1 = new UseApiKeyRequest();
            UseApiKeyRequest request2 = new UseApiKeyRequest();
            request1.setApiKey("key-1");
            request2.setApiKey("key-2");

            // Then
            assertThat(request1.getApiKey()).isNotEqualTo(request2.getApiKey());
        }

        @Test
        @DisplayName("多次调用getter应该返回相同值")
        void shouldReturnSameValue_WhenCallingGetterMultipleTimes() {
            // Given
            request.setApiKey("consistent-key");

            // When & Then
            assertThat(request.getApiKey()).isEqualTo("consistent-key");
            assertThat(request.getApiKey()).isEqualTo("consistent-key");
            assertThat(request.getApiKey()).isEqualTo("consistent-key");
        }
    }

    @Nested
    @DisplayName("并发安全测试")
    class ConcurrencyTests {

        @Test
        @DisplayName("多线程并发操作应该是安全的")
        void shouldBeThreadSafe_ConcurrentOperations() throws InterruptedException {
            // Given
            int threadCount = 10;
            Thread[] threads = new Thread[threadCount];
            boolean[] results = new boolean[threadCount];

            // When
            for (int i = 0; i < threadCount; i++) {
                final int threadIndex = i;
                threads[i] = new Thread(() -> {
                    try {
                        UseApiKeyRequest localRequest = new UseApiKeyRequest();
                        localRequest.setApiKey("key-" + threadIndex);
                        
                        // 验证getter
                        assertThat(localRequest.getApiKey()).isEqualTo("key-" + threadIndex);
                        
                        // 验证验证器
                        Set<ConstraintViolation<UseApiKeyRequest>> violations = validator.validate(localRequest);
                        assertThat(violations).isEmpty();
                        
                        results[threadIndex] = true;
                    } catch (Exception e) {
                        results[threadIndex] = false;
                    }
                });
            }

            // 启动所有线程
            for (Thread thread : threads) {
                thread.start();
            }

            // 等待所有线程完成
            for (Thread thread : threads) {
                thread.join();
            }

            // Then
            for (int i = 0; i < threadCount; i++) {
                assertThat(results[i]).isTrue();
            }
        }
    }
}
