package dev.lions.unionflow.server.messaging;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.verify;

import dev.lions.unionflow.server.service.WebSocketBroadcastService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import io.smallrye.reactive.messaging.kafka.Record;
import jakarta.inject.Inject;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests unitaires pour KafkaEventConsumer.
 *
 * <p>Les méthodes @Incoming sont appelées directement (sans broker Kafka) ;
 * WebSocketBroadcastService est mocké via @InjectMock pour isoler le consumer.
 *
 * @author UnionFlow Team
 */
@QuarkusTest
@DisplayName("KafkaEventConsumer")
class KafkaEventConsumerTest {

    @Inject
    KafkaEventConsumer kafkaEventConsumer;

    @InjectMock
    WebSocketBroadcastService webSocketBroadcastService;

    @BeforeEach
    void resetMock() {
        doNothing().when(webSocketBroadcastService).broadcast(anyString());
    }

    // =========================================================================
    // consumeFinanceApprovals
    // =========================================================================

    @Test
    @DisplayName("consumeFinanceApprovals — broadcast appelé avec la valeur du record")
    void consumeFinanceApprovals_broadcastAppele() {
        Record<String, String> record = Record.of("key-finance", "{\"type\":\"APPROVAL_PENDING\"}");

        assertThatCode(() -> kafkaEventConsumer.consumeFinanceApprovals(record))
                .doesNotThrowAnyException();

        verify(webSocketBroadcastService).broadcast("{\"type\":\"APPROVAL_PENDING\"}");
    }

    @Test
    @DisplayName("consumeFinanceApprovals — exception swallowée (pas de propagation)")
    void consumeFinanceApprovals_exceptionSwallowee() {
        Record<String, String> record = Record.of("key-finance", "payload");
        doThrow(new RuntimeException("WebSocket error")).when(webSocketBroadcastService).broadcast(anyString());

        // L'exception doit être attrapée dans le catch — aucune propagation
        assertThatCode(() -> kafkaEventConsumer.consumeFinanceApprovals(record))
                .doesNotThrowAnyException();
    }

    // =========================================================================
    // consumeDashboardStats
    // =========================================================================

    @Test
    @DisplayName("consumeDashboardStats — broadcast appelé avec la valeur du record")
    void consumeDashboardStats_broadcastAppele() {
        Record<String, String> record = Record.of("key-stats", "{\"totalMembers\":250}");

        assertThatCode(() -> kafkaEventConsumer.consumeDashboardStats(record))
                .doesNotThrowAnyException();

        verify(webSocketBroadcastService).broadcast("{\"totalMembers\":250}");
    }

    @Test
    @DisplayName("consumeDashboardStats — exception swallowée (pas de propagation)")
    void consumeDashboardStats_exceptionSwallowee() {
        Record<String, String> record = Record.of("key-stats", "payload");
        doThrow(new RuntimeException("WebSocket unavailable")).when(webSocketBroadcastService).broadcast(anyString());

        assertThatCode(() -> kafkaEventConsumer.consumeDashboardStats(record))
                .doesNotThrowAnyException();
    }

    // =========================================================================
    // consumeNotifications
    // =========================================================================

    @Test
    @DisplayName("consumeNotifications — broadcast appelé avec la valeur du record")
    void consumeNotifications_broadcastAppele() {
        Record<String, String> record = Record.of("key-notif", "{\"message\":\"Cotisation reçue\"}");

        assertThatCode(() -> kafkaEventConsumer.consumeNotifications(record))
                .doesNotThrowAnyException();

        verify(webSocketBroadcastService).broadcast("{\"message\":\"Cotisation reçue\"}");
    }

    @Test
    @DisplayName("consumeNotifications — exception swallowée (pas de propagation)")
    void consumeNotifications_exceptionSwallowee() {
        Record<String, String> record = Record.of("key-notif", "payload");
        doThrow(new RuntimeException("Broadcast failed")).when(webSocketBroadcastService).broadcast(anyString());

        assertThatCode(() -> kafkaEventConsumer.consumeNotifications(record))
                .doesNotThrowAnyException();
    }

    // =========================================================================
    // consumeMembersEvents
    // =========================================================================

    @Test
    @DisplayName("consumeMembersEvents — broadcast appelé avec la valeur du record")
    void consumeMembersEvents_broadcastAppele() {
        Record<String, String> record = Record.of("key-member", "{\"action\":\"MEMBER_CREATED\"}");

        assertThatCode(() -> kafkaEventConsumer.consumeMembersEvents(record))
                .doesNotThrowAnyException();

        verify(webSocketBroadcastService).broadcast("{\"action\":\"MEMBER_CREATED\"}");
    }

    @Test
    @DisplayName("consumeMembersEvents — exception swallowée (pas de propagation)")
    void consumeMembersEvents_exceptionSwallowee() {
        Record<String, String> record = Record.of("key-member", "payload");
        doThrow(new RuntimeException("Connection lost")).when(webSocketBroadcastService).broadcast(anyString());

        assertThatCode(() -> kafkaEventConsumer.consumeMembersEvents(record))
                .doesNotThrowAnyException();
    }

    // =========================================================================
    // consumeContributionsEvents
    // =========================================================================

    @Test
    @DisplayName("consumeContributionsEvents — broadcast appelé avec la valeur du record")
    void consumeContributionsEvents_broadcastAppele() {
        Record<String, String> record = Record.of("key-contrib", "{\"action\":\"CONTRIBUTION_PAID\"}");

        assertThatCode(() -> kafkaEventConsumer.consumeContributionsEvents(record))
                .doesNotThrowAnyException();

        verify(webSocketBroadcastService).broadcast("{\"action\":\"CONTRIBUTION_PAID\"}");
    }

    @Test
    @DisplayName("consumeContributionsEvents — exception swallowée (pas de propagation)")
    void consumeContributionsEvents_exceptionSwallowee() {
        Record<String, String> record = Record.of("key-contrib", "payload");
        doThrow(new RuntimeException("Timeout")).when(webSocketBroadcastService).broadcast(anyString());

        assertThatCode(() -> kafkaEventConsumer.consumeContributionsEvents(record))
                .doesNotThrowAnyException();
    }
}
