package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.entity.SystemLog;
import dev.lions.unionflow.server.repository.SystemLogRepository;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;

@QuarkusTest
class SystemLoggingServiceTest {

    @Inject
    SystemLoggingService systemLoggingService;

    @InjectMock
    SystemLogRepository systemLogRepository;

    // -------------------------------------------------------------------------
    // logRequest — branches getLogLevelFromStatusCode
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("logRequest avec status 200 crée un log de niveau DEBUG")
    void logRequest_statusCode200_createsDebugLog() {
        systemLoggingService.logRequest("GET", "/api/membres", 200, null, null, null, 50L);

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        SystemLog log = captor.getValue();
        assertThat(log.getLevel()).isEqualTo("DEBUG");
        assertThat(log.getSource()).isEqualTo("API");
        assertThat(log.getMessage()).contains("GET").contains("/api/membres").contains("200");
        assertThat(log.getHttpStatusCode()).isEqualTo(200);
        assertThat(log.getEndpoint()).isEqualTo("/api/membres");
        assertThat(log.getTimestamp()).isNotNull();
    }

    @Test
    @DisplayName("logRequest avec status 302 crée un log de niveau INFO")
    void logRequest_statusCode302_createsInfoLog() {
        systemLoggingService.logRequest("GET", "/api/redirect", 302, null, null, null, 10L);

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        assertThat(captor.getValue().getLevel()).isEqualTo("INFO");
        assertThat(captor.getValue().getHttpStatusCode()).isEqualTo(302);
    }

    @Test
    @DisplayName("logRequest avec status 404 crée un log de niveau WARNING")
    void logRequest_statusCode404_createsWarningLog() {
        systemLoggingService.logRequest("GET", "/api/unknown", 404, null, "192.168.1.1", "sess-1", 5L);

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        SystemLog log = captor.getValue();
        assertThat(log.getLevel()).isEqualTo("WARNING");
        assertThat(log.getHttpStatusCode()).isEqualTo(404);
        assertThat(log.getIpAddress()).isEqualTo("192.168.1.1");
        assertThat(log.getSessionId()).isEqualTo("sess-1");
    }

    @Test
    @DisplayName("logRequest avec status 500 crée un log de niveau ERROR")
    void logRequest_statusCode500_createsErrorLog() {
        systemLoggingService.logRequest("POST", "/api/action", 500, "user-42", null, null, 120L);

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        SystemLog log = captor.getValue();
        assertThat(log.getLevel()).isEqualTo("ERROR");
        assertThat(log.getHttpStatusCode()).isEqualTo(500);
        assertThat(log.getUserId()).isEqualTo("user-42");
        assertThat(log.getMessage()).contains("500");
    }

    @Test
    @DisplayName("logRequest avec status 503 crée un log de niveau ERROR (>= 500)")
    void logRequest_statusCode503_createsErrorLog() {
        systemLoggingService.logRequest("GET", "/api/health", 503, null, null, null, 200L);

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        assertThat(captor.getValue().getLevel()).isEqualTo("ERROR");
    }

    @Test
    @DisplayName("logRequest avec status 400 crée un log de niveau WARNING")
    void logRequest_statusCode400_createsWarningLog() {
        systemLoggingService.logRequest("POST", "/api/form", 400, null, null, null, 8L);

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        assertThat(captor.getValue().getLevel()).isEqualTo("WARNING");
    }

    // -------------------------------------------------------------------------
    // logError
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("logError persiste un log avec level ERROR")
    void logError_createsLog() {
        systemLoggingService.logError(
                "Database",
                "Connexion échouée",
                "java.sql.SQLException: timeout",
                "user-1",
                "10.0.0.1",
                "/api/data",
                500
        );

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        SystemLog log = captor.getValue();
        assertThat(log.getLevel()).isEqualTo("ERROR");
        assertThat(log.getSource()).isEqualTo("Database");
        assertThat(log.getMessage()).isEqualTo("Connexion échouée");
        assertThat(log.getDetails()).contains("SQLException");
        assertThat(log.getUserId()).isEqualTo("user-1");
        assertThat(log.getIpAddress()).isEqualTo("10.0.0.1");
        assertThat(log.getEndpoint()).isEqualTo("/api/data");
        assertThat(log.getHttpStatusCode()).isEqualTo(500);
        assertThat(log.getTimestamp()).isNotNull();
    }

    // -------------------------------------------------------------------------
    // logCritical
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("logCritical persiste un log avec level CRITICAL")
    void logCritical_createsLog() {
        systemLoggingService.logCritical(
                "Security",
                "Intrusion détectée",
                "Multiple failed login attempts",
                "user-99",
                "192.168.0.100"
        );

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        SystemLog log = captor.getValue();
        assertThat(log.getLevel()).isEqualTo("CRITICAL");
        assertThat(log.getSource()).isEqualTo("Security");
        assertThat(log.getMessage()).isEqualTo("Intrusion détectée");
        assertThat(log.getDetails()).isEqualTo("Multiple failed login attempts");
        assertThat(log.getUserId()).isEqualTo("user-99");
        assertThat(log.getIpAddress()).isEqualTo("192.168.0.100");
        assertThat(log.getTimestamp()).isNotNull();
    }

    // -------------------------------------------------------------------------
    // logWarning
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("logWarning persiste un log avec level WARNING")
    void logWarning_createsLog() {
        systemLoggingService.logWarning(
                "Cache",
                "Cache presque plein",
                "Utilisation: 90%",
                null,
                null
        );

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        SystemLog log = captor.getValue();
        assertThat(log.getLevel()).isEqualTo("WARNING");
        assertThat(log.getSource()).isEqualTo("Cache");
        assertThat(log.getMessage()).isEqualTo("Cache presque plein");
        assertThat(log.getDetails()).isEqualTo("Utilisation: 90%");
        assertThat(log.getTimestamp()).isNotNull();
    }

    // -------------------------------------------------------------------------
    // logInfo
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("logInfo persiste un log avec level INFO")
    void logInfo_createsLog() {
        systemLoggingService.logInfo(
                "Application",
                "Démarrage du service",
                "Service démarré en 5s"
        );

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        SystemLog log = captor.getValue();
        assertThat(log.getLevel()).isEqualTo("INFO");
        assertThat(log.getSource()).isEqualTo("Application");
        assertThat(log.getMessage()).isEqualTo("Démarrage du service");
        assertThat(log.getDetails()).isEqualTo("Service démarré en 5s");
        assertThat(log.getTimestamp()).isNotNull();
    }

    // -------------------------------------------------------------------------
    // logDebug
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("logDebug persiste un log avec level DEBUG")
    void logDebug_createsLog() {
        systemLoggingService.logDebug(
                "Kafka",
                "Message publié",
                "topic=unionflow.dashboard.stats, offset=42"
        );

        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        verify(systemLogRepository).persist(captor.capture());

        SystemLog log = captor.getValue();
        assertThat(log.getLevel()).isEqualTo("DEBUG");
        assertThat(log.getSource()).isEqualTo("Kafka");
        assertThat(log.getMessage()).isEqualTo("Message publié");
        assertThat(log.getDetails()).contains("offset=42");
        assertThat(log.getTimestamp()).isNotNull();
    }

    // -------------------------------------------------------------------------
    // Resilience : exception dans persist ne propage pas
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("logRequest ne propage pas une exception levée par le repository")
    void logRequest_exceptionInPersist_doesNotPropagate() {
        doThrow(new RuntimeException("DB failure")).when(systemLogRepository).persist(any(SystemLog.class));

        assertThatCode(() ->
                systemLoggingService.logRequest("GET", "/api/test", 200, null, null, null, 10L)
        ).doesNotThrowAnyException();

        // persist() a bien été appelé (le service a tenté de persister)
        verify(systemLogRepository).persist(any(SystemLog.class));
    }

    @Test
    @DisplayName("logError ne propage pas une exception levée par le repository")
    void logError_exceptionInPersist_doesNotPropagate() {
        doThrow(new RuntimeException("DB failure")).when(systemLogRepository).persist(any(SystemLog.class));

        assertThatCode(() ->
                systemLoggingService.logError("Test", "msg", "details", null, null, null, null)
        ).doesNotThrowAnyException();
    }

    @Test
    @DisplayName("logCritical ne propage pas une exception levée par le repository")
    void logCritical_exceptionInPersist_doesNotPropagate() {
        doThrow(new RuntimeException("DB failure")).when(systemLogRepository).persist(any(SystemLog.class));

        assertThatCode(() ->
                systemLoggingService.logCritical("Test", "msg", "details", null, null)
        ).doesNotThrowAnyException();
    }

    @Test
    @DisplayName("logWarning ne propage pas une exception levée par le repository")
    void logWarning_exceptionInPersist_doesNotPropagate() {
        doThrow(new RuntimeException("DB failure")).when(systemLogRepository).persist(any(SystemLog.class));

        assertThatCode(() ->
                systemLoggingService.logWarning("Test", "msg", "details", null, null)
        ).doesNotThrowAnyException();
    }

    @Test
    @DisplayName("logInfo ne propage pas une exception levée par le repository")
    void logInfo_exceptionInPersist_doesNotPropagate() {
        doThrow(new RuntimeException("DB failure")).when(systemLogRepository).persist(any(SystemLog.class));

        assertThatCode(() ->
                systemLoggingService.logInfo("Test", "msg", "details")
        ).doesNotThrowAnyException();
    }

    @Test
    @DisplayName("logDebug ne propage pas une exception levée par le repository")
    void logDebug_exceptionInPersist_doesNotPropagate() {
        doThrow(new RuntimeException("DB failure")).when(systemLogRepository).persist(any(SystemLog.class));

        assertThatCode(() ->
                systemLoggingService.logDebug("Test", "msg", "details")
        ).doesNotThrowAnyException();
    }

    @Test
    @DisplayName("logRequest avec statusCode null → niveau INFO")
    void logRequest_statusCodeNull_createsInfoLog() {
        ArgumentCaptor<SystemLog> captor = ArgumentCaptor.forClass(SystemLog.class);
        doNothing().when(systemLogRepository).persist(captor.capture());

        systemLoggingService.logRequest("GET", "/api/test", null, null, null, null, 10L);

        assertThat(captor.getValue().getLevel()).isEqualTo("INFO");
    }
}
