package dev.lions.unionflow.server.service;

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.MockedStatic;
import org.mockito.Mockito;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.OperatingSystemMXBean;
import java.lang.reflect.Method;

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

/**
 * Tests de couverture complémentaires pour LogsMonitoringService.
 * Couvre les branches de formatUptime() via réflexion.
 */
@QuarkusTest
class LogsMonitoringServiceCoverageTest {

    @Inject
    LogsMonitoringService logsMonitoringService;

    /**
     * Invoque la méthode privée formatUptime(long) via réflexion.
     */
    private String invokeFormatUptime(long uptimeMs) throws Exception {
        Method method = LogsMonitoringService.class.getDeclaredMethod("formatUptime", long.class);
        method.setAccessible(true);
        return (String) method.invoke(logsMonitoringService, uptimeMs);
    }

    @Test
    @DisplayName("formatUptime - minutes seulement (< 1 heure)")
    void formatUptime_minutesOnly() throws Exception {
        // 15 minutes = 15 * 60 * 1000 = 900_000 ms
        String result = invokeFormatUptime(900_000L);
        assertThat(result).isEqualTo("15m");
    }

    @Test
    @DisplayName("formatUptime - heures et minutes (>= 1 heure, < 1 jour)")
    void formatUptime_hoursAndMinutes() throws Exception {
        // 2h 30m = 2*3600*1000 + 30*60*1000 = 7_200_000 + 1_800_000 = 9_000_000 ms
        String result = invokeFormatUptime(9_000_000L);
        assertThat(result).isEqualTo("2h 30m");
    }

    @Test
    @DisplayName("formatUptime - jours, heures et minutes (>= 1 jour)")
    void formatUptime_daysHoursMinutes() throws Exception {
        // 1j 3h 15m = 1*24*3600*1000 + 3*3600*1000 + 15*60*1000
        //           = 86_400_000 + 10_800_000 + 900_000 = 98_100_000 ms
        String result = invokeFormatUptime(98_100_000L);
        assertThat(result).isEqualTo("1j 3h 15m");
    }

    @Test
    @DisplayName("formatUptime - exactement 1 heure")
    void formatUptime_exactlyOneHour() throws Exception {
        // 1h 0m = 3_600_000 ms
        String result = invokeFormatUptime(3_600_000L);
        assertThat(result).isEqualTo("1h 0m");
    }

    @Test
    @DisplayName("formatUptime - exactement 0 ms → 0 minutes")
    void formatUptime_zero() throws Exception {
        String result = invokeFormatUptime(0L);
        assertThat(result).isEqualTo("0m");
    }

    @Test
    @DisplayName("formatUptime - ne lève pas d'exception")
    void formatUptime_doesNotThrow() {
        assertThatCode(() -> invokeFormatUptime(123_456_789L)).doesNotThrowAnyException();
    }

    // =========================================================================
    // getSystemMetrics — branches JMX dépendantes de la plateforme
    // =========================================================================

    @Test
    @DisplayName("getSystemMetrics — systemLoadAverage > 0 → branche true L101 (Math.min(100, load*10))")
    void getSystemMetrics_systemLoadAveragePositif_brancheTrueL101() {
        // Couvre la branche true de : osBean.getSystemLoadAverage() > 0 ? Math.min(100, load*10) : random
        // Windows retourne -1 → cette branche n'est jamais prise sur Windows sans mock
        OperatingSystemMXBean mockOsBean = mock(OperatingSystemMXBean.class);
        when(mockOsBean.getSystemLoadAverage()).thenReturn(2.0); // > 0 → branche true

        MemoryMXBean mockMemoryBean = mock(MemoryMXBean.class);
        MemoryUsage mockMemoryUsage = mock(MemoryUsage.class);
        when(mockMemoryUsage.getMax()).thenReturn(1024L * 1024L * 512L); // 512MB > 0 → branche true L107
        when(mockMemoryUsage.getUsed()).thenReturn(1024L * 1024L * 128L); // 128MB
        when(mockMemoryBean.getHeapMemoryUsage()).thenReturn(mockMemoryUsage);

        try (MockedStatic<ManagementFactory> mf = Mockito.mockStatic(ManagementFactory.class)) {
            mf.when(ManagementFactory::getOperatingSystemMXBean).thenReturn(mockOsBean);
            mf.when(ManagementFactory::getMemoryMXBean).thenReturn(mockMemoryBean);

            var metrics = logsMonitoringService.getSystemMetrics();

            assertThat(metrics).isNotNull();
            // load = 2.0 > 0 → cpuLoad = Math.min(100, 2.0 * 10) = 20.0
            assertThat(metrics.getCpuUsagePercent()).isEqualTo(20.0);
        }
    }

    @Test
    @DisplayName("getSystemMetrics — maxMemory <= 0 → branche false L107 (67.2)")
    void getSystemMetrics_maxMemoryZero_brancheFalseL107() {
        // Couvre la branche false de : maxMemory > 0 ? ... : 67.2
        // Sur un vrai JVM maxMemory est toujours > 0 → cette branche est structurellement inatteignable
        // sans mock
        OperatingSystemMXBean mockOsBean = mock(OperatingSystemMXBean.class);
        when(mockOsBean.getSystemLoadAverage()).thenReturn(-1.0); // <= 0 → branche false L101 (déjà couverte)

        MemoryMXBean mockMemoryBean = mock(MemoryMXBean.class);
        MemoryUsage mockMemoryUsage = mock(MemoryUsage.class);
        when(mockMemoryUsage.getMax()).thenReturn(-1L); // <= 0 → branche false L107 → 67.2
        when(mockMemoryUsage.getUsed()).thenReturn(0L);
        when(mockMemoryBean.getHeapMemoryUsage()).thenReturn(mockMemoryUsage);

        try (MockedStatic<ManagementFactory> mf = Mockito.mockStatic(ManagementFactory.class)) {
            mf.when(ManagementFactory::getOperatingSystemMXBean).thenReturn(mockOsBean);
            mf.when(ManagementFactory::getMemoryMXBean).thenReturn(mockMemoryBean);

            var metrics = logsMonitoringService.getSystemMetrics();

            assertThat(metrics).isNotNull();
            // maxMemory = -1 → memoryUsage = 67.2
            assertThat(metrics.getMemoryUsagePercent()).isEqualTo(67.2);
        }
    }
}
