package com.kt.event.participation.test.unit;

import com.kt.event.participation.domain.participant.Participant;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

/**
 * Participant Entity 단위 테스트
 *
 * @author Digital Garage Team
 * @since 2025-01-24
 */
@DisplayName("Participant 엔티티 단위 테스트")
class ParticipantUnitTest {

    // 테스트 데이터 상수
    private static final String VALID_EVENT_ID = "evt_20250124_001";
    private static final String VALID_NAME = "홍길동";
    private static final String VALID_PHONE = "010-1234-5678";
    private static final String VALID_EMAIL = "hong@test.com";
    private static final Long VALID_SEQUENCE = 1L;

    @Test
    @DisplayName("매장 방문 시 participantId가 정상적으로 생성된다")
    void givenStoreVisited_whenGenerateParticipantId_thenSuccess() {
        // Given
        String eventId = VALID_EVENT_ID;
        Long sequenceNumber = VALID_SEQUENCE;

        // When
        String participantId = Participant.generateParticipantId(eventId, sequenceNumber);

        // Then
        assertThat(participantId).isEqualTo("prt_20250124_001");
        assertThat(participantId).startsWith("prt_");
        assertThat(participantId).hasSize(16);
    }

    @Test
    @DisplayName("시퀀스 번호가 증가하면 participantId도 증가한다")
    void givenLargeSequence_whenGenerateParticipantId_thenIdIncreases() {
        // Given
        String eventId = VALID_EVENT_ID;
        Long sequenceNumber = 999L;

        // When
        String participantId = Participant.generateParticipantId(eventId, sequenceNumber);

        // Then
        assertThat(participantId).isEqualTo("prt_20250124_999");
    }

    @Test
    @DisplayName("매장 방문 시 보너스 응모권이 5개가 된다")
    void givenStoreVisited_whenCalculateBonusEntries_thenFive() {
        // Given
        Boolean storeVisited = true;

        // When
        Integer bonusEntries = Participant.calculateBonusEntries(storeVisited);

        // Then
        assertThat(bonusEntries).isEqualTo(5);
    }

    @Test
    @DisplayName("매장 미방문 시 보너스 응모권이 1개가 된다")
    void givenNotVisited_whenCalculateBonusEntries_thenOne() {
        // Given
        Boolean storeVisited = false;

        // When
        Integer bonusEntries = Participant.calculateBonusEntries(storeVisited);

        // Then
        assertThat(bonusEntries).isEqualTo(1);
    }

    @Test
    @DisplayName("당첨자로 표시하면 isWinner가 true가 되고 당첨 정보가 설정된다")
    void givenParticipant_whenMarkAsWinner_thenWinnerFieldsSet() {
        // Given
        Participant participant = createValidParticipant();
        Integer winnerRank = 1;

        // When
        participant.markAsWinner(winnerRank);

        // Then
        assertThat(participant.getIsWinner()).isTrue();
        assertThat(participant.getWinnerRank()).isEqualTo(1);
        assertThat(participant.getWonAt()).isNotNull();
    }

    @Test
    @DisplayName("빌더로 참여자를 생성하면 필드가 정상 설정된다")
    void givenValidData_whenBuild_thenParticipantCreated() {
        // Given & When
        Participant participant = Participant.builder()
                .participantId("prt_20250124_001")
                .eventId(VALID_EVENT_ID)
                .name(VALID_NAME)
                .phoneNumber(VALID_PHONE)
                .email(VALID_EMAIL)
                .storeVisited(true)
                .bonusEntries(5)
                .agreeMarketing(true)
                .agreePrivacy(true)
                .isWinner(false)
                .build();

        // Then
        assertThat(participant.getParticipantId()).isEqualTo("prt_20250124_001");
        assertThat(participant.getEventId()).isEqualTo(VALID_EVENT_ID);
        assertThat(participant.getName()).isEqualTo(VALID_NAME);
        assertThat(participant.getPhoneNumber()).isEqualTo(VALID_PHONE);
        assertThat(participant.getEmail()).isEqualTo(VALID_EMAIL);
        assertThat(participant.getStoreVisited()).isTrue();
        assertThat(participant.getBonusEntries()).isEqualTo(5);
        assertThat(participant.getAgreeMarketing()).isTrue();
        assertThat(participant.getAgreePrivacy()).isTrue();
        assertThat(participant.getIsWinner()).isFalse();
    }

    @Test
    @DisplayName("prePersist에서 개인정보 동의가 null이면 예외가 발생한다")
    void givenNullPrivacyAgree_whenPrePersist_thenThrowException() {
        // Given
        Participant participant = Participant.builder()
                .participantId("prt_20250124_001")
                .eventId(VALID_EVENT_ID)
                .name(VALID_NAME)
                .phoneNumber(VALID_PHONE)
                .storeVisited(true)
                .agreePrivacy(null)
                .build();

        // When & Then
        assertThatThrownBy(participant::prePersist)
                .isInstanceOf(IllegalStateException.class)
                .hasMessage("개인정보 수집 및 이용 동의는 필수입니다");
    }

    @Test
    @DisplayName("prePersist에서 개인정보 동의가 false이면 예외가 발생한다")
    void givenFalsePrivacyAgree_whenPrePersist_thenThrowException() {
        // Given
        Participant participant = Participant.builder()
                .participantId("prt_20250124_001")
                .eventId(VALID_EVENT_ID)
                .name(VALID_NAME)
                .phoneNumber(VALID_PHONE)
                .storeVisited(true)
                .agreePrivacy(false)
                .build();

        // When & Then
        assertThatThrownBy(participant::prePersist)
                .isInstanceOf(IllegalStateException.class)
                .hasMessage("개인정보 수집 및 이용 동의는 필수입니다");
    }

    @Test
    @DisplayName("prePersist에서 bonusEntries가 null이면 자동 계산된다")
    void givenNullBonusEntries_whenPrePersist_thenCalculated() {
        // Given
        Participant participant = Participant.builder()
                .participantId("prt_20250124_001")
                .eventId(VALID_EVENT_ID)
                .name(VALID_NAME)
                .phoneNumber(VALID_PHONE)
                .storeVisited(true)
                .agreePrivacy(true)
                .bonusEntries(null)
                .build();

        // When
        participant.prePersist();

        // Then
        assertThat(participant.getBonusEntries()).isEqualTo(5);
    }

    @Test
    @DisplayName("prePersist에서 isWinner가 null이면 false로 설정된다")
    void givenNullIsWinner_whenPrePersist_thenSetFalse() {
        // Given
        Participant participant = Participant.builder()
                .participantId("prt_20250124_001")
                .eventId(VALID_EVENT_ID)
                .name(VALID_NAME)
                .phoneNumber(VALID_PHONE)
                .storeVisited(true)
                .agreePrivacy(true)
                .isWinner(null)
                .build();

        // When
        participant.prePersist();

        // Then
        assertThat(participant.getIsWinner()).isFalse();
    }

    // 헬퍼 메서드
    private Participant createValidParticipant() {
        return Participant.builder()
                .participantId("prt_20250124_001")
                .eventId(VALID_EVENT_ID)
                .name(VALID_NAME)
                .phoneNumber(VALID_PHONE)
                .email(VALID_EMAIL)
                .storeVisited(true)
                .bonusEntries(5)
                .agreeMarketing(true)
                .agreePrivacy(true)
                .isWinner(false)
                .build();
    }
}
