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

import com.kt.event.participation.application.dto.ParticipationRequest;
import com.kt.event.participation.application.service.ParticipationService;
import com.kt.event.participation.domain.participant.ParticipantRepository;
import com.kt.event.participation.infrastructure.kafka.event.ParticipantRegisteredEvent;
import org.apache.kafka.clients.consumer.Consumer;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.support.serializer.JsonDeserializer;
import org.springframework.kafka.test.EmbeddedKafkaBroker;
import org.springframework.kafka.test.context.EmbeddedKafka;
import org.springframework.kafka.test.utils.KafkaTestUtils;
import org.springframework.test.context.ActiveProfiles;

import java.time.Duration;
import java.util.Collections;
import java.util.Map;

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

/**
 * Kafka 이벤트 발행 통합 테스트
 *
 * @author Digital Garage Team
 * @since 2025-01-24
 */
@Disabled("Kafka producer가 embedded broker의 bootstrap servers를 사용하도록 설정 필요")
@SpringBootTest
@EmbeddedKafka(partitions = 1, topics = {"participant-registered-events"}, ports = {0})
@DisplayName("Kafka 이벤트 발행 통합 테스트")
class KafkaEventPublishIntegrationTest {

    private static final String TOPIC = "participant-registered-events";
    private static final String TEST_EVENT_ID = "EVT-TEST-001";

    @Autowired
    private ParticipationService participationService;

    @Autowired
    private ParticipantRepository participantRepository;

    @Autowired
    private EmbeddedKafkaBroker embeddedKafka;

    private Consumer<String, ParticipantRegisteredEvent> consumer;

    @BeforeEach
    void setUp() {
        // Kafka Consumer 설정
        Map<String, Object> consumerProps = KafkaTestUtils.consumerProps(
            embeddedKafka.getBrokersAsString(), "test-group", "false");
        consumerProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        consumerProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
        consumerProps.put(JsonDeserializer.TRUSTED_PACKAGES, "*");
        consumerProps.put(JsonDeserializer.VALUE_DEFAULT_TYPE, ParticipantRegisteredEvent.class);
        consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "latest");

        DefaultKafkaConsumerFactory<String, ParticipantRegisteredEvent> consumerFactory =
            new DefaultKafkaConsumerFactory<>(consumerProps);
        consumer = consumerFactory.createConsumer();
        consumer.subscribe(Collections.singletonList(TOPIC));
    }

    @AfterEach
    void tearDown() {
        if (consumer != null) {
            consumer.close();
        }
        // 테스트 데이터 정리
        participantRepository.deleteAll();
    }

    @Test
    @DisplayName("이벤트 참여 시 Kafka 이벤트가 발행되어야 한다")
    void shouldPublishKafkaEventWhenParticipate() throws Exception {
        // Given: 참여 요청 데이터
        ParticipationRequest request = ParticipationRequest.builder()
            .name("테스트사용자")
            .phoneNumber("01012345678")
            .email("test@example.com")
            .storeVisited(true)
            .agreeMarketing(true)
            .agreePrivacy(true)
            .build();

        // When: 이벤트 참여
        participationService.participate(TEST_EVENT_ID, request);

        // Then: Kafka 메시지 수신 확인
        ConsumerRecord<String, ParticipantRegisteredEvent> record =
            KafkaTestUtils.getSingleRecord(consumer, TOPIC, Duration.ofSeconds(10));

        assertThat(record).isNotNull();
        assertThat(record.key()).isEqualTo(TEST_EVENT_ID);

        ParticipantRegisteredEvent event = record.value();
        assertThat(event).isNotNull();
        assertThat(event.getEventId()).isEqualTo(TEST_EVENT_ID);
        assertThat(event.getName()).isEqualTo("테스트사용자");
        assertThat(event.getPhoneNumber()).isEqualTo("01012345678");
        assertThat(event.getStoreVisited()).isTrue();
        assertThat(event.getBonusEntries()).isEqualTo(5);
        assertThat(event.getParticipatedAt()).isNotNull();
    }

    @Test
    @DisplayName("매장 미방문 참여자의 이벤트가 발행되어야 한다")
    void shouldPublishEventForNonStoreVisitor() throws Exception {
        // Given: 매장 미방문 참여 요청
        ParticipationRequest request = ParticipationRequest.builder()
            .name("온라인사용자")
            .phoneNumber("01098765432")
            .email("online@example.com")
            .storeVisited(false)
            .agreeMarketing(false)
            .agreePrivacy(true)
            .build();

        // When: 이벤트 참여
        participationService.participate(TEST_EVENT_ID, request);

        // Then: Kafka 메시지 수신 확인
        ConsumerRecord<String, ParticipantRegisteredEvent> record =
            KafkaTestUtils.getSingleRecord(consumer, TOPIC, Duration.ofSeconds(10));

        assertThat(record).isNotNull();

        ParticipantRegisteredEvent event = record.value();
        assertThat(event.getStoreVisited()).isFalse();
        assertThat(event.getBonusEntries()).isEqualTo(1);
    }

    @Test
    @DisplayName("여러 참여자의 이벤트가 순차적으로 발행되어야 한다")
    void shouldPublishMultipleEventsSequentially() throws Exception {
        // Given: 3명의 참여자
        for (int i = 1; i <= 3; i++) {
            ParticipationRequest request = ParticipationRequest.builder()
                .name("참여자" + i)
                .phoneNumber("0101234567" + i)
                .email("user" + i + "@example.com")
                .storeVisited(i % 2 == 0)
                .agreeMarketing(true)
                .agreePrivacy(true)
                .build();

            // When: 이벤트 참여
            participationService.participate(TEST_EVENT_ID, request);
        }

        // Then: 3개의 Kafka 메시지 수신 확인
        for (int i = 1; i <= 3; i++) {
            ConsumerRecord<String, ParticipantRegisteredEvent> record =
                KafkaTestUtils.getSingleRecord(consumer, TOPIC, Duration.ofSeconds(10));

            assertThat(record).isNotNull();

            ParticipantRegisteredEvent event = record.value();
            assertThat(event.getName()).startsWith("참여자");
            assertThat(event.getEventId()).isEqualTo(TEST_EVENT_ID);
        }
    }
}
