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.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/**
 * Tests dédiés à la couverture des branches L317, L333, L349 dans
 * {@link SystemMetricsService} :
 *
 * <pre>
 *   private Integer getDbConnectionPoolSize() {
 *       if (dataSource instanceof AgroalDataSource agroalDataSource) { ... }
 *       return 0;   // L317 — branche false (DataSource n'est pas AgroalDataSource)
 *   }
 *
 *   private Integer getDbActiveConnections() {
 *       if (dataSource instanceof AgroalDataSource agroalDataSource) { ... }
 *       return 0;   // L333
 *   }
 *
 *   private Integer getDbIdleConnections() {
 *       if (dataSource instanceof AgroalDataSource agroalDataSource) { ... }
 *       return 0;   // L349
 *   }
 * </pre>
 *
 * <p>Cette classe isole complètement le test en n'injectant que {@code @InjectMock DataSource}
 * (pas {@code AgroalDataSource}).  Quarkus remplace alors le bean {@code DataSource} par un mock
 * Mockito ordinaire qui N'EST PAS une instance d'{@code AgroalDataSource}, ce qui force
 * l'évaluation de la branche {@code instanceof} à {@code false} et l'exécution des
 * {@code return 0} aux lignes 317, 333 et 349.
 */
@QuarkusTest
@DisplayName("SystemMetricsService — branches return 0 quand DataSource n'est pas AgroalDataSource")
class SystemMetricsServiceNonAgroalBranchTest {

    @Inject
    SystemMetricsService systemMetricsService;

    /**
     * Mock ORDINAIRE de {@code DataSource} — intentionnellement PAS {@code AgroalDataSource}.
     * Le test est isolé d'AgroalTest pour éviter tout conflit de context Quarkus.
     */
    @InjectMock
    DataSource dataSource;

    @InjectMock
    MembreRepository membreRepository;

    @BeforeEach
    void setupDefaultMocks() throws SQLException {
        // Connexion valide pour isDatabaseHealthy()
        Connection conn = mock(Connection.class);
        when(conn.isValid(5)).thenReturn(true);
        when(dataSource.getConnection()).thenReturn(conn);

        // Comptes membres
        when(membreRepository.count(anyString(), (Object[]) any())).thenReturn(2L);
        when(membreRepository.count()).thenReturn(8L);
    }

    // =========================================================================
    // L317 — getDbConnectionPoolSize() : instanceof false → return 0
    // =========================================================================

    @Test
    @DisplayName("getDbConnectionPoolSize retourne 0 (L317) quand le DataSource mocké n'est pas AgroalDataSource")
    void getDbConnectionPoolSize_notAgroal_returnsZero_L317() {
        SystemMetricsResponse response = systemMetricsService.getSystemMetrics();

        // Le mock DataSource ordinaire ne satisfait pas `instanceof AgroalDataSource`
        // → branche false → return 0; (ligne 317)
        assertThat(response.getDbConnectionPoolSize())
                .as("L317 doit retourner 0 quand DataSource n'est pas AgroalDataSource")
                .isEqualTo(0);
    }

    // =========================================================================
    // L333 — getDbActiveConnections() : instanceof false → return 0
    // =========================================================================

    @Test
    @DisplayName("getDbActiveConnections retourne 0 (L333) quand le DataSource mocké n'est pas AgroalDataSource")
    void getDbActiveConnections_notAgroal_returnsZero_L333() {
        SystemMetricsResponse response = systemMetricsService.getSystemMetrics();

        // Le mock DataSource ordinaire ne satisfait pas `instanceof AgroalDataSource`
        // → branche false → return 0; (ligne 333)
        assertThat(response.getDbActiveConnections())
                .as("L333 doit retourner 0 quand DataSource n'est pas AgroalDataSource")
                .isEqualTo(0);
    }

    // =========================================================================
    // L349 — getDbIdleConnections() : instanceof false → return 0
    // =========================================================================

    @Test
    @DisplayName("getDbIdleConnections retourne 0 (L349) quand le DataSource mocké n'est pas AgroalDataSource")
    void getDbIdleConnections_notAgroal_returnsZero_L349() {
        SystemMetricsResponse response = systemMetricsService.getSystemMetrics();

        // Le mock DataSource ordinaire ne satisfait pas `instanceof AgroalDataSource`
        // → branche false → return 0; (ligne 349)
        assertThat(response.getDbIdleConnections())
                .as("L349 doit retourner 0 quand DataSource n'est pas AgroalDataSource")
                .isEqualTo(0);
    }

    // =========================================================================
    // Test consolidé — les trois branches en un seul appel
    // =========================================================================

    @Test
    @DisplayName("Les trois métriques DB (poolSize, activeConnections, idleConnections) sont 0 (L317+L333+L349)")
    void getSystemMetrics_allDbPoolMetrics_zeroWhenNotAgroal_L317_L333_L349() {
        SystemMetricsResponse response = systemMetricsService.getSystemMetrics();

        assertThat(response.getDbConnectionPoolSize())
                .as("dbConnectionPoolSize — branche L317 (instanceof false)")
                .isEqualTo(0);
        assertThat(response.getDbActiveConnections())
                .as("dbActiveConnections — branche L333 (instanceof false)")
                .isEqualTo(0);
        assertThat(response.getDbIdleConnections())
                .as("dbIdleConnections — branche L349 (instanceof false)")
                .isEqualTo(0);
    }

    // =========================================================================
    // L168 — getCpuUsage() : branche loadAvg >= 0 → return Math.min(100.0, ...)
    // Ce test n'est couvert que sur Linux/Mac (loadAvg >= 0).
    // Sur Windows, getSystemLoadAverage() retourne -1 → branche if (loadAvg < 0) = true.
    // Le test ci-dessous documente et vérifie le comportement quel que soit l'OS.
    // =========================================================================

    @Test
    @DisplayName("getCpuUsage retourne une valeur dans [0.0, 100.0] (L168 couvert sur Linux où loadAvg >= 0)")
    void getCpuUsage_loadAvgPositive_returnsValueInRange_L168() {
        SystemMetricsResponse response = systemMetricsService.getSystemMetrics();

        // Sur Linux (CI), loadAvg >= 0 → L163 évalue false → exécute L168
        // Sur Windows (dev), loadAvg = -1 → L163 évalue true → retourne 0.0
        // Dans les deux cas, la valeur doit être dans [0.0, 100.0]
        assertThat(response.getCpuUsagePercent())
                .as("cpuUsagePercent doit être dans [0.0, 100.0]")
                .isNotNull()
                .isGreaterThanOrEqualTo(0.0)
                .isLessThanOrEqualTo(100.0);
    }
}
