package com.mosquito.project.persistence.entity;

import org.junit.jupiter.api.BeforeEach;
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.OffsetDateTime;
import java.time.ZoneOffset;

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

class UserInviteEntityTest {

    private UserInviteEntity entity;

    @BeforeEach
    void setUp() {
        entity = new UserInviteEntity();
    }

    @Test
    void shouldReturnNullId_whenNotSet() {
        assertThat(entity.getId()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "1, 1",
        "100, 100",
        "999999, 999999",
        "0, 0"
    })
    void shouldReturnSetId_whenSetWithValue(Long id, Long expected) {
        entity.setId(id);
        assertThat(entity.getId()).isEqualTo(expected);
    }

    @Test
    void shouldReturnSetId_whenSetWithMaxValue() {
        entity.setId(Long.MAX_VALUE);
        assertThat(entity.getId()).isEqualTo(Long.MAX_VALUE);
    }

    @Test
    void shouldReturnNullActivityId_whenNotSet() {
        assertThat(entity.getActivityId()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "1, 1",
        "100, 100",
        "0, 0",
        "-1, -1"
    })
    void shouldReturnSetActivityId_whenSetWithValue(Long activityId, Long expected) {
        entity.setActivityId(activityId);
        assertThat(entity.getActivityId()).isEqualTo(expected);
    }

    @Test
    void shouldReturnNullInviterUserId_whenNotSet() {
        assertThat(entity.getInviterUserId()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "1, 1",
        "999, 999",
        "0, 0",
        "-999, -999"
    })
    void shouldReturnSetInviterUserId_whenSetWithValue(Long inviterUserId, Long expected) {
        entity.setInviterUserId(inviterUserId);
        assertThat(entity.getInviterUserId()).isEqualTo(expected);
    }

    @Test
    void shouldReturnNullInviteeUserId_whenNotSet() {
        assertThat(entity.getInviteeUserId()).isNull();
    }

    @ParameterizedTest
    @CsvSource({
        "1, 1",
        "999, 999",
        "0, 0",
        "-1, -1"
    })
    void shouldReturnSetInviteeUserId_whenSetWithValue(Long inviteeUserId, Long expected) {
        entity.setInviteeUserId(inviteeUserId);
        assertThat(entity.getInviteeUserId()).isEqualTo(expected);
    }

    @Test
    void shouldReturnNullCreatedAt_whenNotSet() {
        assertThat(entity.getCreatedAt()).isNull();
    }

    @Test
    void shouldReturnSetCreatedAt_whenSet() {
        OffsetDateTime now = OffsetDateTime.now();
        entity.setCreatedAt(now);
        assertThat(entity.getCreatedAt()).isEqualTo(now);
    }

    @Test
    void shouldHandleDifferentTimeZones_whenSettingCreatedAt() {
        OffsetDateTime utc = OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 0, ZoneOffset.UTC);
        OffsetDateTime beijing = OffsetDateTime.of(2024, 1, 1, 20, 0, 0, 0, ZoneOffset.ofHours(8));
        OffsetDateTime newYork = OffsetDateTime.of(2024, 1, 1, 7, 0, 0, 0, ZoneOffset.ofHours(-5));

        entity.setCreatedAt(utc);
        assertThat(entity.getCreatedAt()).isEqualTo(utc);

        entity.setCreatedAt(beijing);
        assertThat(entity.getCreatedAt()).isEqualTo(beijing);

        entity.setCreatedAt(newYork);
        assertThat(entity.getCreatedAt()).isEqualTo(newYork);
    }

    @Test
    void shouldHandleEpochTime_whenSet() {
        OffsetDateTime epoch = OffsetDateTime.of(1970, 1, 1, 0, 0, 0, 0, ZoneOffset.UTC);
        entity.setCreatedAt(epoch);
        assertThat(entity.getCreatedAt()).isEqualTo(epoch);
    }

    @Test
    void shouldHandleFutureTime_whenSet() {
        OffsetDateTime future = OffsetDateTime.of(2099, 12, 31, 23, 59, 59, 0, ZoneOffset.UTC);
        entity.setCreatedAt(future);
        assertThat(entity.getCreatedAt()).isEqualTo(future);
    }

    @Test
    void shouldReturnNullStatus_whenNotSet() {
        assertThat(entity.getStatus()).isNull();
    }

    @ParameterizedTest
    @ValueSource(strings = {
        "PENDING",
        "ACCEPTED",
        "REJECTED",
        "EXPIRED",
        "COMPLETED",
        "active",
        "inactive"
    })
    void shouldAcceptVariousStatusValues_whenSet(String status) {
        entity.setStatus(status);
        assertThat(entity.getStatus()).isEqualTo(status);
    }

    @Test
    void shouldAcceptEmptyStatus_whenSet() {
        entity.setStatus("");
        assertThat(entity.getStatus()).isEmpty();
    }

    @Test
    void shouldAcceptLongStatus_whenUpTo32Chars() {
        String longStatus = "S".repeat(32);
        entity.setStatus(longStatus);
        assertThat(entity.getStatus()).hasSize(32);
    }

    @Test
    void shouldCreateCompleteEntity_whenAllFieldsSet() {
        entity.setId(1L);
        entity.setActivityId(100L);
        entity.setInviterUserId(50L);
        entity.setInviteeUserId(51L);
        entity.setCreatedAt(OffsetDateTime.now());
        entity.setStatus("PENDING");

        assertThat(entity.getId()).isEqualTo(1L);
        assertThat(entity.getActivityId()).isEqualTo(100L);
        assertThat(entity.getInviterUserId()).isEqualTo(50L);
        assertThat(entity.getInviteeUserId()).isEqualTo(51L);
        assertThat(entity.getCreatedAt()).isNotNull();
        assertThat(entity.getStatus()).isEqualTo("PENDING");
    }

    @Test
    void shouldAllowFieldReassignment_whenMultipleSets() {
        entity.setId(1L);
        entity.setId(2L);
        assertThat(entity.getId()).isEqualTo(2L);

        entity.setActivityId(100L);
        entity.setActivityId(200L);
        assertThat(entity.getActivityId()).isEqualTo(200L);

        entity.setStatus("PENDING");
        entity.setStatus("ACCEPTED");
        assertThat(entity.getStatus()).isEqualTo("ACCEPTED");
    }

    @Test
    void shouldAcceptNullValues_whenExplicitlySetToNull() {
        entity.setId(1L);
        entity.setId(null);
        assertThat(entity.getId()).isNull();

        entity.setStatus("ACTIVE");
        entity.setStatus(null);
        assertThat(entity.getStatus()).isNull();

        entity.setCreatedAt(OffsetDateTime.now());
        entity.setCreatedAt(null);
        assertThat(entity.getCreatedAt()).isNull();
    }

    @Test
    void shouldSupportChainedSetters_whenBuildingEntity() {
        OffsetDateTime createdAt = OffsetDateTime.now();
        entity.setId(1L);
        entity.setActivityId(100L);
        entity.setInviterUserId(50L);
        entity.setInviteeUserId(51L);
        entity.setCreatedAt(createdAt);
        entity.setStatus("PENDING");

        assertThat(entity.getId()).isEqualTo(1L);
        assertThat(entity.getActivityId()).isEqualTo(100L);
        assertThat(entity.getInviterUserId()).isEqualTo(50L);
        assertThat(entity.getInviteeUserId()).isEqualTo(51L);
        assertThat(entity.getCreatedAt()).isEqualTo(createdAt);
        assertThat(entity.getStatus()).isEqualTo("PENDING");
    }

    @Test
    void shouldHandleEmptyEntityState_whenNoFieldsSet() {
        assertThat(entity.getId()).isNull();
        assertThat(entity.getActivityId()).isNull();
        assertThat(entity.getInviterUserId()).isNull();
        assertThat(entity.getInviteeUserId()).isNull();
        assertThat(entity.getCreatedAt()).isNull();
        assertThat(entity.getStatus()).isNull();
    }

    @Test
    void shouldAcceptPartialEntityState_whenSomeFieldsSet() {
        entity.setActivityId(100L);
        entity.setInviteeUserId(50L);

        assertThat(entity.getId()).isNull();
        assertThat(entity.getActivityId()).isEqualTo(100L);
        assertThat(entity.getInviterUserId()).isNull();
        assertThat(entity.getInviteeUserId()).isEqualTo(50L);
    }

    @Test
    void shouldAssociateWithActivityEntity_whenActivityIdSet() {
        ActivityEntity activity = new ActivityEntity();
        activity.setId(100L);

        entity.setActivityId(activity.getId());
        entity.setInviterUserId(1L);
        entity.setInviteeUserId(2L);

        assertThat(entity.getActivityId()).isEqualTo(100L);
    }

    @Test
    void shouldHandleMaxLongIds_whenSet() {
        entity.setId(Long.MAX_VALUE);
        entity.setActivityId(Long.MAX_VALUE);
        entity.setInviterUserId(Long.MAX_VALUE);
        entity.setInviteeUserId(Long.MAX_VALUE);

        assertThat(entity.getId()).isEqualTo(Long.MAX_VALUE);
        assertThat(entity.getActivityId()).isEqualTo(Long.MAX_VALUE);
        assertThat(entity.getInviterUserId()).isEqualTo(Long.MAX_VALUE);
        assertThat(entity.getInviteeUserId()).isEqualTo(Long.MAX_VALUE);
    }

    @Test
    void shouldHandleNegativeIds_whenSet() {
        entity.setId(-1L);
        entity.setActivityId(-100L);
        entity.setInviterUserId(-999L);
        entity.setInviteeUserId(-888L);

        assertThat(entity.getId()).isEqualTo(-1L);
        assertThat(entity.getActivityId()).isEqualTo(-100L);
        assertThat(entity.getInviterUserId()).isEqualTo(-999L);
        assertThat(entity.getInviteeUserId()).isEqualTo(-888L);
    }

    @Test
    void shouldHandleZeroIds_whenSet() {
        entity.setId(0L);
        entity.setActivityId(0L);
        entity.setInviterUserId(0L);
        entity.setInviteeUserId(0L);

        assertThat(entity.getId()).isZero();
        assertThat(entity.getActivityId()).isZero();
        assertThat(entity.getInviterUserId()).isZero();
        assertThat(entity.getInviteeUserId()).isZero();
    }

    @Test
    void shouldHandleTimePrecision_whenMillisecondsSet() {
        OffsetDateTime precise = OffsetDateTime.of(2024, 1, 1, 12, 0, 0, 123456789, ZoneOffset.UTC);
        entity.setCreatedAt(precise);
        assertThat(entity.getCreatedAt()).isEqualTo(precise);
    }

    @Test
    void shouldHandleUnicodeCharactersInStatus_whenSet() {
        entity.setStatus("状态-🎉-émoji");
        assertThat(entity.getStatus()).contains("状态").contains("🎉");
    }

    @Test
    void shouldHandleWhitespaceOnlyStatus_whenSet() {
        entity.setStatus("   ");
        assertThat(entity.getStatus()).isEqualTo("   ");
    }

    @Test
    void shouldHandleStatusTransitions_whenChangedMultipleTimes() {
        entity.setStatus("PENDING");
        assertThat(entity.getStatus()).isEqualTo("PENDING");

        entity.setStatus("ACCEPTED");
        assertThat(entity.getStatus()).isEqualTo("ACCEPTED");

        entity.setStatus("COMPLETED");
        assertThat(entity.getStatus()).isEqualTo("COMPLETED");
    }

    @Test
    void shouldRepresentInviteRelationship_whenBothUserIdsSet() {
        Long inviterId = 100L;
        Long inviteeId = 200L;

        entity.setInviterUserId(inviterId);
        entity.setInviteeUserId(inviteeId);

        assertThat(entity.getInviterUserId()).isNotEqualTo(entity.getInviteeUserId());
        assertThat(entity.getInviterUserId()).isEqualTo(inviterId);
        assertThat(entity.getInviteeUserId()).isEqualTo(inviteeId);
    }

    @Test
    void shouldHandleSameUserAsInviterAndInvitee_whenSet() {
        entity.setInviterUserId(100L);
        entity.setInviteeUserId(100L);

        assertThat(entity.getInviterUserId()).isEqualTo(entity.getInviteeUserId());
    }

    @Test
    void shouldHandleStatusWithSpecialCharacters_whenSet() {
        entity.setStatus("STATUS_WITH_UNDERSCORES-123");
        assertThat(entity.getStatus()).contains("_").contains("-").contains("123");
    }

    @ParameterizedTest
    @ValueSource(strings = {"ACTIVE", "INACTIVE", "SUSPENDED", "DELETED", "ARCHIVED"})
    void shouldAcceptCommonStatusEnumValues_whenSet(String status) {
        entity.setStatus(status);
        assertThat(entity.getStatus()).isEqualTo(status);
    }

    @Test
    void shouldMaintainConsistency_whenSelfReferencingInvite() {
        entity.setActivityId(1L);
        entity.setInviterUserId(50L);
        entity.setInviteeUserId(50L);
        entity.setStatus("SELF_INVITE");

        assertThat(entity.getInviterUserId()).isEqualTo(entity.getInviteeUserId());
    }

    @Test
    void shouldHandleLargeActivityId_whenSet() {
        entity.setActivityId(9999999999L);
        assertThat(entity.getActivityId()).isEqualTo(9999999999L);
    }

    @Test
    void shouldHandleAllStatusesAsString_whenNoEnumConstraint() {
        String[] statuses = {"0", "1", "true", "false", "yes", "no", "null", "undefined"};

        for (String status : statuses) {
            entity.setStatus(status);
            assertThat(entity.getStatus()).isEqualTo(status);
        }
    }
}
