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

import dev.lions.user.manager.client.KeycloakAdminClient;
import dev.lions.user.manager.dto.user.UserDTO;
import dev.lions.user.manager.dto.user.UserSearchCriteriaDTO;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.UserRepresentation;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Collections;
import java.util.List;

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

/**
 * Tests complets pour UserServiceImpl pour atteindre 100% de couverture
 * Couvre les branches manquantes : filterUsers, searchUsers avec différents critères, etc.
 */
@ExtendWith(MockitoExtension.class)
class UserServiceImplCompleteTest {

    private static final String REALM = "test-realm";

    @Mock
    private KeycloakAdminClient keycloakAdminClient;

    @Mock
    private UsersResource usersResource;

    @InjectMocks
    private UserServiceImpl userService;

    @BeforeEach
    void setUp() {
        when(keycloakAdminClient.getUsers(REALM)).thenReturn(usersResource);
    }

    @Test
    void testSearchUsers_WithSearchTerm() {
        UserRepresentation user = new UserRepresentation();
        user.setUsername("testuser");
        user.setEnabled(true);
        when(usersResource.search("test", 0, 10)).thenReturn(List.of(user));
        when(usersResource.count()).thenReturn(1);

        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
            .realmName(REALM)
            .searchTerm("test")
            .page(0)
            .pageSize(10)
            .build();

        var result = userService.searchUsers(criteria);

        assertNotNull(result);
        assertEquals(1, result.getUsers().size());
        verify(usersResource).search("test", 0, 10);
    }

    @Test
    void testSearchUsers_WithUsername() {
        UserRepresentation user = new UserRepresentation();
        user.setUsername("testuser");
        user.setEnabled(true);
        when(usersResource.search("testuser", 0, 10, true)).thenReturn(List.of(user));
        when(usersResource.count()).thenReturn(1);

        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
            .realmName(REALM)
            .username("testuser")
            .page(0)
            .pageSize(10)
            .build();

        var result = userService.searchUsers(criteria);

        assertNotNull(result);
        assertEquals(1, result.getUsers().size());
        verify(usersResource).search("testuser", 0, 10, true);
    }

    @Test
    void testSearchUsers_WithEmail() {
        UserRepresentation user = new UserRepresentation();
        user.setUsername("testuser");
        user.setEmail("test@example.com");
        user.setEnabled(true);
        when(usersResource.searchByEmail("test@example.com", true)).thenReturn(List.of(user));
        when(usersResource.count()).thenReturn(1);

        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
            .realmName(REALM)
            .email("test@example.com")
            .page(0)
            .pageSize(10)
            .build();

        var result = userService.searchUsers(criteria);

        assertNotNull(result);
        assertEquals(1, result.getUsers().size());
        verify(usersResource).searchByEmail("test@example.com", true);
    }

    @Test
    void testSearchUsers_ListAll() {
        UserRepresentation user = new UserRepresentation();
        user.setUsername("testuser");
        user.setEnabled(true);
        when(usersResource.list(0, 10)).thenReturn(List.of(user));
        when(usersResource.count()).thenReturn(1);

        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
            .realmName(REALM)
            .page(0)
            .pageSize(10)
            .build();

        var result = userService.searchUsers(criteria);

        assertNotNull(result);
        assertEquals(1, result.getUsers().size());
        verify(usersResource).list(0, 10);
    }

    @Test
    void testSearchUsers_WithEnabledFilter() {
        UserRepresentation enabledUser = new UserRepresentation();
        enabledUser.setUsername("enabled");
        enabledUser.setEnabled(true);
        UserRepresentation disabledUser = new UserRepresentation();
        disabledUser.setUsername("disabled");
        disabledUser.setEnabled(false);
        
        when(usersResource.list(0, 10)).thenReturn(List.of(enabledUser, disabledUser));
        when(usersResource.count()).thenReturn(2);

        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
            .realmName(REALM)
            .enabled(true)
            .page(0)
            .pageSize(10)
            .build();

        var result = userService.searchUsers(criteria);

        assertNotNull(result);
        // Seul l'utilisateur activé devrait être retourné
        assertEquals(1, result.getUsers().size());
        assertTrue(result.getUsers().get(0).getEnabled());
    }

    @Test
    void testSearchUsers_WithEmailVerifiedFilter() {
        UserRepresentation verifiedUser = new UserRepresentation();
        verifiedUser.setUsername("verified");
        verifiedUser.setEmailVerified(true);
        verifiedUser.setEnabled(true);
        UserRepresentation unverifiedUser = new UserRepresentation();
        unverifiedUser.setUsername("unverified");
        unverifiedUser.setEmailVerified(false);
        unverifiedUser.setEnabled(true);
        
        when(usersResource.list(0, 10)).thenReturn(List.of(verifiedUser, unverifiedUser));
        when(usersResource.count()).thenReturn(2);

        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
            .realmName(REALM)
            .emailVerified(true)
            .page(0)
            .pageSize(10)
            .build();

        var result = userService.searchUsers(criteria);

        assertNotNull(result);
        // Seul l'utilisateur avec email vérifié devrait être retourné
        assertEquals(1, result.getUsers().size());
        assertTrue(result.getUsers().get(0).getEmailVerified());
    }

    @Test
    void testSearchUsers_WithBlankSearchTerm() {
        UserRepresentation user = new UserRepresentation();
        user.setUsername("testuser");
        user.setEnabled(true);
        when(usersResource.list(0, 10)).thenReturn(List.of(user));
        when(usersResource.count()).thenReturn(1);

        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
            .realmName(REALM)
            .searchTerm("   ") // Blank search term
            .page(0)
            .pageSize(10)
            .build();

        var result = userService.searchUsers(criteria);

        assertNotNull(result);
        // Devrait utiliser list() au lieu de search() pour un terme vide
        verify(usersResource).list(0, 10);
    }

    @Test
    void testUpdateUser_WithAllFields() {
        UserResource userResource = mock(UserResource.class);
        when(usersResource.get("user-id")).thenReturn(userResource);

        UserRepresentation existingUser = new UserRepresentation();
        existingUser.setId("user-id");
        existingUser.setUsername("olduser");
        existingUser.setEnabled(true);
        when(userResource.toRepresentation()).thenReturn(existingUser);

        UserDTO userDTO = UserDTO.builder()
            .email("new@example.com")
            .prenom("John")
            .nom("Doe")
            .enabled(false)
            .emailVerified(true)
            .attributes(java.util.Map.of("key", java.util.List.of("value")))
            .build();

        UserRepresentation updatedUser = new UserRepresentation();
        updatedUser.setId("user-id");
        updatedUser.setUsername("olduser");
        updatedUser.setEmail("new@example.com");
        updatedUser.setEnabled(true);
        when(userResource.toRepresentation()).thenReturn(existingUser, updatedUser);

        UserDTO result = userService.updateUser("user-id", userDTO, REALM);

        assertNotNull(result);
        verify(userResource).update(any(UserRepresentation.class));
    }

    @Test
    void testUpdateUser_WithNullFields() {
        UserResource userResource = mock(UserResource.class);
        when(usersResource.get("user-id")).thenReturn(userResource);

        UserRepresentation existingUser = new UserRepresentation();
        existingUser.setId("user-id");
        existingUser.setUsername("olduser");
        existingUser.setEnabled(true);
        when(userResource.toRepresentation()).thenReturn(existingUser);

        UserDTO userDTO = UserDTO.builder()
            .email(null)
            .prenom(null)
            .nom(null)
            .enabled(null)
            .emailVerified(null)
            .attributes(null)
            .build();

        UserRepresentation updatedUser = new UserRepresentation();
        updatedUser.setId("user-id");
        updatedUser.setUsername("olduser");
        updatedUser.setEnabled(true);
        when(userResource.toRepresentation()).thenReturn(existingUser, updatedUser);

        UserDTO result = userService.updateUser("user-id", userDTO, REALM);

        assertNotNull(result);
        verify(userResource).update(any(UserRepresentation.class));
    }

    @Test
    void testDeleteUser_HardDelete() {
        UserResource userResource = mock(UserResource.class);
        when(usersResource.get("user-id")).thenReturn(userResource);

        userService.deleteUser("user-id", REALM, true);

        verify(userResource).remove();
        verify(userResource, never()).update(any());
    }

    @Test
    void testDeleteUser_SoftDelete() {
        UserResource userResource = mock(UserResource.class);
        when(usersResource.get("user-id")).thenReturn(userResource);

        UserRepresentation user = new UserRepresentation();
        user.setId("user-id");
        user.setEnabled(true);
        when(userResource.toRepresentation()).thenReturn(user);

        userService.deleteUser("user-id", REALM, false);

        verify(userResource, never()).remove();
        verify(userResource).update(any(UserRepresentation.class));
    }

    @Test
    void testSearchUsers_Exception() {
        when(usersResource.list(0, 10)).thenThrow(new RuntimeException("Connection error"));

        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
            .realmName(REALM)
            .page(0)
            .pageSize(10)
            .build();

        assertThrows(RuntimeException.class, () -> 
            userService.searchUsers(criteria));
    }
}

