package com.mosquito.project.domain;

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

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

@DisplayName("Reward 领域对象测试")
class RewardTest {

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

        @Test
        @DisplayName("积分构造函数应该创建POINTS类型奖励")
        void shouldCreatePointsReward_whenUsingPointsConstructor() {
            // Given
            int points = 100;

            // When
            Reward reward = new Reward(points);

            // Then
            assertThat(reward.getRewardType()).isEqualTo(RewardType.POINTS);
            assertThat(reward.getPoints()).isEqualTo(points);
            assertThat(reward.getCouponBatchId()).isNull();
        }

        @Test
        @DisplayName("优惠券构造函数应该创建COUPON类型奖励")
        void shouldCreateCouponReward_whenUsingCouponConstructor() {
            // Given
            String couponBatchId = "BATCH-123";

            // When
            Reward reward = new Reward(couponBatchId);

            // Then
            assertThat(reward.getRewardType()).isEqualTo(RewardType.COUPON);
            assertThat(reward.getCouponBatchId()).isEqualTo(couponBatchId);
            assertThat(reward.getPoints()).isEqualTo(0);
        }

        @Test
        @DisplayName("应该支持零积分奖励")
        void shouldSupportZeroPoints() {
            // When
            Reward reward = new Reward(0);

            // Then
            assertThat(reward.getRewardType()).isEqualTo(RewardType.POINTS);
            assertThat(reward.getPoints()).isEqualTo(0);
        }

        @Test
        @DisplayName("应该支持负积分奖励")
        void shouldSupportNegativePoints() {
            // When
            Reward reward = new Reward(-50);

            // Then
            assertThat(reward.getRewardType()).isEqualTo(RewardType.POINTS);
            assertThat(reward.getPoints()).isEqualTo(-50);
        }

        @Test
        @DisplayName("应该支持null优惠券批次ID")
        void shouldSupportNullCouponBatchId() {
            // When
            Reward reward = new Reward((String) null);

            // Then
            assertThat(reward.getRewardType()).isEqualTo(RewardType.COUPON);
            assertThat(reward.getCouponBatchId()).isNull();
        }

        @Test
        @DisplayName("应该支持空字符串优惠券批次ID")
        void shouldSupportEmptyCouponBatchId() {
            // When
            Reward reward = new Reward("");

            // Then
            assertThat(reward.getRewardType()).isEqualTo(RewardType.COUPON);
            assertThat(reward.getCouponBatchId()).isEmpty();
        }
    }

    @Nested
    @DisplayName("equals和hashCode测试")
    class EqualsAndHashCodeTests {

        @Test
        @DisplayName("相同积分的奖励应该相等")
        void shouldBeEqual_whenSamePoints() {
            // Given
            Reward reward1 = new Reward(100);
            Reward reward2 = new Reward(100);

            // Then
            assertThat(reward1).isEqualTo(reward2);
            assertThat(reward1.hashCode()).isEqualTo(reward2.hashCode());
        }

        @Test
        @DisplayName("不同积分的奖励不应该相等")
        void shouldNotBeEqual_whenDifferentPoints() {
            // Given
            Reward reward1 = new Reward(100);
            Reward reward2 = new Reward(200);

            // Then
            assertThat(reward1).isNotEqualTo(reward2);
        }

        @Test
        @DisplayName("相同优惠券批次ID的奖励应该相等")
        void shouldBeEqual_whenSameCouponBatchId() {
            // Given
            Reward reward1 = new Reward("BATCH-123");
            Reward reward2 = new Reward("BATCH-123");

            // Then
            assertThat(reward1).isEqualTo(reward2);
            assertThat(reward1.hashCode()).isEqualTo(reward2.hashCode());
        }

        @Test
        @DisplayName("不同优惠券批次ID的奖励不应该相等")
        void shouldNotBeEqual_whenDifferentCouponBatchId() {
            // Given
            Reward reward1 = new Reward("BATCH-123");
            Reward reward2 = new Reward("BATCH-456");

            // Then
            assertThat(reward1).isNotEqualTo(reward2);
        }

        @Test
        @DisplayName("积分奖励和优惠券奖励不应该相等")
        void shouldNotBeEqual_whenDifferentRewardTypes() {
            // Given
            Reward pointsReward = new Reward(100);
            Reward couponReward = new Reward("BATCH-123");

            // Then
            assertThat(pointsReward).isNotEqualTo(couponReward);
        }

        @Test
        @DisplayName("与自身比较应该相等")
        void shouldBeEqual_whenComparingWithSelf() {
            // Given
            Reward reward = new Reward(100);

            // Then
            assertThat(reward).isEqualTo(reward);
        }

        @Test
        @DisplayName("与null比较不应该相等")
        void shouldNotBeEqual_whenComparingWithNull() {
            // Given
            Reward reward = new Reward(100);

            // Then
            assertThat(reward).isNotEqualTo(null);
        }

        @Test
        @DisplayName("与不同类型对象比较不应该相等")
        void shouldNotBeEqual_whenComparingWithDifferentType() {
            // Given
            Reward reward = new Reward(100);
            String other = "not a reward";

            // Then
            assertThat(reward).isNotEqualTo(other);
        }

        @Test
        @DisplayName("null优惠券批次ID的奖励应该相等")
        void shouldBeEqual_whenBothHaveNullCouponBatchId() {
            // Given
            Reward reward1 = new Reward((String) null);
            Reward reward2 = new Reward((String) null);

            // Then
            assertThat(reward1).isEqualTo(reward2);
            assertThat(reward1.hashCode()).isEqualTo(reward2.hashCode());
        }

        @Test
        @DisplayName("一个null一个非null的优惠券批次ID不应该相等")
        void shouldNotBeEqual_whenOneHasNullCouponBatchId() {
            // Given
            Reward reward1 = new Reward((String) null);
            Reward reward2 = new Reward("BATCH-123");

            // Then
            assertThat(reward1).isNotEqualTo(reward2);
        }
    }

    @Nested
    @DisplayName("Getter方法测试")
    class GetterTests {

        @Test
        @DisplayName("getRewardType应该返回正确的类型")
        void shouldReturnCorrectRewardType() {
            // Given
            Reward pointsReward = new Reward(100);
            Reward couponReward = new Reward("BATCH-123");

            // Then
            assertThat(pointsReward.getRewardType()).isEqualTo(RewardType.POINTS);
            assertThat(couponReward.getRewardType()).isEqualTo(RewardType.COUPON);
        }

        @Test
        @DisplayName("getPoints应该返回正确的积分值")
        void shouldReturnCorrectPoints() {
            // Given
            Reward reward = new Reward(250);

            // Then
            assertThat(reward.getPoints()).isEqualTo(250);
        }

        @Test
        @DisplayName("getCouponBatchId应该返回正确的批次ID")
        void shouldReturnCorrectCouponBatchId() {
            // Given
            Reward reward = new Reward("BATCH-XYZ");

            // Then
            assertThat(reward.getCouponBatchId()).isEqualTo("BATCH-XYZ");
        }

        @Test
        @DisplayName("积分奖励的getCouponBatchId应该返回null")
        void shouldReturnNullCouponBatchId_forPointsReward() {
            // Given
            Reward reward = new Reward(100);

            // Then
            assertThat(reward.getCouponBatchId()).isNull();
        }

        @Test
        @DisplayName("优惠券奖励的getPoints应该返回0")
        void shouldReturnZeroPoints_forCouponReward() {
            // Given
            Reward reward = new Reward("BATCH-123");

            // Then
            assertThat(reward.getPoints()).isEqualTo(0);
        }
    }

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

        @Test
        @DisplayName("应该支持最大整数积分")
        void shouldSupportMaxIntegerPoints() {
            // When
            Reward reward = new Reward(Integer.MAX_VALUE);

            // Then
            assertThat(reward.getPoints()).isEqualTo(Integer.MAX_VALUE);
        }

        @Test
        @DisplayName("应该支持最小整数积分")
        void shouldSupportMinIntegerPoints() {
            // When
            Reward reward = new Reward(Integer.MIN_VALUE);

            // Then
            assertThat(reward.getPoints()).isEqualTo(Integer.MIN_VALUE);
        }

        @Test
        @DisplayName("应该支持超长优惠券批次ID")
        void shouldSupportLongCouponBatchId() {
            // Given
            String longId = "BATCH-" + "X".repeat(1000);

            // When
            Reward reward = new Reward(longId);

            // Then
            assertThat(reward.getCouponBatchId()).isEqualTo(longId);
        }

        @Test
        @DisplayName("应该支持包含特殊字符的优惠券批次ID")
        void shouldSupportSpecialCharactersInCouponBatchId() {
            // Given
            String specialId = "BATCH-!@#$%^&*()_+-=[]{}|;:',.<>?/~`";

            // When
            Reward reward = new Reward(specialId);

            // Then
            assertThat(reward.getCouponBatchId()).isEqualTo(specialId);
        }
    }
}
