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 dev.lions.user.manager.dto.user.UserSearchResultDTO;
import jakarta.ws.rs.core.Response;
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.RoleMappingResource;
import org.keycloak.admin.client.resource.RoleScopeResource;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.net.URI;
import java.util.Collections;
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.*;

@ExtendWith(MockitoExtension.class)
class UserServiceImplTest {

    @Mock
    KeycloakAdminClient keycloakAdminClient;

    @Mock
    UsersResource usersResource;

    @Mock
    UserResource userResource;

    @Mock
    RoleMappingResource roleMappingResource;

    @Mock
    RoleScopeResource roleScopeResource;

    @InjectMocks
    UserServiceImpl userService;

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

    @BeforeEach
    void setUp() {
        // lenient().when(keycloakAdminClient.getUsers(anyString())).thenReturn(usersResource);
    }

    @Test
    void testSearchUsers() {
        when(keycloakAdminClient.getUsers(REALM)).thenReturn(usersResource);
        UserRepresentation userRep = new UserRepresentation();
        userRep.setId("1");
        userRep.setUsername("user");
        userRep.setEnabled(true);

        when(usersResource.search(anyString(), anyInt(), anyInt())).thenReturn(Collections.singletonList(userRep));
        when(usersResource.count()).thenReturn(1);

        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
                .realmName(REALM)
                .searchTerm("user")
                .enabled(true)
                .build();

        UserSearchResultDTO result = userService.searchUsers(criteria);

        assertNotNull(result);
        assertEquals(1, result.getUsers().size());
        assertEquals("user", result.getUsers().get(0).getUsername());
    }

    @Test
    void testGetUserById() {
        when(keycloakAdminClient.getUsers(REALM)).thenReturn(usersResource);
        when(usersResource.get("1")).thenReturn(userResource);

        UserRepresentation userRep = new UserRepresentation();
        userRep.setId("1");
        userRep.setUsername("user");
        userRep.setEnabled(true);
        when(userResource.toRepresentation()).thenReturn(userRep);

        when(userResource.roles()).thenReturn(roleMappingResource);
        when(roleMappingResource.realmLevel()).thenReturn(roleScopeResource);
        when(roleScopeResource.listAll()).thenReturn(Collections.emptyList());

        Optional<UserDTO> result = userService.getUserById("1", REALM);

        assertTrue(result.isPresent());
        assertEquals("1", result.get().getId());
    }

    @Test
    void testCreateUser() {
        when(keycloakAdminClient.getUsers(REALM)).thenReturn(usersResource);
        UserDTO newUser = UserDTO.builder().username("newuser").email("new@example.com").build();

        // Check exists
        when(usersResource.search("newuser", 0, 1, true)).thenReturn(Collections.emptyList());
        when(usersResource.searchByEmail("new@example.com", true)).thenReturn(Collections.emptyList());

        // Mock creation response
        Response response = Response.status(201).location(URI.create("http://localhost/users/123")).build();
        when(usersResource.create(any(UserRepresentation.class))).thenReturn(response);

        // Mock get created user
        when(usersResource.get("123")).thenReturn(userResource);
        UserRepresentation createdRep = new UserRepresentation();
        createdRep.setId("123");
        createdRep.setUsername("newuser");
        createdRep.setEnabled(true);
        when(userResource.toRepresentation()).thenReturn(createdRep);

        UserDTO created = userService.createUser(newUser, REALM);

        assertNotNull(created);
        assertEquals("123", created.getId());
        assertEquals("newuser", created.getUsername());
    }

    @Test
    void testUpdateUser() {
        when(keycloakAdminClient.getUsers(REALM)).thenReturn(usersResource);
        when(usersResource.get("1")).thenReturn(userResource);

        UserRepresentation existing = new UserRepresentation();
        existing.setId("1");
        existing.setEnabled(true);
        when(userResource.toRepresentation()).thenReturn(existing);

        UserDTO update = UserDTO.builder().username("updated").email("up@example.com").build();

        UserDTO result = userService.updateUser("1", update, REALM);

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

    @Test
    void testDeleteUser() {
        when(keycloakAdminClient.getUsers(REALM)).thenReturn(usersResource);
        when(usersResource.get("1")).thenReturn(userResource);

        userService.deleteUser("1", REALM, true);
        verify(userResource).remove();

        when(userResource.toRepresentation()).thenReturn(new UserRepresentation());
        userService.deleteUser("1", REALM, false);
        verify(userResource).update(any(UserRepresentation.class));
    }

    @Test
    void testActivateUser() {
        when(keycloakAdminClient.getUsers(REALM)).thenReturn(usersResource);
        when(usersResource.get("1")).thenReturn(userResource);
        when(userResource.toRepresentation()).thenReturn(new UserRepresentation());

        userService.activateUser("1", REALM);

        verify(userResource).update(argThat(rep -> rep.isEnabled()));
    }

    @Test
    void testExportUsersToCSV() {
        when(keycloakAdminClient.getUsers(REALM)).thenReturn(usersResource);
        UserRepresentation user1 = new UserRepresentation();
        user1.setId("1");
        user1.setUsername("user1");
        user1.setEmail("user1@example.com");
        user1.setFirstName("First");
        user1.setLastName("Last");
        user1.setEnabled(true);

        when(usersResource.list(any(), any())).thenReturn(Collections.singletonList(user1));
        when(usersResource.count()).thenReturn(1);

        UserSearchCriteriaDTO criteria = UserSearchCriteriaDTO.builder()
                .realmName(REALM)
                .build();

        String csv = userService.exportUsersToCSV(criteria);

        assertNotNull(csv);
        assertTrue(csv.contains("username,email,firstName,lastName,enabled"));
        assertTrue(csv.contains("user1,user1@example.com,First,Last,true"));
    }

    @Test
    void testImportUsersFromCSV() {
        when(keycloakAdminClient.getUsers(REALM)).thenReturn(usersResource);

        // Mock checks for existing users
        lenient().when(usersResource.search(anyString(), anyInt(), anyInt(), anyBoolean()))
                .thenReturn(Collections.emptyList());
        lenient().when(usersResource.searchByEmail(anyString(), anyBoolean())).thenReturn(Collections.emptyList());

        // Mock creation response
        Response response = Response.status(201).location(URI.create("http://localhost/users/123")).build();
        lenient().when(usersResource.create(any(UserRepresentation.class))).thenReturn(response);

        // Mock retrieving created user
        lenient().when(usersResource.get(anyString())).thenReturn(userResource);
        UserRepresentation createdRep = new UserRepresentation();
        createdRep.setId("123");
        createdRep.setUsername("imported");
        createdRep.setEnabled(true);
        lenient().when(userResource.toRepresentation()).thenReturn(createdRep);

        // For password setting
        lenient().doNothing().when(userResource)
                .resetPassword(any(org.keycloak.representations.idm.CredentialRepresentation.class));

        String csvContent = "username,prenom,nom,email\n" +
                "imported,Imp,Orter,imp@test.com";

        dev.lions.user.manager.dto.importexport.ImportResultDTO result =
                userService.importUsersFromCSV(csvContent, REALM);

        assertNotNull(result);
        assertEquals(1, result.getSuccessCount());
        assertEquals(0, result.getErrorCount());
        verify(usersResource, atLeastOnce()).create(argThat(u -> u.getUsername().equals("imported")));
    }
}
