package dev.lions.user.manager.resource;

import dev.lions.user.manager.dto.audit.AuditLogDTO;
import dev.lions.user.manager.enums.audit.TypeActionAudit;
import dev.lions.user.manager.service.AuditService;
import jakarta.ws.rs.core.Response;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
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 AuditResourceTest {

    @Mock
    AuditService auditService;

    @InjectMocks
    AuditResource auditResource;

    @Test
    void testSearchLogs() {
        List<AuditLogDTO> logs = Collections.singletonList(
                AuditLogDTO.builder().acteurUsername("admin").typeAction(TypeActionAudit.USER_CREATE).build());
        when(auditService.findByActeur(eq("admin"), isNull(), isNull(), eq(0), eq(50))).thenReturn(logs);

        Response response = auditResource.searchLogs("admin", null, null, null, null, null, 0, 50);

        assertEquals(200, response.getStatus());
        assertEquals(logs, response.getEntity());
    }

    @Test
    void testSearchLogsWithDates() {
        List<AuditLogDTO> logs = Collections.emptyList();
        when(auditService.findByRealm(eq("master"), any(), any(), eq(0), eq(50))).thenReturn(logs);

        Response response = auditResource.searchLogs(null, "2024-01-01T00:00:00", "2024-12-31T23:59:59",
                TypeActionAudit.USER_CREATE, null, true, 0, 50);

        assertEquals(200, response.getStatus());
    }

    @Test
    void testSearchLogsError() {
        when(auditService.findByRealm(eq("master"), isNull(), isNull(), eq(0), eq(50)))
                .thenThrow(new RuntimeException("Error"));

        Response response = auditResource.searchLogs(null, null, null, null, null, null, 0, 50);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetLogsByActor() {
        List<AuditLogDTO> logs = Collections.singletonList(
                AuditLogDTO.builder().acteurUsername("admin").build());
        when(auditService.findByActeur(eq("admin"), isNull(), isNull(), eq(0), eq(100))).thenReturn(logs);

        Response response = auditResource.getLogsByActor("admin", 100);

        assertEquals(200, response.getStatus());
        assertEquals(logs, response.getEntity());
    }

    @Test
    void testGetLogsByActorError() {
        when(auditService.findByActeur(eq("admin"), isNull(), isNull(), eq(0), eq(100)))
                .thenThrow(new RuntimeException("Error"));

        Response response = auditResource.getLogsByActor("admin", 100);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetLogsByResource() {
        List<AuditLogDTO> logs = Collections.emptyList();
        when(auditService.findByRessource(eq("USER"), eq("1"), isNull(), isNull(), eq(0), eq(100)))
                .thenReturn(logs);

        Response response = auditResource.getLogsByResource("USER", "1", 100);

        assertEquals(200, response.getStatus());
        assertEquals(logs, response.getEntity());
    }

    @Test
    void testGetLogsByResourceError() {
        when(auditService.findByRessource(eq("USER"), eq("1"), isNull(), isNull(), eq(0), eq(100)))
                .thenThrow(new RuntimeException("Error"));

        Response response = auditResource.getLogsByResource("USER", "1", 100);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetLogsByAction() {
        List<AuditLogDTO> logs = Collections.emptyList();
        when(auditService.findByTypeAction(eq(TypeActionAudit.USER_CREATE), eq("master"), isNull(), isNull(), eq(0), eq(100)))
                .thenReturn(logs);

        Response response = auditResource.getLogsByAction(TypeActionAudit.USER_CREATE, null, null, 100);

        assertEquals(200, response.getStatus());
    }

    @Test
    void testGetLogsByActionWithDates() {
        List<AuditLogDTO> logs = Collections.emptyList();
        when(auditService.findByTypeAction(eq(TypeActionAudit.USER_UPDATE), eq("master"), any(), any(), eq(0), eq(50)))
                .thenReturn(logs);

        Response response = auditResource.getLogsByAction(TypeActionAudit.USER_UPDATE,
                "2024-01-01T00:00:00", "2024-12-31T23:59:59", 50);

        assertEquals(200, response.getStatus());
    }

    @Test
    void testGetLogsByActionError() {
        when(auditService.findByTypeAction(any(), eq("master"), any(), any(), anyInt(), anyInt()))
                .thenThrow(new RuntimeException("Error"));

        Response response = auditResource.getLogsByAction(TypeActionAudit.USER_CREATE, null, null, 100);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetActionStatistics() {
        Map<TypeActionAudit, Long> stats = Map.of(TypeActionAudit.USER_CREATE, 10L);
        when(auditService.countByActionType(eq("master"), isNull(), isNull())).thenReturn(stats);

        Response response = auditResource.getActionStatistics(null, null);

        assertEquals(200, response.getStatus());
        assertEquals(stats, response.getEntity());
    }

    @Test
    void testGetActionStatisticsError() {
        when(auditService.countByActionType(eq("master"), any(), any())).thenThrow(new RuntimeException("Error"));

        Response response = auditResource.getActionStatistics(null, null);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetUserActivityStatistics() {
        Map<String, Long> stats = Map.of("admin", 100L);
        when(auditService.countByActeur(eq("master"), isNull(), isNull())).thenReturn(stats);

        Response response = auditResource.getUserActivityStatistics(null, null);

        assertEquals(200, response.getStatus());
        assertEquals(stats, response.getEntity());
    }

    @Test
    void testGetUserActivityStatisticsError() {
        when(auditService.countByActeur(eq("master"), any(), any())).thenThrow(new RuntimeException("Error"));

        Response response = auditResource.getUserActivityStatistics(null, null);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetFailureCount() {
        Map<String, Long> successVsFailure = Map.of("failure", 5L, "success", 100L);
        when(auditService.countSuccessVsFailure(eq("master"), isNull(), isNull())).thenReturn(successVsFailure);

        Response response = auditResource.getFailureCount(null, null);

        assertEquals(200, response.getStatus());
        assertNotNull(response.getEntity());
    }

    @Test
    void testGetFailureCountError() {
        when(auditService.countSuccessVsFailure(eq("master"), any(), any())).thenThrow(new RuntimeException("Error"));

        Response response = auditResource.getFailureCount(null, null);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetSuccessCount() {
        Map<String, Long> successVsFailure = Map.of("failure", 5L, "success", 100L);
        when(auditService.countSuccessVsFailure(eq("master"), isNull(), isNull())).thenReturn(successVsFailure);

        Response response = auditResource.getSuccessCount(null, null);

        assertEquals(200, response.getStatus());
        assertNotNull(response.getEntity());
    }

    @Test
    void testGetSuccessCountError() {
        when(auditService.countSuccessVsFailure(eq("master"), any(), any())).thenThrow(new RuntimeException("Error"));

        Response response = auditResource.getSuccessCount(null, null);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testExportLogsToCSV() {
        when(auditService.exportToCSV(eq("master"), isNull(), isNull())).thenReturn("csv,data");

        Response response = auditResource.exportLogsToCSV(null, null);

        assertEquals(200, response.getStatus());
    }

    @Test
    void testExportLogsToCSVError() {
        when(auditService.exportToCSV(eq("master"), any(), any())).thenThrow(new RuntimeException("Error"));

        Response response = auditResource.exportLogsToCSV(null, null);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testPurgeOldLogs() {
        when(auditService.purgeOldLogs(any())).thenReturn(50L);

        Response response = auditResource.purgeOldLogs(90);

        assertEquals(204, response.getStatus());
    }

    @Test
    void testPurgeOldLogsError() {
        when(auditService.purgeOldLogs(any())).thenThrow(new RuntimeException("Error"));

        Response response = auditResource.purgeOldLogs(90);

        assertEquals(500, response.getStatus());
    }

    // ============== Inner Class Tests ==============

    @Test
    void testCountResponseClass() {
        AuditResource.CountResponse response = new AuditResource.CountResponse(42);
        assertEquals(42, response.count);
    }

    @Test
    void testErrorResponseClass() {
        AuditResource.ErrorResponse response = new AuditResource.ErrorResponse("Error message");
        assertEquals("Error message", response.message);
    }
}
