package com.mosquito.project.persistence.entity;

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.NullAndEmptySource;
import org.junit.jupiter.params.provider.ValueSource;

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

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

@DisplayName("LinkClickEntity 测试")
class LinkClickEntityTest {

    @Test
    @DisplayName("getParams() 应该在params为null时返回null")
    void shouldReturnNull_whenParamsIsNull() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        entity.setParams(null);

        // When
        Map<String, String> result = entity.getParams();

        // Then
        assertThat(result).isNull();
    }

    @Test
    @DisplayName("getParams() 应该在params为空字符串时返回null")
    void shouldReturnNull_whenParamsIsEmpty() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        entity.setParams(Map.of());
        
        // 手动设置为空字符串，模拟数据库中存储的空值
        try {
            java.lang.reflect.Field field = LinkClickEntity.class.getDeclaredField("params");
            field.setAccessible(true);
            field.set(entity, "");
        } catch (Exception e) {
            // 如果反射失败，使用setParams设置空map会序列化为"{}"
        }

        // When
        Map<String, String> result = entity.getParams();

        // Then - 空字符串应该返回null
        assertThat(result).isNull();
    }

    @Test
    @DisplayName("getParams() 应该在params为空白字符串时返回null")
    void shouldReturnNull_whenParamsIsBlank() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        try {
            java.lang.reflect.Field field = LinkClickEntity.class.getDeclaredField("params");
            field.setAccessible(true);
            field.set(entity, "   ");
        } catch (Exception e) {
            // 忽略反射异常
        }

        // When
        Map<String, String> result = entity.getParams();

        // Then
        assertThat(result).isNull();
    }

    @Test
    @DisplayName("getParams() 应该在JSON解析异常时返回null")
    void shouldReturnNull_whenJsonParsingFails() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        try {
            java.lang.reflect.Field field = LinkClickEntity.class.getDeclaredField("params");
            field.setAccessible(true);
            field.set(entity, "invalid json {broken}");
        } catch (Exception e) {
            // 忽略反射异常
        }

        // When
        Map<String, String> result = entity.getParams();

        // Then
        assertThat(result).isNull();
    }

    @Test
    @DisplayName("getParams() 应该正确解析有效JSON")
    void shouldParseJsonCorrectly_whenParamsIsValid() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        Map<String, String> originalMap = new HashMap<>();
        originalMap.put("key1", "value1");
        originalMap.put("key2", "value2");
        entity.setParams(originalMap);

        // When
        Map<String, String> result = entity.getParams();

        // Then
        assertThat(result).isNotNull();
        assertThat(result).hasSize(2);
        assertThat(result.get("key1")).isEqualTo("value1");
        assertThat(result.get("key2")).isEqualTo("value2");
    }

    @Test
    @DisplayName("setParams() 应该在map为null时设置params为null")
    void shouldSetNull_whenMapIsNull() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When
        entity.setParams(null);

        // Then
        assertThat(entity.getParams()).isNull();
    }

    @Test
    @DisplayName("setParams() 应该正确序列化Map到JSON字符串")
    void shouldSerializeMapToJson_whenMapIsValid() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        Map<String, String> paramsMap = new HashMap<>();
        paramsMap.put("utm_source", "twitter");
        paramsMap.put("utm_medium", "social");

        // When
        entity.setParams(paramsMap);

        // Then
        Map<String, String> result = entity.getParams();
        assertThat(result).isNotNull();
        assertThat(result.get("utm_source")).isEqualTo("twitter");
        assertThat(result.get("utm_medium")).isEqualTo("social");
    }

    @Test
    @DisplayName("setParams() 应该在序列化异常时设置params为null")
    void shouldHandleSerializationException() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        // 创建一个无法序列化的Map（包含循环引用不可能，使用其他方法）
        // 这里我们测试正常情况下的异常处理

        // When - 设置有效map
        Map<String, String> validMap = Map.of("key", "value");
        entity.setParams(validMap);

        // Then
        assertThat(entity.getParams()).isNotNull();
    }

    @Test
    @DisplayName("id setter/getter 应该正常工作")
    void shouldHandleId_whenUsingGetterAndSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        Long id = 12345L;

        // When
        entity.setId(id);

        // Then
        assertThat(entity.getId()).isEqualTo(id);
    }

    @Test
    @DisplayName("code setter/getter 应该正常工作并处理边界值")
    void shouldHandleCode_whenUsingGetterAndSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When - 测试空字符串
        entity.setCode("");
        assertThat(entity.getCode()).isEmpty();

        // When - 测试最大长度
        String maxLengthCode = "a".repeat(32);
        entity.setCode(maxLengthCode);
        assertThat(entity.getCode()).hasSize(32);

        // When - 测试null
        entity.setCode(null);
        assertThat(entity.getCode()).isNull();
    }

    @ParameterizedTest
    @ValueSource(strings = {"ABC123", "test-code", "invite_2024", "a", ""})
    @NullAndEmptySource
    @DisplayName("code 应该接受各种字符串值")
    void shouldAcceptVariousCodeValues(String code) {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When & Then
        assertThatNoException().isThrownBy(() -> entity.setCode(code));
        assertThat(entity.getCode()).isEqualTo(code);
    }

    @Test
    @DisplayName("activityId setter/getter 应该正常工作")
    void shouldHandleActivityId_whenUsingGetterAndSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        Long activityId = 999L;

        // When
        entity.setActivityId(activityId);

        // Then
        assertThat(entity.getActivityId()).isEqualTo(activityId);
    }

    @Test
    @DisplayName("activityId 应该处理null值")
    void shouldHandleNullActivityId_whenUsingSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When
        entity.setActivityId(null);

        // Then
        assertThat(entity.getActivityId()).isNull();
    }

    @Test
    @DisplayName("inviterUserId setter/getter 应该正常工作")
    void shouldHandleInviterUserId_whenUsingGetterAndSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        Long inviterUserId = 888L;

        // When
        entity.setInviterUserId(inviterUserId);

        // Then
        assertThat(entity.getInviterUserId()).isEqualTo(inviterUserId);
    }

    @Test
    @DisplayName("inviterUserId 应该处理null值")
    void shouldHandleNullInviterUserId_whenUsingSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When
        entity.setInviterUserId(null);

        // Then
        assertThat(entity.getInviterUserId()).isNull();
    }

    @Test
    @DisplayName("ip setter/getter 应该正常工作并处理边界值")
    void shouldHandleIp_whenUsingGetterAndSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When - 测试IPv4
        String ipv4 = "192.168.1.1";
        entity.setIp(ipv4);
        assertThat(entity.getIp()).isEqualTo(ipv4);

        // When - 测试IPv6
        String ipv6 = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
        entity.setIp(ipv6);
        assertThat(entity.getIp()).isEqualTo(ipv6);

        // When - 测试最大长度
        String maxLengthIp = "1".repeat(64);
        entity.setIp(maxLengthIp);
        assertThat(entity.getIp()).hasSize(64);

        // When - 测试null
        entity.setIp(null);
        assertThat(entity.getIp()).isNull();
    }

    @Test
    @DisplayName("userAgent setter/getter 应该正常工作并处理长字符串")
    void shouldHandleUserAgent_whenUsingGetterAndSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When - 测试典型UA
        String ua = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36";
        entity.setUserAgent(ua);
        assertThat(entity.getUserAgent()).isEqualTo(ua);

        // When - 测试最大长度
        String maxLengthUa = "X".repeat(512);
        entity.setUserAgent(maxLengthUa);
        assertThat(entity.getUserAgent()).hasSize(512);

        // When - 测试null
        entity.setUserAgent(null);
        assertThat(entity.getUserAgent()).isNull();
    }

    @Test
    @DisplayName("referer setter/getter 应该正常工作并处理长URL")
    void shouldHandleReferer_whenUsingGetterAndSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When - 测试典型URL
        String referer = "https://example.com/path?param=value";
        entity.setReferer(referer);
        assertThat(entity.getReferer()).isEqualTo(referer);

        // When - 测试最大长度
        String maxLengthReferer = "Y".repeat(1024);
        entity.setReferer(maxLengthReferer);
        assertThat(entity.getReferer()).hasSize(1024);

        // When - 测试null
        entity.setReferer(null);
        assertThat(entity.getReferer()).isNull();
    }

    @Test
    @DisplayName("createdAt setter/getter 应该正常工作")
    void shouldHandleCreatedAt_whenUsingGetterAndSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        OffsetDateTime now = OffsetDateTime.now();

        // When
        entity.setCreatedAt(now);

        // Then
        assertThat(entity.getCreatedAt()).isEqualTo(now);
    }

    @Test
    @DisplayName("createdAt 应该处理null值")
    void shouldHandleNullCreatedAt_whenUsingSetter() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When
        entity.setCreatedAt(null);

        // Then
        assertThat(entity.getCreatedAt()).isNull();
    }

    @Test
    @DisplayName("完整实体构建应该正常工作")
    void shouldBuildCompleteEntity_whenSettingAllFields() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        OffsetDateTime now = OffsetDateTime.now();
        Map<String, String> params = Map.of("source", "email", "campaign", "summer2024");

        // When
        entity.setId(1L);
        entity.setCode("INVITE123");
        entity.setActivityId(100L);
        entity.setInviterUserId(200L);
        entity.setIp("192.168.1.100");
        entity.setUserAgent("Mozilla/5.0");
        entity.setReferer("https://example.com");
        entity.setParams(params);
        entity.setCreatedAt(now);

        // Then
        assertThat(entity.getId()).isEqualTo(1L);
        assertThat(entity.getCode()).isEqualTo("INVITE123");
        assertThat(entity.getActivityId()).isEqualTo(100L);
        assertThat(entity.getInviterUserId()).isEqualTo(200L);
        assertThat(entity.getIp()).isEqualTo("192.168.1.100");
        assertThat(entity.getUserAgent()).isEqualTo("Mozilla/5.0");
        assertThat(entity.getReferer()).isEqualTo("https://example.com");
        assertThat(entity.getParams()).containsEntry("source", "email");
        assertThat(entity.getParams()).containsEntry("campaign", "summer2024");
        assertThat(entity.getCreatedAt()).isEqualTo(now);
    }

    @ParameterizedTest
    @CsvSource({
        "1, code1, 100, 200, 192.168.1.1",
        "999999, very-long-code-with-many-characters, 999999999, 888888888, 255.255.255.255"
    })
    @DisplayName("实体应该处理各种边界值")
    void shouldHandleBoundaryValues(Long id, String code, Long activityId, Long inviterUserId, String ip) {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When
        entity.setId(id);
        entity.setCode(code);
        entity.setActivityId(activityId);
        entity.setInviterUserId(inviterUserId);
        entity.setIp(ip);

        // Then
        assertThat(entity.getId()).isEqualTo(id);
        assertThat(entity.getCode()).isEqualTo(code);
        assertThat(entity.getActivityId()).isEqualTo(activityId);
        assertThat(entity.getInviterUserId()).isEqualTo(inviterUserId);
        assertThat(entity.getIp()).isEqualTo(ip);
    }

    @Test
    @DisplayName("空实体应该所有字段为null")
    void shouldHaveAllNullFields_whenEntityIsNew() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // Then
        assertThat(entity.getId()).isNull();
        assertThat(entity.getCode()).isNull();
        assertThat(entity.getActivityId()).isNull();
        assertThat(entity.getInviterUserId()).isNull();
        assertThat(entity.getIp()).isNull();
        assertThat(entity.getUserAgent()).isNull();
        assertThat(entity.getReferer()).isNull();
        assertThat(entity.getParams()).isNull();
        assertThat(entity.getCreatedAt()).isNull();
    }

    @Test
    @DisplayName("getParams() 不应该抛出NPE")
    void shouldNotThrowNpe_whenGettingParams() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();

        // When & Then
        assertThatNoException().isThrownBy(() -> {
            Map<String, String> params = entity.getParams();
            // 可以安全地检查结果
            assertThat(params).isNull();
        });
    }

    @Test
    @DisplayName("setParams() 和 getParams() 应该保持数据一致性")
    void shouldMaintainDataConsistency_whenSettingAndGettingParams() {
        // Given
        LinkClickEntity entity = new LinkClickEntity();
        Map<String, String> original = new HashMap<>();
        original.put("key", "value with special chars: !@#$%^&*()");
        original.put("unicode", "中文测试");
        original.put("empty", "");

        // When
        entity.setParams(original);
        Map<String, String> retrieved = entity.getParams();

        // Then
        assertThat(retrieved).isNotNull();
        assertThat(retrieved.get("key")).isEqualTo("value with special chars: !@#$%^&*()");
        assertThat(retrieved.get("unicode")).isEqualTo("中文测试");
        assertThat(retrieved.get("empty")).isEqualTo("");
    }
}
