package dev.lions.unionflow.server.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.api.dto.logs.response.SystemMetricsResponse;
import dev.lions.unionflow.server.repository.MembreRepository;
import io.agroal.api.AgroalDataSource;
import io.agroal.api.AgroalDataSourceMetrics;
import io.agroal.api.configuration.AgroalConnectionPoolConfiguration;
import io.agroal.api.configuration.AgroalDataSourceConfiguration;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import java.sql.Connection;
import java.sql.SQLException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests pour {@link SystemMetricsService} avec un mock AgroalDataSource.
 *
 * <p>Ce test couvre les branches {@code if (dataSource instanceof AgroalDataSource)} dans
 * getDbConnectionPoolSize(), getDbActiveConnections() et getDbIdleConnections() — branches
 * inaccessibles avec un mock {@code DataSource} ordinaire.
 */
@QuarkusTest
class SystemMetricsServiceAgroalTest {

    @Inject
    SystemMetricsService systemMetricsService;

    @InjectMock
    AgroalDataSource agroalDataSource;

    @InjectMock
    MembreRepository membreRepository;

    @BeforeEach
    void setupDefaultMocks() throws SQLException {
        Connection conn = mock(Connection.class);
        when(conn.isValid(5)).thenReturn(true);
        when(agroalDataSource.getConnection()).thenReturn(conn);

        when(membreRepository.count(anyString(), (Object[]) any())).thenReturn(5L);
        when(membreRepository.count()).thenReturn(10L);
    }

    // =========================================================================
    // Chemin principal : AgroalDataSource présent, configuration et métriques OK
    // =========================================================================

    @Test
    @DisplayName("getSystemMetrics — DB pool correctement renseigné quand AgroalDataSource est mocké")
    void getSystemMetrics_dbPoolFields_populatedWithAgroalDataSource() {
        AgroalConnectionPoolConfiguration poolConfig = mock(AgroalConnectionPoolConfiguration.class);
        when(poolConfig.maxSize()).thenReturn(10);

        AgroalDataSourceConfiguration config = mock(AgroalDataSourceConfiguration.class);
        when(config.connectionPoolConfiguration()).thenReturn(poolConfig);
        when(agroalDataSource.getConfiguration()).thenReturn(config);

        AgroalDataSourceMetrics metrics = mock(AgroalDataSourceMetrics.class);
        when(metrics.activeCount()).thenReturn(3L);
        when(metrics.availableCount()).thenReturn(7L);
        when(agroalDataSource.getMetrics()).thenReturn(metrics);

        SystemMetricsResponse response = systemMetricsService.getSystemMetrics();

        assertThat(response.getDbConnectionPoolSize()).isEqualTo(10);
        assertThat(response.getDbActiveConnections()).isEqualTo(3);
        assertThat(response.getDbIdleConnections()).isEqualTo(7);
    }

    // =========================================================================
    // Branche catch dans getDbConnectionPoolSize : getConfiguration() lève une exception
    // Couvre aussi : getMetrics() retourne null → if (metrics == null) return 0
    // =========================================================================

    @Test
    @DisplayName("getDbConnectionPoolSize — retourne 0 quand getConfiguration() lève une exception")
    void getDbConnectionPoolSize_returnsZero_whenConfigurationThrows() {
        when(agroalDataSource.getConfiguration()).thenThrow(new RuntimeException("config error"));
        // getMetrics() non stubbé → retourne null → couvre if (metrics == null) return 0

        SystemMetricsResponse response = systemMetricsService.getSystemMetrics();

        assertThat(response.getDbConnectionPoolSize()).isEqualTo(0);
        assertThat(response.getDbActiveConnections()).isEqualTo(0);
        assertThat(response.getDbIdleConnections()).isEqualTo(0);
    }

    // =========================================================================
    // Branche catch dans getDbActiveConnections et getDbIdleConnections
    // =========================================================================

    @Test
    @DisplayName("getDbActiveConnections et getDbIdleConnections — retournent 0 quand getMetrics() lève une exception")
    void getDbConnectionMetrics_returnZero_whenMetricsThrows() {
        AgroalConnectionPoolConfiguration poolConfig = mock(AgroalConnectionPoolConfiguration.class);
        when(poolConfig.maxSize()).thenReturn(5);

        AgroalDataSourceConfiguration config = mock(AgroalDataSourceConfiguration.class);
        when(config.connectionPoolConfiguration()).thenReturn(poolConfig);
        when(agroalDataSource.getConfiguration()).thenReturn(config);

        when(agroalDataSource.getMetrics()).thenThrow(new RuntimeException("metrics error"));

        SystemMetricsResponse response = systemMetricsService.getSystemMetrics();

        assertThat(response.getDbConnectionPoolSize()).isEqualTo(5);
        assertThat(response.getDbActiveConnections()).isEqualTo(0);
        assertThat(response.getDbIdleConnections()).isEqualTo(0);
    }
}
