package dev.lions.user.manager.client.view;

import dev.lions.user.manager.client.service.AuditServiceClient;
import dev.lions.user.manager.client.service.RealmServiceClient;
import dev.lions.user.manager.dto.audit.AuditLogDTO;
import dev.lions.user.manager.enums.audit.TypeActionAudit;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
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;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class AuditConsultationBeanTest {

    @Mock
    AuditServiceClient auditServiceClient;

    @Mock
    RealmServiceClient realmServiceClient;

    @Mock
    FacesContext facesContext;

    @InjectMocks
    AuditConsultationBean auditConsultationBean;

    MockedStatic<FacesContext> facesContextMock;

    @BeforeEach
    void setUp() {
        facesContextMock = mockStatic(FacesContext.class);
        facesContextMock.when(FacesContext::getCurrentInstance).thenReturn(facesContext);
        // Par défaut, aucun message n'est ajouté, les tests configureront si besoin
    }

    @AfterEach
    void tearDown() {
        facesContextMock.close();
    }

    @Test
    void testInit() {
        Map<TypeActionAudit, Long> actionStats = Map.of(TypeActionAudit.USER_CREATE, 10L);
        Map<String, Long> userStats = Map.of("admin", 5L);

        dev.lions.user.manager.dto.common.CountDTO failureDto = new dev.lions.user.manager.dto.common.CountDTO(2L);
        dev.lions.user.manager.dto.common.CountDTO successDto = new dev.lions.user.manager.dto.common.CountDTO(8L);

        when(auditServiceClient.getActionStatistics(isNull(), isNull())).thenReturn(actionStats);
        when(auditServiceClient.getUserActivityStatistics(isNull(), isNull())).thenReturn(userStats);
        when(auditServiceClient.getFailureCount(isNull(), isNull())).thenReturn(failureDto);
        when(auditServiceClient.getSuccessCount(isNull(), isNull())).thenReturn(successDto);
        when(realmServiceClient.getAllRealms()).thenReturn(java.util.List.of("master"));

        auditConsultationBean.init();

        assertNotNull(auditConsultationBean.getAuditLogs());
        assertNotNull(auditConsultationBean.getActionStatistics());
        assertNotNull(auditConsultationBean.getUserActivityStatistics());
    }

    @Test
    void testSearchLogs() {
        List<AuditLogDTO> logs = Collections.singletonList(
                AuditLogDTO.builder()
                        .acteurUsername("admin")
                        .typeAction(TypeActionAudit.USER_CREATE)
                        .build());

        when(auditServiceClient.searchLogs(
                nullable(String.class), nullable(String.class), nullable(String.class),
                nullable(TypeActionAudit.class), nullable(String.class), nullable(Boolean.class),
                anyInt(), anyInt()))
                .thenReturn(logs);

        auditConsultationBean.setActeurUsername("admin");
        auditConsultationBean.searchLogs();

        assertFalse(auditConsultationBean.getAuditLogs().isEmpty());
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testSearchLogsError() {
        when(auditServiceClient.searchLogs(
                anyString(), anyString(), anyString(), any(), anyString(), anyBoolean(),
                anyInt(), anyInt()))
                .thenThrow(new RuntimeException("Error"));

        auditConsultationBean.searchLogs();

        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testLoadLogsByActeur() {
        List<AuditLogDTO> logs = Collections.singletonList(
                AuditLogDTO.builder().acteurUsername("admin").build());

        when(auditServiceClient.getLogsByActor("admin", 100)).thenReturn(logs);

        auditConsultationBean.loadLogsByActeur("admin");

        assertFalse(auditConsultationBean.getAuditLogs().isEmpty());
        assertEquals(1, auditConsultationBean.getTotalRecords());
    }

    @Test
    void testLoadLogsByActeurError() {
        when(auditServiceClient.getLogsByActor("admin", 100))
                .thenThrow(new RuntimeException("Error"));

        auditConsultationBean.loadLogsByActeur("admin");

        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testLoadLogsByRealm() {
        // La méthode loadLogsByRealm est maintenant un no-op loggant un avertissement
        auditConsultationBean.loadLogsByRealm("master");
        assertTrue(auditConsultationBean.getAuditLogs().isEmpty());
    }

    @Test
    void testLoadLogsByRealmError() {
        // No-op, no exception expected
        auditConsultationBean.loadLogsByRealm("master");
    }

    @Test
    void testLoadStatistics() {
        Map<TypeActionAudit, Long> actionStats = Map.of(TypeActionAudit.USER_CREATE, 10L);
        Map<String, Long> userStats = Map.of("admin", 5L);

        dev.lions.user.manager.dto.common.CountDTO failureDto = new dev.lions.user.manager.dto.common.CountDTO(2L);
        dev.lions.user.manager.dto.common.CountDTO successDto = new dev.lions.user.manager.dto.common.CountDTO(8L);

        when(auditServiceClient.getActionStatistics(anyString(), anyString())).thenReturn(actionStats);
        when(auditServiceClient.getUserActivityStatistics(anyString(), anyString())).thenReturn(userStats);
        when(auditServiceClient.getFailureCount(anyString(), anyString())).thenReturn(failureDto);
        when(auditServiceClient.getSuccessCount(anyString(), anyString())).thenReturn(successDto);

        auditConsultationBean.setDateDebut(LocalDateTime.now().minusDays(7));
        auditConsultationBean.setDateFin(LocalDateTime.now());
        auditConsultationBean.loadStatistics();

        assertNotNull(auditConsultationBean.getActionStatistics());
        assertNotNull(auditConsultationBean.getUserActivityStatistics());
        assertEquals(2L, auditConsultationBean.getFailureCount());
        assertEquals(8L, auditConsultationBean.getSuccessCount());
    }

    @Test
    void testLoadStatisticsError() {
        when(auditServiceClient.getActionStatistics(anyString(), anyString()))
                .thenThrow(new RuntimeException("Error"));

        auditConsultationBean.loadStatistics();

        // L'erreur est loggée mais ne doit pas planter
        assertNotNull(auditConsultationBean);
    }

    @Test
    void testExportToCSV() {
        jakarta.ws.rs.core.Response response = mock(jakarta.ws.rs.core.Response.class);
        when(auditServiceClient.exportLogsToCSV(anyString(), anyString()))
                .thenReturn(response);

        // Contenu CSV simulé
        when(response.readEntity(String.class)).thenReturn("col1,col2\nv1,v2");
        when(response.getHeaderString("Content-Disposition"))
                .thenReturn("attachment; filename=\"audit-logs-test.csv\"");

        // Mock de l'ExternalContext et du flux de sortie
        jakarta.faces.context.ExternalContext externalContext = mock(jakarta.faces.context.ExternalContext.class);
        java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream();
        try {
            when(externalContext.getResponseOutputStream()).thenReturn(baos);
        } catch (java.io.IOException e) {
            throw new RuntimeException(e);
        }
        when(facesContext.getExternalContext()).thenReturn(externalContext);

        auditConsultationBean.setDateDebut(LocalDateTime.now().minusDays(7));
        auditConsultationBean.setDateFin(LocalDateTime.now());
        auditConsultationBean.exportToCSV();

        verify(auditServiceClient).exportLogsToCSV(anyString(), anyString());
        verify(externalContext).setResponseContentType(startsWith("text/csv"));
        verify(externalContext).setResponseHeader(eq("Content-Disposition"), contains("audit-logs-test.csv"));
        verify(facesContext).responseComplete();
    }

    @Test
    void testExportToCSVError() {
        when(auditServiceClient.exportLogsToCSV(anyString(), anyString()))
                .thenThrow(new RuntimeException("Error"));

        auditConsultationBean.exportToCSV();

        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testResetFilters() {
        auditConsultationBean.setActeurUsername("admin");
        auditConsultationBean.setDateDebut(LocalDateTime.now());
        auditConsultationBean.setDateFin(LocalDateTime.now());
        auditConsultationBean.setSelectedTypeAction(TypeActionAudit.USER_CREATE);
        auditConsultationBean.setRessourceType("USER");
        auditConsultationBean.setSucces(true);
        auditConsultationBean.setCurrentPage(2);
        auditConsultationBean.getAuditLogs().add(AuditLogDTO.builder().build());

        auditConsultationBean.resetFilters();

        assertNull(auditConsultationBean.getActeurUsername());
        assertNull(auditConsultationBean.getDateDebut());
        assertNull(auditConsultationBean.getDateFin());
        assertNull(auditConsultationBean.getSelectedTypeAction());
        assertNull(auditConsultationBean.getRessourceType());
        assertNull(auditConsultationBean.getSucces());
        assertEquals(0, auditConsultationBean.getCurrentPage());
        assertTrue(auditConsultationBean.getAuditLogs().isEmpty());
    }

    @Test
    void testPreviousPage() {
        auditConsultationBean.setCurrentPage(2);
        List<AuditLogDTO> logs = Collections.singletonList(AuditLogDTO.builder().build());
        when(auditServiceClient.searchLogs(
                anyString(), anyString(), anyString(), any(), anyString(), anyBoolean(),
                anyInt(), anyInt()))
                .thenReturn(logs);

        auditConsultationBean.previousPage();

        assertEquals(1, auditConsultationBean.getCurrentPage());
    }

    @Test
    void testPreviousPageAtFirstPage() {
        auditConsultationBean.setCurrentPage(0);

        auditConsultationBean.previousPage();

        assertEquals(0, auditConsultationBean.getCurrentPage());
        verify(auditServiceClient, never()).searchLogs(
                anyString(), anyString(), anyString(), any(), anyString(), anyBoolean(),
                anyInt(), anyInt());
    }

    @Test
    void testNextPage() {
        List<AuditLogDTO> logs = Collections.singletonList(AuditLogDTO.builder().build());
        when(auditServiceClient.searchLogs(
                anyString(), anyString(), anyString(), any(), anyString(), anyBoolean(),
                anyInt(), anyInt()))
                .thenReturn(logs);

        auditConsultationBean.setCurrentPage(0);
        auditConsultationBean.nextPage();

        assertEquals(1, auditConsultationBean.getCurrentPage());
    }
}
