package dev.lions.unionflow.server.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.api.dto.logs.response.SystemMetricsResponse;
import dev.lions.unionflow.server.repository.MembreRepository;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
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;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 * Tests Mockito PURS (sans CDI Quarkus) pour les branches L317, L333, L349 et L168 de
 * {@link SystemMetricsService}.
 *
 * <p>Problème avec les tests Quarkus (@QuarkusTest + @InjectMock DataSource) : Quarkus génère un
 * proxy CDI pour le bean {@code AgroalDataSource}, et ce proxy implémente {@code AgroalDataSource}.
 * Ainsi {@code dataSource instanceof AgroalDataSource} est TOUJOURS {@code true}, même si le bean
 * mocké est déclaré comme {@code DataSource} simple. Les lignes L317, L333, L349 (branches
 * {@code return 0} quand instanceof est {@code false}) ne sont jamais atteintes.
 *
 * <p>Solution : utiliser {@code @ExtendWith(MockitoExtension.class)} + {@code @InjectMocks} qui
 * crée une instance JAVA directe de {@link SystemMetricsService} sans CDI proxy. Le mock
 * {@code DataSource} est un mock Mockito ordinaire qui N'EST PAS une instance d'{@code
 * AgroalDataSource} → {@code instanceof AgroalDataSource} évalue à {@code false} → L317/L333/L349
 * sont exécutés.
 *
 * <p>L168 : {@code getCpuUsage()} retourne {@code Math.min(100.0, ...)} quand {@code loadAvg >= 0}
 * → couvert via mockStatic sur {@link ManagementFactory}.
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("SystemMetricsService — branches L317/L333/L349/L168 (Mockito pur, sans CDI)")
class SystemMetricsServiceUnitTest {

    /** DataSource Mockito ordinaire — PAS AgroalDataSource → instanceof AgroalDataSource = false */
    @Mock
    DataSource dataSource;

    @Mock
    MembreRepository membreRepository;

    @InjectMocks
    SystemMetricsService service;

    @BeforeEach
    void setup() throws SQLException {
        // dataSource.getConnection() pour isDatabaseHealthy()
        Connection conn = mock(Connection.class);
        when(conn.isValid(5)).thenReturn(true);
        when(dataSource.getConnection()).thenReturn(conn);
        // membreRepository.count() retourne 0L par défaut (Mockito) — pas besoin de stub
    }

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

    @Test
    @DisplayName("L317 : getDbConnectionPoolSize() retourne 0 quand DataSource n'est pas AgroalDataSource")
    void getDbConnectionPoolSize_dataSourceNotAgroal_returnsZero_L317() {
        SystemMetricsResponse response = service.getSystemMetrics();

        assertThat(response.getDbConnectionPoolSize())
                .as("L317 : dataSource n'est pas AgroalDataSource → return 0")
                .isEqualTo(0);
    }

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

    @Test
    @DisplayName("L333 : getDbActiveConnections() retourne 0 quand DataSource n'est pas AgroalDataSource")
    void getDbActiveConnections_dataSourceNotAgroal_returnsZero_L333() {
        SystemMetricsResponse response = service.getSystemMetrics();

        assertThat(response.getDbActiveConnections())
                .as("L333 : dataSource n'est pas AgroalDataSource → return 0")
                .isEqualTo(0);
    }

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

    @Test
    @DisplayName("L349 : getDbIdleConnections() retourne 0 quand DataSource n'est pas AgroalDataSource")
    void getDbIdleConnections_dataSourceNotAgroal_returnsZero_L349() {
        SystemMetricsResponse response = service.getSystemMetrics();

        assertThat(response.getDbIdleConnections())
                .as("L349 : dataSource n'est pas AgroalDataSource → return 0")
                .isEqualTo(0);
    }

    // =========================================================================
    // L168 — getCpuUsage() : loadAvg >= 0 → return Math.min(100.0, ...)
    // Sur Windows, getSystemLoadAverage() retourne -1 (non supporté).
    // On force loadAvg = 2.0 via mockStatic pour couvrir L168.
    // =========================================================================

    @Test
    @DisplayName("L168 : getCpuUsage() retourne Math.min(100.0, ...) quand loadAvg >= 0 (mockStatic ManagementFactory)")
    void getCpuUsage_loadAvgPositive_returnsMathMin_L168() throws SQLException {
        OperatingSystemMXBean osMock = mock(OperatingSystemMXBean.class);
        when(osMock.getSystemLoadAverage()).thenReturn(2.0);
        when(osMock.getAvailableProcessors()).thenReturn(4);

        try (MockedStatic<ManagementFactory> mf = mockStatic(ManagementFactory.class)) {
            mf.when(ManagementFactory::getOperatingSystemMXBean).thenReturn(osMock);

            SystemMetricsResponse response = service.getSystemMetrics();

            // loadAvg = 2.0, processors = 4 → (2.0/4)*100 = 50.0 → Math.min(100.0, 50.0) = 50.0
            assertThat(response.getCpuUsagePercent())
                    .as("L168 : loadAvg=2.0, processors=4 → cpuUsage = 50.0")
                    .isEqualTo(50.0);
        }
    }

    @Test
    @DisplayName("L163 branche true : getCpuUsage() retourne 0.0 quand loadAvg < 0 (mockStatic ManagementFactory)")
    void getCpuUsage_loadAvgNegative_returnsZero_L163() throws SQLException {
        OperatingSystemMXBean osMock = mock(OperatingSystemMXBean.class);
        when(osMock.getSystemLoadAverage()).thenReturn(-1.0);
        when(osMock.getAvailableProcessors()).thenReturn(4);

        try (MockedStatic<ManagementFactory> mf = mockStatic(ManagementFactory.class)) {
            mf.when(ManagementFactory::getOperatingSystemMXBean).thenReturn(osMock);

            SystemMetricsResponse response = service.getSystemMetrics();

            assertThat(response.getCpuUsagePercent())
                    .as("L163 branche true : loadAvg < 0 → return 0.0")
                    .isEqualTo(0.0);
        }
    }

    // =========================================================================
    // Test consolidé L317 + L333 + L349 en un seul appel
    // =========================================================================

    @Test
    @DisplayName("L317+L333+L349 : les trois métriques DB pool sont 0 quand DataSource n'est pas AgroalDataSource")
    void getSystemMetrics_allDbPoolMetrics_zeroWhenNotAgroal() {
        SystemMetricsResponse response = service.getSystemMetrics();

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