package dev.lions.user.manager.service.impl;

import dev.lions.user.manager.dto.audit.AuditLogDTO;
import dev.lions.user.manager.enums.audit.TypeActionAudit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

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

import static org.junit.jupiter.api.Assertions.*;

/**
 * Tests complets pour AuditServiceImpl pour atteindre 100% de couverture
 * Couvre les branches manquantes : auditEnabled=false, acteurUsername="*", dates null, etc.
 */
class AuditServiceImplCompleteTest {

    private AuditServiceImpl auditService;

    @BeforeEach
    void setUp() {
        auditService = new AuditServiceImpl();
        auditService.auditEnabled = true;
        auditService.logToDatabase = false;
    }

    @Test
    void testLogAction_AuditDisabled() {
        auditService.auditEnabled = false;
        
        AuditLogDTO auditLog = AuditLogDTO.builder()
            .typeAction(TypeActionAudit.USER_CREATE)
            .acteurUsername("admin")
            .build();

        AuditLogDTO result = auditService.logAction(auditLog);

        assertEquals(auditLog, result);
    }

    @Test
    void testLogAction_WithId() {
        AuditLogDTO auditLog = AuditLogDTO.builder()
            .id("custom-id")
            .typeAction(TypeActionAudit.USER_CREATE)
            .acteurUsername("admin")
            .build();

        AuditLogDTO result = auditService.logAction(auditLog);

        assertEquals("custom-id", result.getId());
    }

    @Test
    void testLogAction_WithDateAction() {
        LocalDateTime customDate = LocalDateTime.now().minusDays(1);
        AuditLogDTO auditLog = AuditLogDTO.builder()
            .typeAction(TypeActionAudit.USER_CREATE)
            .acteurUsername("admin")
            .dateAction(customDate)
            .build();

        AuditLogDTO result = auditService.logAction(auditLog);

        assertEquals(customDate, result.getDateAction());
    }

    @Test
    void testSearchLogs_WithWildcardActeur() {
        auditService.logSuccess(TypeActionAudit.USER_CREATE, "USER", "1", "user1", "realm1", "admin", "Created");
        auditService.logSuccess(TypeActionAudit.USER_UPDATE, "USER", "2", "user2", "realm1", "user2", "Updated");

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        // Test avec acteurUsername = "*" (wildcard)
        List<AuditLogDTO> logs = auditService.findByActeur("*", past, future, 0, 10);

        assertNotNull(logs);
        assertTrue(logs.size() >= 2);
    }

    @Test
    void testSearchLogs_WithNullDates() {
        auditService.logSuccess(TypeActionAudit.USER_CREATE, "USER", "1", "user1", "realm1", "admin", "Created");

        // Test avec dates null
        List<AuditLogDTO> logs = auditService.findByActeur("admin", null, null, 0, 10);

        assertNotNull(logs);
        assertTrue(logs.size() > 0);
    }

    @Test
    void testSearchLogs_WithNullTypeAction() {
        auditService.logSuccess(TypeActionAudit.USER_CREATE, "USER", "1", "user1", "realm1", "admin", "Created");
        auditService.logSuccess(TypeActionAudit.USER_UPDATE, "USER", "2", "user2", "realm1", "admin", "Updated");

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        // Test avec typeAction null (via findByRealm qui ne filtre pas par typeAction)
        List<AuditLogDTO> logs = auditService.findByRealm("realm1", past, future, 0, 10);

        assertNotNull(logs);
    }

    @Test
    void testSearchLogs_WithNullRessourceType() {
        auditService.logSuccess(TypeActionAudit.USER_CREATE, "USER", "1", "user1", "realm1", "admin", "Created");

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        // Test avec ressourceType null (via findByRealm)
        List<AuditLogDTO> logs = auditService.findByRealm("realm1", past, future, 0, 10);

        assertNotNull(logs);
    }

    @Test
    void testFindFailures() {
        auditService.logSuccess(TypeActionAudit.USER_CREATE, "USER", "1", "user1", "realm1", "admin", "Created");
        auditService.logFailure(TypeActionAudit.USER_CREATE, "USER", "2", "user2", "realm1", "admin", "Failed", "Error");

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        List<AuditLogDTO> failures = auditService.findFailures("realm1", past, future, 0, 10);

        assertNotNull(failures);
        assertTrue(failures.size() > 0);
        assertFalse(failures.get(0).isSuccessful());
    }

    @Test
    void testFindCriticalActions_UserDelete() {
        auditService.logSuccess(TypeActionAudit.USER_DELETE, "USER", "1", "user1", "realm1", "admin", "Deleted");

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        List<AuditLogDTO> critical = auditService.findCriticalActions("realm1", past, future, 0, 10);

        assertNotNull(critical);
        assertTrue(critical.size() > 0);
        assertEquals(TypeActionAudit.USER_DELETE, critical.get(0).getTypeAction());
    }

    @Test
    void testFindCriticalActions_RoleDelete() {
        auditService.logSuccess(TypeActionAudit.ROLE_DELETE, "ROLE", "1", "role1", "realm1", "admin", "Deleted");

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        List<AuditLogDTO> critical = auditService.findCriticalActions("realm1", past, future, 0, 10);

        assertNotNull(critical);
        assertTrue(critical.size() > 0);
        assertEquals(TypeActionAudit.ROLE_DELETE, critical.get(0).getTypeAction());
    }

    @Test
    void testFindCriticalActions_SessionRevokeAll() {
        auditService.logSuccess(TypeActionAudit.SESSION_REVOKE_ALL, "SESSION", "1", "session1", "realm1", "admin", "Revoked");

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        List<AuditLogDTO> critical = auditService.findCriticalActions("realm1", past, future, 0, 10);

        assertNotNull(critical);
        assertTrue(critical.size() > 0);
        assertEquals(TypeActionAudit.SESSION_REVOKE_ALL, critical.get(0).getTypeAction());
    }

    @Test
    void testFindCriticalActions_WithDateFilters() {
        LocalDateTime oldDate = LocalDateTime.now().minusDays(10);
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        // Créer un log ancien (hors de la plage)
        AuditLogDTO oldLog = AuditLogDTO.builder()
            .typeAction(TypeActionAudit.USER_DELETE)
            .acteurUsername("admin")
            .dateAction(oldDate)
            .build();
        auditService.logAction(oldLog);

        // Créer un log récent (dans la plage)
        auditService.logSuccess(TypeActionAudit.USER_DELETE, "USER", "2", "user2", "realm1", "admin", "Deleted");

        List<AuditLogDTO> critical = auditService.findCriticalActions("realm1", past, future, 0, 10);

        assertNotNull(critical);
        // Seul le log récent devrait être retourné
        assertTrue(critical.size() >= 1);
    }

    @Test
    void testGetAuditStatistics() {
        auditService.logSuccess(TypeActionAudit.USER_CREATE, "USER", "1", "user1", "realm1", "admin", "Created");
        auditService.logFailure(TypeActionAudit.USER_CREATE, "USER", "2", "user2", "realm1", "admin", "Failed", "Error");

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        Map<String, Object> stats = auditService.getAuditStatistics("realm1", past, future);

        assertNotNull(stats);
        assertTrue(stats.containsKey("total"));
        assertTrue(stats.containsKey("success"));
        assertTrue(stats.containsKey("failure"));
        assertTrue(stats.containsKey("byActionType"));
        assertTrue(stats.containsKey("byActeur"));
    }

    @Test
    void testExportToCSV_WithNullValues() {
        AuditLogDTO auditLog = AuditLogDTO.builder()
            .typeAction(TypeActionAudit.USER_CREATE)
            .acteurUsername("admin")
            .ressourceType("USER")
            .ressourceId("1")
            .success(true)
            .ipAddress(null)
            .description(null)
            .errorMessage(null)
            .build();
        auditService.logAction(auditLog);

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        String csv = auditService.exportToCSV("realm1", past, future);

        assertNotNull(csv);
        assertTrue(csv.contains("admin"));
    }

    @Test
    void testExportToCSV_WithQuotesInDescription() {
        AuditLogDTO auditLog = AuditLogDTO.builder()
            .typeAction(TypeActionAudit.USER_CREATE)
            .acteurUsername("admin")
            .ressourceType("USER")
            .ressourceId("1")
            .success(true)
            .description("Test \"quoted\" description")
            .errorMessage("Error \"message\"")
            .build();
        auditService.logAction(auditLog);

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        String csv = auditService.exportToCSV("realm1", past, future);

        assertNotNull(csv);
        // Les guillemets devraient être échappés
        assertTrue(csv.contains("\"\""));
    }

    @Test
    void testClearAll() {
        auditService.logSuccess(TypeActionAudit.USER_CREATE, "USER", "1", "user1", "realm1", "admin", "Created");
        
        assertEquals(1, auditService.getTotalCount());
        
        auditService.clearAll();
        
        assertEquals(0, auditService.getTotalCount());
    }

    @Test
    void testFindByTypeAction() {
        auditService.logSuccess(TypeActionAudit.USER_CREATE, "USER", "1", "user1", "realm1", "admin", "Created");
        auditService.logSuccess(TypeActionAudit.USER_UPDATE, "USER", "2", "user2", "realm1", "admin", "Updated");

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        List<AuditLogDTO> logs = auditService.findByTypeAction(TypeActionAudit.USER_CREATE, "realm1", past, future, 0, 10);

        assertNotNull(logs);
        assertTrue(logs.size() > 0);
        assertEquals(TypeActionAudit.USER_CREATE, logs.get(0).getTypeAction());
    }

    @Test
    void testSearchLogs_WithNullSuccess() {
        auditService.logSuccess(TypeActionAudit.USER_CREATE, "USER", "1", "user1", "realm1", "admin", "Created");
        auditService.logFailure(TypeActionAudit.USER_CREATE, "USER", "2", "user2", "realm1", "admin", "Failed", "Error");

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime past = now.minusDays(1);
        LocalDateTime future = now.plusDays(1);

        // findByRealm ne filtre pas par success, donc success = null
        List<AuditLogDTO> logs = auditService.findByRealm("realm1", past, future, 0, 10);

        assertNotNull(logs);
        assertTrue(logs.size() >= 2);
    }
}

