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

import dev.lions.user.manager.dto.realm.RealmAssignmentDTO;
import dev.lions.user.manager.enums.audit.TypeActionAudit;
import dev.lions.user.manager.service.AuditService;
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.junit.jupiter.MockitoExtension;

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

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

/**
 * Tests unitaires pour RealmAuthorizationServiceImpl
 */
@ExtendWith(MockitoExtension.class)
class RealmAuthorizationServiceImplTest {

    @Mock
    private AuditService auditService;

    @InjectMocks
    private RealmAuthorizationServiceImpl realmAuthorizationService;

    private RealmAssignmentDTO assignment;

    @BeforeEach
    void setUp() {
        assignment = RealmAssignmentDTO.builder()
            .id("assignment-1")
            .userId("user-1")
            .username("testuser")
            .email("test@example.com")
            .realmName("realm1")
            .isSuperAdmin(false)
            .active(true)
            .assignedAt(LocalDateTime.now())
            .assignedBy("admin")
            .build();
    }

    @Test
    void testGetAllAssignments_Empty() {
        List<RealmAssignmentDTO> assignments = realmAuthorizationService.getAllAssignments();
        assertTrue(assignments.isEmpty());
    }

    @Test
    void testGetAllAssignments_WithAssignments() {
        realmAuthorizationService.assignRealmToUser(assignment);
        List<RealmAssignmentDTO> assignments = realmAuthorizationService.getAllAssignments();
        assertEquals(1, assignments.size());
        assertEquals("assignment-1", assignments.get(0).getId());
    }

    @Test
    void testGetAssignmentsByUser_Success() {
        realmAuthorizationService.assignRealmToUser(assignment);
        List<RealmAssignmentDTO> assignments = realmAuthorizationService.getAssignmentsByUser("user-1");
        assertEquals(1, assignments.size());
    }

    @Test
    void testGetAssignmentsByUser_Empty() {
        List<RealmAssignmentDTO> assignments = realmAuthorizationService.getAssignmentsByUser("user-1");
        assertTrue(assignments.isEmpty());
    }

    @Test
    void testGetAssignmentsByRealm_Success() {
        realmAuthorizationService.assignRealmToUser(assignment);
        List<RealmAssignmentDTO> assignments = realmAuthorizationService.getAssignmentsByRealm("realm1");
        assertEquals(1, assignments.size());
    }

    @Test
    void testGetAssignmentById_Success() {
        realmAuthorizationService.assignRealmToUser(assignment);
        Optional<RealmAssignmentDTO> found = realmAuthorizationService.getAssignmentById("assignment-1");
        assertTrue(found.isPresent());
        assertEquals("assignment-1", found.get().getId());
    }

    @Test
    void testGetAssignmentById_NotFound() {
        Optional<RealmAssignmentDTO> found = realmAuthorizationService.getAssignmentById("non-existent");
        assertFalse(found.isPresent());
    }

    @Test
    void testCanManageRealm_SuperAdmin() {
        realmAuthorizationService.setSuperAdmin("user-1", true);
        assertTrue(realmAuthorizationService.canManageRealm("user-1", "any-realm"));
    }

    @Test
    void testCanManageRealm_WithAssignment() {
        realmAuthorizationService.assignRealmToUser(assignment);
        assertTrue(realmAuthorizationService.canManageRealm("user-1", "realm1"));
    }

    @Test
    void testCanManageRealm_NoAccess() {
        assertFalse(realmAuthorizationService.canManageRealm("user-1", "realm1"));
    }

    @Test
    void testIsSuperAdmin_True() {
        realmAuthorizationService.setSuperAdmin("user-1", true);
        assertTrue(realmAuthorizationService.isSuperAdmin("user-1"));
    }

    @Test
    void testIsSuperAdmin_False() {
        assertFalse(realmAuthorizationService.isSuperAdmin("user-1"));
    }

    @Test
    void testGetAuthorizedRealms_SuperAdmin() {
        realmAuthorizationService.setSuperAdmin("user-1", true);
        List<String> realms = realmAuthorizationService.getAuthorizedRealms("user-1");
        assertTrue(realms.isEmpty()); // Super admin retourne liste vide
    }

    @Test
    void testGetAuthorizedRealms_WithAssignments() {
        realmAuthorizationService.assignRealmToUser(assignment);
        List<String> realms = realmAuthorizationService.getAuthorizedRealms("user-1");
        assertEquals(1, realms.size());
        assertEquals("realm1", realms.get(0));
    }

    @Test
    void testAssignRealmToUser_Success() {
        doNothing().when(auditService).logSuccess(
            any(TypeActionAudit.class),
            anyString(),
            anyString(),
            anyString(),
            anyString(),
            anyString(),
            anyString()
        );

        RealmAssignmentDTO result = realmAuthorizationService.assignRealmToUser(assignment);
        assertNotNull(result);
        assertNotNull(result.getId());
        assertTrue(result.isActive());
        assertNotNull(result.getAssignedAt());
    }

    @Test
    void testAssignRealmToUser_NoUserId() {
        assignment.setUserId(null);
        assertThrows(IllegalArgumentException.class, () -> {
            realmAuthorizationService.assignRealmToUser(assignment);
        });
    }

    @Test
    void testAssignRealmToUser_NoRealmName() {
        assignment.setRealmName(null);
        assertThrows(IllegalArgumentException.class, () -> {
            realmAuthorizationService.assignRealmToUser(assignment);
        });
    }

    @Test
    void testAssignRealmToUser_Duplicate() {
        doNothing().when(auditService).logSuccess(any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
        realmAuthorizationService.assignRealmToUser(assignment);
        assertThrows(IllegalArgumentException.class, () -> {
            realmAuthorizationService.assignRealmToUser(assignment);
        });
    }

    @Test
    void testRevokeRealmFromUser_Success() {
        doNothing().when(auditService).logSuccess(any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
        realmAuthorizationService.assignRealmToUser(assignment);
        realmAuthorizationService.revokeRealmFromUser("user-1", "realm1");
        assertFalse(realmAuthorizationService.canManageRealm("user-1", "realm1"));
    }

    @Test
    void testRevokeRealmFromUser_NotExists() {
        // Ne doit pas lever d'exception si l'assignation n'existe pas
        assertDoesNotThrow(() -> {
            realmAuthorizationService.revokeRealmFromUser("user-1", "realm1");
        });
    }

    @Test
    void testRevokeAllRealmsFromUser() {
        doNothing().when(auditService).logSuccess(any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
        realmAuthorizationService.assignRealmToUser(assignment);
        realmAuthorizationService.revokeAllRealmsFromUser("user-1");
        assertTrue(realmAuthorizationService.getAssignmentsByUser("user-1").isEmpty());
    }

    @Test
    void testSetSuperAdmin_True() {
        doNothing().when(auditService).logSuccess(any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
        realmAuthorizationService.setSuperAdmin("user-1", true);
        assertTrue(realmAuthorizationService.isSuperAdmin("user-1"));
    }

    @Test
    void testSetSuperAdmin_False() {
        doNothing().when(auditService).logSuccess(any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
        realmAuthorizationService.setSuperAdmin("user-1", true);
        realmAuthorizationService.setSuperAdmin("user-1", false);
        assertFalse(realmAuthorizationService.isSuperAdmin("user-1"));
    }

    @Test
    void testDeactivateAssignment_Success() {
        doNothing().when(auditService).logSuccess(any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
        realmAuthorizationService.assignRealmToUser(assignment);
        realmAuthorizationService.deactivateAssignment(assignment.getId());
        Optional<RealmAssignmentDTO> found = realmAuthorizationService.getAssignmentById(assignment.getId());
        assertTrue(found.isPresent());
        assertFalse(found.get().isActive());
    }

    @Test
    void testDeactivateAssignment_NotFound() {
        assertThrows(IllegalArgumentException.class, () -> {
            realmAuthorizationService.deactivateAssignment("non-existent");
        });
    }

    @Test
    void testActivateAssignment_Success() {
        doNothing().when(auditService).logSuccess(any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
        realmAuthorizationService.assignRealmToUser(assignment);
        realmAuthorizationService.deactivateAssignment(assignment.getId());
        realmAuthorizationService.activateAssignment(assignment.getId());
        Optional<RealmAssignmentDTO> found = realmAuthorizationService.getAssignmentById(assignment.getId());
        assertTrue(found.isPresent());
        assertTrue(found.get().isActive());
    }

    @Test
    void testCountAssignmentsByUser() {
        doNothing().when(auditService).logSuccess(any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
        realmAuthorizationService.assignRealmToUser(assignment);
        long count = realmAuthorizationService.countAssignmentsByUser("user-1");
        assertEquals(1, count);
    }

    @Test
    void testCountUsersByRealm() {
        doNothing().when(auditService).logSuccess(any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
        realmAuthorizationService.assignRealmToUser(assignment);
        long count = realmAuthorizationService.countUsersByRealm("realm1");
        assertEquals(1, count);
    }

    @Test
    void testAssignmentExists_True() {
        doNothing().when(auditService).logSuccess(any(), anyString(), anyString(), anyString(), anyString(), anyString(), anyString());
        realmAuthorizationService.assignRealmToUser(assignment);
        assertTrue(realmAuthorizationService.assignmentExists("user-1", "realm1"));
    }

    @Test
    void testAssignmentExists_False() {
        assertFalse(realmAuthorizationService.assignmentExists("user-1", "realm1"));
    }
}

