package com.mosquito.project.dto;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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.ArrayList;
import java.util.Collections;
import java.util.List;

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

/**
 * ActivityStatsResponse DTO测试
 */
@DisplayName("ActivityStatsResponse DTO测试")
class ActivityStatsResponseTest {

    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        objectMapper = new ObjectMapper();
    }

    @Nested
    @DisplayName("构造函数测试")
    class ConstructorTests {

        @Test
        @DisplayName("全参数构造函数应该正确设置所有字段")
        void shouldSetAllFieldsCorrectly_WhenUsingAllArgsConstructor() {
            // Given
            long totalParticipants = 100L;
            long totalShares = 50L;
            List<ActivityStatsResponse.DailyStats> dailyStats = createDailyStats();

            // When
            ActivityStatsResponse response = new ActivityStatsResponse(totalParticipants, totalShares, dailyStats);

            // Then
            assertThat(response.getTotalParticipants()).isEqualTo(totalParticipants);
            assertThat(response.getTotalShares()).isEqualTo(totalShares);
            assertThat(response.getDailyStats()).isEqualTo(dailyStats);
        }

        @Test
        @DisplayName("空列表构造函数应该正确处理")
        void shouldHandleEmptyList_WhenUsingConstructor() {
            // Given
            long totalParticipants = 0L;
            long totalShares = 0L;
            List<ActivityStatsResponse.DailyStats> emptyStats = Collections.emptyList();

            // When
            ActivityStatsResponse response = new ActivityStatsResponse(totalParticipants, totalShares, emptyStats);

            // Then
            assertThat(response.getTotalParticipants()).isZero();
            assertThat(response.getTotalShares()).isZero();
            assertThat(response.getDailyStats()).isEmpty();
        }

        @Test
        @DisplayName("null列表构造函数应该正确处理")
        void shouldHandleNullList_WhenUsingConstructor() {
            // Given
            long totalParticipants = 10L;
            long totalShares = 5L;

            // When
            ActivityStatsResponse response = new ActivityStatsResponse(totalParticipants, totalShares, null);

            // Then
            assertThat(response.getTotalParticipants()).isEqualTo(totalParticipants);
            assertThat(response.getTotalShares()).isEqualTo(totalShares);
            assertThat(response.getDailyStats()).isNull();
        }

        @Test
        @DisplayName("边界值构造函数应该正确处理极大值")
        void shouldHandleMaxValues_WhenUsingConstructor() {
            // Given
            long totalParticipants = Long.MAX_VALUE;
            long totalShares = Long.MAX_VALUE;
            List<ActivityStatsResponse.DailyStats> dailyStats = Collections.emptyList();

            // When
            ActivityStatsResponse response = new ActivityStatsResponse(totalParticipants, totalShares, dailyStats);

            // Then
            assertThat(response.getTotalParticipants()).isEqualTo(Long.MAX_VALUE);
            assertThat(response.getTotalShares()).isEqualTo(Long.MAX_VALUE);
        }

        @Test
        @DisplayName("边界值构造函数应该正确处理负数")
        void shouldHandleNegativeValues_WhenUsingConstructor() {
            // Given
            long totalParticipants = -100L;
            long totalShares = -50L;
            List<ActivityStatsResponse.DailyStats> dailyStats = Collections.emptyList();

            // When
            ActivityStatsResponse response = new ActivityStatsResponse(totalParticipants, totalShares, dailyStats);

            // Then
            assertThat(response.getTotalParticipants()).isEqualTo(-100L);
            assertThat(response.getTotalShares()).isEqualTo(-50L);
        }

        @Test
        @DisplayName("边界值构造函数应该正确处理零值")
        void shouldHandleZeroValues_WhenUsingConstructor() {
            // Given
            long totalParticipants = 0L;
            long totalShares = 0L;
            List<ActivityStatsResponse.DailyStats> dailyStats = Collections.emptyList();

            // When
            ActivityStatsResponse response = new ActivityStatsResponse(totalParticipants, totalShares, dailyStats);

            // Then
            assertThat(response.getTotalParticipants()).isZero();
            assertThat(response.getTotalShares()).isZero();
        }
    }

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

        private ActivityStatsResponse response;

        @BeforeEach
        void setUp() {
            response = new ActivityStatsResponse(0L, 0L, Collections.emptyList());
        }

        @Test
        @DisplayName("totalParticipants字段的getter和setter应该正常工作")
        void shouldWorkCorrectly_TotalParticipantsGetterSetter() {
            // Given
            long value = 999L;

            // When
            response.setTotalParticipants(value);

            // Then
            assertThat(response.getTotalParticipants()).isEqualTo(value);
        }

        @Test
        @DisplayName("totalShares字段的getter和setter应该正常工作")
        void shouldWorkCorrectly_TotalSharesGetterSetter() {
            // Given
            long value = 888L;

            // When
            response.setTotalShares(value);

            // Then
            assertThat(response.getTotalShares()).isEqualTo(value);
        }

        @Test
        @DisplayName("dailyStats字段的getter和setter应该正常工作")
        void shouldWorkCorrectly_DailyStatsGetterSetter() {
            // Given
            List<ActivityStatsResponse.DailyStats> stats = createDailyStats();

            // When
            response.setDailyStats(stats);

            // Then
            assertThat(response.getDailyStats()).isEqualTo(stats);
        }

        @Test
        @DisplayName("多次设置totalParticipants应该正确更新")
        void shouldUpdateCorrectly_WhenSettingTotalParticipantsMultipleTimes() {
            // Given
            response.setTotalParticipants(100L);
            assertThat(response.getTotalParticipants()).isEqualTo(100L);

            // When
            response.setTotalParticipants(200L);

            // Then
            assertThat(response.getTotalParticipants()).isEqualTo(200L);
            assertThat(response.getTotalParticipants()).isNotEqualTo(100L);
        }

        @Test
        @DisplayName("多次设置totalShares应该正确更新")
        void shouldUpdateCorrectly_WhenSettingTotalSharesMultipleTimes() {
            // Given
            response.setTotalShares(50L);
            assertThat(response.getTotalShares()).isEqualTo(50L);

            // When
            response.setTotalShares(100L);

            // Then
            assertThat(response.getTotalShares()).isEqualTo(100L);
        }

        @Test
        @DisplayName("多次设置dailyStats应该正确更新")
        void shouldUpdateCorrectly_WhenSettingDailyStatsMultipleTimes() {
            // Given
            List<ActivityStatsResponse.DailyStats> stats1 = createDailyStats();
            response.setDailyStats(stats1);
            assertThat(response.getDailyStats()).isEqualTo(stats1);

            // When
            List<ActivityStatsResponse.DailyStats> stats2 = Collections.emptyList();
            response.setDailyStats(stats2);

            // Then
            assertThat(response.getDailyStats()).isEqualTo(stats2);
        }

        @Test
        @DisplayName("设置null值应该正确处理")
        void shouldHandleNullValues_WhenSettingFields() {
            // Given
            response.setDailyStats(createDailyStats());
            assertThat(response.getDailyStats()).isNotNull();

            // When
            response.setDailyStats(null);

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

    @Nested
    @DisplayName("DailyStats内部类测试")
    class DailyStatsTests {

        @Test
        @DisplayName("DailyStats构造函数应该正确设置所有字段")
        void shouldSetAllFieldsCorrectly_WhenUsingDailyStatsConstructor() {
            // Given
            String date = "2024-01-15";
            int participants = 50;
            int shares = 25;

            // When
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats(date, participants, shares);

            // Then
            assertThat(stats.getDate()).isEqualTo(date);
            assertThat(stats.getParticipants()).isEqualTo(participants);
            assertThat(stats.getShares()).isEqualTo(shares);
        }

        @Test
        @DisplayName("DailyStats getter和setter应该正常工作")
        void shouldWorkCorrectly_DailyStatsGetterSetter() {
            // Given
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats("2024-01-01", 0, 0);

            // When
            stats.setDate("2024-12-31");
            stats.setParticipants(100);
            stats.setShares(50);

            // Then
            assertThat(stats.getDate()).isEqualTo("2024-12-31");
            assertThat(stats.getParticipants()).isEqualTo(100);
            assertThat(stats.getShares()).isEqualTo(50);
        }

        @Test
        @DisplayName("DailyStats边界值应该正确处理极大值")
        void shouldHandleMaxValues_WhenUsingDailyStats() {
            // Given
            String date = "2099-12-31";
            int participants = Integer.MAX_VALUE;
            int shares = Integer.MAX_VALUE;

            // When
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats(date, participants, shares);

            // Then
            assertThat(stats.getParticipants()).isEqualTo(Integer.MAX_VALUE);
            assertThat(stats.getShares()).isEqualTo(Integer.MAX_VALUE);
        }

        @Test
        @DisplayName("DailyStats边界值应该正确处理负数")
        void shouldHandleNegativeValues_WhenUsingDailyStats() {
            // Given
            String date = "2024-01-01";
            int participants = -100;
            int shares = -50;

            // When
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats(date, participants, shares);

            // Then
            assertThat(stats.getParticipants()).isEqualTo(-100);
            assertThat(stats.getShares()).isEqualTo(-50);
        }

        @Test
        @DisplayName("DailyStats边界值应该正确处理零值")
        void shouldHandleZeroValues_WhenUsingDailyStats() {
            // Given
            String date = "2024-01-01";

            // When
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats(date, 0, 0);

            // Then
            assertThat(stats.getParticipants()).isZero();
            assertThat(stats.getShares()).isZero();
        }

        @ParameterizedTest
        @NullAndEmptySource
        @ValueSource(strings = {"   ", "\t", "\n"})
        @DisplayName("DailyStats应该处理各种空日期值")
        void shouldHandleVariousEmptyDates(String date) {
            // When
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats(date, 10, 5);

            // Then
            assertThat(stats.getDate()).isEqualTo(date);
        }

        @Test
        @DisplayName("DailyStats多次设置应该正确更新")
        void shouldUpdateCorrectly_WhenSettingDailyStatsMultipleTimes() {
            // Given
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats("2024-01-01", 10, 5);

            // When
            stats.setDate("2024-12-31");
            stats.setParticipants(100);
            stats.setShares(50);

            // Then
            assertThat(stats.getDate()).isEqualTo("2024-12-31");
            assertThat(stats.getParticipants()).isEqualTo(100);
            assertThat(stats.getShares()).isEqualTo(50);
        }

        @Test
        @DisplayName("DailyStats设置null日期应该正确处理")
        void shouldHandleNullDate_WhenSettingDailyStats() {
            // Given
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats("2024-01-01", 10, 5);

            // When
            stats.setDate(null);

            // Then
            assertThat(stats.getDate()).isNull();
        }
    }

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

        @Test
        @DisplayName("完整对象应该正确序列化为JSON")
        void shouldSerializeCorrectly_CompleteObject() throws JsonProcessingException {
            // Given
            List<ActivityStatsResponse.DailyStats> dailyStats = createDailyStats();
            ActivityStatsResponse response = new ActivityStatsResponse(100L, 50L, dailyStats);

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

            // Then
            assertThat(json).isNotNull();
            assertThat(json).contains("\"totalParticipants\":100");
            assertThat(json).contains("\"totalShares\":50");
            assertThat(json).contains("\"dailyStats\"");
        }

        @Test
        @DisplayName("空列表应该正确序列化为JSON")
        void shouldSerializeCorrectly_WithEmptyList() throws JsonProcessingException {
            // Given
            ActivityStatsResponse response = new ActivityStatsResponse(0L, 0L, Collections.emptyList());

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

            // Then
            assertThat(json).isNotNull();
            assertThat(json).contains("\"dailyStats\":[]");
        }

        @Test
        @DisplayName("null dailyStats应该正确序列化为JSON")
        void shouldSerializeCorrectly_WithNullDailyStats() throws JsonProcessingException {
            // Given
            ActivityStatsResponse response = new ActivityStatsResponse(10L, 5L, null);

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

            // Then
            assertThat(json).isNotNull();
            assertThat(json).contains("\"totalParticipants\":10");
            assertThat(json).contains("\"totalShares\":5");
        }

        @Test
        @DisplayName("包含DailyStats的对象应该正确序列化为JSON")
        void shouldSerializeCorrectly_WithDailyStats() throws JsonProcessingException {
            // Given
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats("2024-01-15", 50, 25);
            List<ActivityStatsResponse.DailyStats> dailyStats = List.of(stats);
            ActivityStatsResponse response = new ActivityStatsResponse(100L, 50L, dailyStats);

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

            // Then
            assertThat(json).contains("\"date\":\"2024-01-15\"");
            assertThat(json).contains("\"participants\":50");
            assertThat(json).contains("\"shares\":25");
        }

        @Test
        @DisplayName("边界值应该正确序列化为JSON")
        void shouldSerializeCorrectly_WithBoundaryValues() throws JsonProcessingException {
            // Given
            ActivityStatsResponse response = new ActivityStatsResponse(Long.MAX_VALUE, Long.MIN_VALUE, Collections.emptyList());

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

            // Then
            assertThat(json).contains("\"totalParticipants\":" + Long.MAX_VALUE);
            assertThat(json).contains("\"totalShares\":" + Long.MIN_VALUE);
        }
    }



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

        @Test
        @DisplayName("极大统计数据列表应该正确处理")
        void shouldHandleLargeStatsList() {
            // Given
            List<ActivityStatsResponse.DailyStats> largeStats = new ArrayList<>();
            for (int i = 0; i < 1000; i++) {
                largeStats.add(new ActivityStatsResponse.DailyStats("2024-01-" + (i % 30 + 1), i, i / 2));
            }

            // When
            ActivityStatsResponse response = new ActivityStatsResponse(1000L, 500L, largeStats);

            // Then
            assertThat(response.getDailyStats()).hasSize(1000);
        }

        @Test
        @DisplayName("特殊日期格式应该正确处理")
        void shouldHandleSpecialDateFormats() {
            // Given
            String[] specialDates = {
                "2024-01-01",
                "2024-12-31",
                "2099-12-31",
                "2000-01-01"
            };

            for (String date : specialDates) {
                // When
                ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats(date, 10, 5);

                // Then
                assertThat(stats.getDate()).isEqualTo(date);
            }
        }

        @Test
        @DisplayName("包含null元素的列表应该正确处理")
        void shouldHandleListWithNullElements() {
            // Given
            List<ActivityStatsResponse.DailyStats> statsWithNull = new ArrayList<>();
            statsWithNull.add(new ActivityStatsResponse.DailyStats("2024-01-01", 10, 5));
            statsWithNull.add(null);

            // When
            ActivityStatsResponse response = new ActivityStatsResponse(10L, 5L, statsWithNull);

            // Then
            assertThat(response.getDailyStats()).hasSize(2);
            assertThat(response.getDailyStats().get(0)).isNotNull();
            assertThat(response.getDailyStats().get(1)).isNull();
        }

        @Test
        @DisplayName("DailyStats应该处理极大int值")
        void shouldHandleMaxIntValues() {
            // When
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats(
                "2024-01-01", 
                Integer.MAX_VALUE, 
                Integer.MAX_VALUE
            );

            // Then
            assertThat(stats.getParticipants()).isEqualTo(Integer.MAX_VALUE);
            assertThat(stats.getShares()).isEqualTo(Integer.MAX_VALUE);
        }

        @Test
        @DisplayName("DailyStats应该处理极小int值")
        void shouldHandleMinIntValues() {
            // When
            ActivityStatsResponse.DailyStats stats = new ActivityStatsResponse.DailyStats(
                "2024-01-01", 
                Integer.MIN_VALUE, 
                Integer.MIN_VALUE
            );

            // Then
            assertThat(stats.getParticipants()).isEqualTo(Integer.MIN_VALUE);
            assertThat(stats.getShares()).isEqualTo(Integer.MIN_VALUE);
        }
    }

    @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 {
                        ActivityStatsResponse response = new ActivityStatsResponse(
                            threadIndex, 
                            threadIndex * 2, 
                            Collections.emptyList()
                        );
                        
                        // 验证getter
                        assertThat(response.getTotalParticipants()).isEqualTo(threadIndex);
                        assertThat(response.getTotalShares()).isEqualTo(threadIndex * 2);
                        
                        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();
            }
        }
    }

    private List<ActivityStatsResponse.DailyStats> createDailyStats() {
        List<ActivityStatsResponse.DailyStats> stats = new ArrayList<>();
        stats.add(new ActivityStatsResponse.DailyStats("2024-01-01", 10, 5));
        stats.add(new ActivityStatsResponse.DailyStats("2024-01-02", 20, 10));
        stats.add(new ActivityStatsResponse.DailyStats("2024-01-03", 30, 15));
        return stats;
    }
}
