package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.client.RoleServiceClient;
import dev.lions.unionflow.server.client.UserServiceClient;
import dev.lions.user.manager.dto.role.RoleDTO;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.List;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;

/**
 * Tests de couverture complémentaires pour AdminUserService.
 * Couvre la branche null targetRoleNames dans setUserRoles().
 */
@QuarkusTest
class AdminUserServiceCoverageTest {

    @Inject
    AdminUserService adminUserService;

    @InjectMock
    @RestClient
    UserServiceClient userServiceClient;

    @InjectMock
    @RestClient
    RoleServiceClient roleServiceClient;

    @Test
    @DisplayName("setUserRoles avec null targetRoleNames → révoque tous les rôles existants")
    void setUserRoles_nullTargetRoleNames_revokesAllExisting() {
        String userId = UUID.randomUUID().toString();

        RoleDTO existingRole = new RoleDTO();
        existingRole.setName("membre");

        Mockito.when(roleServiceClient.getUserRealmRoles(eq(userId), any()))
                .thenReturn(List.of(existingRole));

        assertThatCode(() -> adminUserService.setUserRoles(userId, null))
                .doesNotThrowAnyException();

        Mockito.verify(roleServiceClient, Mockito.never()).assignRealmRoles(any(), any(), any());
        Mockito.verify(roleServiceClient).revokeRealmRoles(eq(userId), any(), any());
    }

    @Test
    @DisplayName("setUserRoles avec null targetRoleNames et aucun rôle existant → aucun appel")
    void setUserRoles_nullTargetRoleNames_noExistingRoles_noCalls() {
        String userId = UUID.randomUUID().toString();

        Mockito.when(roleServiceClient.getUserRealmRoles(eq(userId), any()))
                .thenReturn(List.of());

        assertThatCode(() -> adminUserService.setUserRoles(userId, null))
                .doesNotThrowAnyException();

        Mockito.verify(roleServiceClient, Mockito.never()).assignRealmRoles(any(), any(), any());
        Mockito.verify(roleServiceClient, Mockito.never()).revokeRealmRoles(any(), any(), any());
    }

    @Test
    @DisplayName("setUserRoles avec liste non-null et rôles à assigner — couvre branche toAssign non vide (lignes 109-112)")
    void setUserRoles_nonNullTargetRoles_assignsNewRoles() {
        String userId = UUID.randomUUID().toString();

        // L'utilisateur n'a aucun rôle existant → toAssign = ["admin"] (non vide)
        Mockito.when(roleServiceClient.getUserRealmRoles(eq(userId), any()))
                .thenReturn(java.util.List.of());

        // targetRoleNames = ["admin"] → toAssign = ["admin"] (nouveau) → assignRealmRoles appelé
        assertThatCode(() -> adminUserService.setUserRoles(userId, java.util.List.of("admin")))
                .doesNotThrowAnyException();

        Mockito.verify(roleServiceClient).assignRealmRoles(
                eq(userId),
                any(),
                any(RoleServiceClient.RoleNamesRequest.class));
    }

    @Test
    @DisplayName("setUserRoles avec liste non-null et rôles à révoquer — couvre branche toRevoke non vide (lignes 113-116)")
    void setUserRoles_nonNullTargetRoles_revokesOldRoles() {
        String userId = UUID.randomUUID().toString();

        RoleDTO existingRole = new RoleDTO();
        existingRole.setName("admin");

        // L'utilisateur a "admin", on passe une liste vide → toRevoke = ["admin"]
        Mockito.when(roleServiceClient.getUserRealmRoles(eq(userId), any()))
                .thenReturn(java.util.List.of(existingRole));

        // targetRoleNames = [] → toRevoke = ["admin"] (existant à retirer) → revokeRealmRoles appelé
        assertThatCode(() -> adminUserService.setUserRoles(userId, java.util.List.of()))
                .doesNotThrowAnyException();

        Mockito.verify(roleServiceClient).revokeRealmRoles(
                eq(userId),
                any(),
                any(RoleServiceClient.RoleNamesRequest.class));
    }

    @Test
    @DisplayName("searchUsers avec searchTerm vide utilise null comme critère")
    void searchUsers_emptySearchTerm_usesNull() {
        dev.lions.user.manager.dto.user.UserSearchResultDTO mockResult = new dev.lions.user.manager.dto.user.UserSearchResultDTO();
        Mockito.when(userServiceClient.searchUsers(any())).thenReturn(mockResult);

        // searchTerm blanc → doit être mappé à null (branche: searchTerm != null && !searchTerm.isBlank())
        var result = adminUserService.searchUsers(0, 10, "   ");

        Mockito.verify(userServiceClient).searchUsers(any());
    }

    @Test
    @DisplayName("searchUsers avec searchTerm null utilise null comme critère")
    void searchUsers_nullSearchTerm_usesNull() {
        dev.lions.user.manager.dto.user.UserSearchResultDTO mockResult = new dev.lions.user.manager.dto.user.UserSearchResultDTO();
        Mockito.when(userServiceClient.searchUsers(any())).thenReturn(mockResult);

        var result = adminUserService.searchUsers(0, 10, null);

        Mockito.verify(userServiceClient).searchUsers(any());
    }
}
