package dev.lions.user.manager.client.view;

import dev.lions.user.manager.client.service.RoleServiceClient;
import dev.lions.user.manager.client.service.RealmServiceClient;
import dev.lions.user.manager.dto.role.RoleAssignmentDTO;
import dev.lions.user.manager.dto.role.RoleDTO;
import dev.lions.user.manager.dto.user.UserDTO;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import org.junit.jupiter.api.AfterEach;
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.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class RoleGestionBeanTest {

    @Mock
    RoleServiceClient roleServiceClient;

    @Mock
    RealmServiceClient realmServiceClient;

    @Mock
    FacesContext facesContext;

    @Mock
    ExternalContext externalContext;

    @InjectMocks
    RoleGestionBean roleGestionBean;

    MockedStatic<FacesContext> facesContextMock;

    private static final String REALM_NAME = "lions-user-manager";
    private static final String CLIENT_NAME = "test-client";

    @BeforeEach
    void setUp() {
        facesContextMock = mockStatic(FacesContext.class);
        facesContextMock.when(FacesContext::getCurrentInstance).thenReturn(facesContext);
        when(facesContext.getExternalContext()).thenReturn(externalContext);
    }

    @AfterEach
    void tearDown() {
        facesContextMock.close();
    }

    @Test
    void testInit() {
        roleGestionBean.init();

        assertNotNull(roleGestionBean.getRealmRoles());
        assertNotNull(roleGestionBean.getClientRoles());
        assertNotNull(roleGestionBean.getAllRoles());
        assertEquals(REALM_NAME, roleGestionBean.getRealmName());
    }

    @Test
    void testLoadRealmRoles() {
        List<RoleDTO> roles = Collections.singletonList(
                RoleDTO.builder().id("1").name("admin").build());
        when(roleServiceClient.getAllRealmRoles(REALM_NAME)).thenReturn(roles);

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.loadRealmRoles();

        assertFalse(roleGestionBean.getRealmRoles().isEmpty());
        assertEquals(1, roleGestionBean.getRealmRoles().size());
    }

    @Test
    void testLoadRealmRolesEmptyRealm() {
        roleGestionBean.setRealmName("");
        roleGestionBean.loadRealmRoles();

        verify(roleServiceClient, never()).getAllRealmRoles(anyString());
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testLoadRealmRolesError() {
        when(roleServiceClient.getAllRealmRoles(REALM_NAME))
                .thenThrow(new RuntimeException("Error"));

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.loadRealmRoles();

        assertTrue(roleGestionBean.getRealmRoles().isEmpty());
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testLoadClientRoles() {
        List<RoleDTO> roles = Collections.singletonList(
                RoleDTO.builder().id("1").name("client-role").build());
        when(roleServiceClient.getAllClientRoles(eq(CLIENT_NAME), eq(REALM_NAME))).thenReturn(roles);

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.setClientName(CLIENT_NAME);
        roleGestionBean.loadClientRoles();

        assertFalse(roleGestionBean.getClientRoles().isEmpty());
        assertEquals(1, roleGestionBean.getClientRoles().size());
    }

    @Test
    void testLoadClientRolesEmptyClient() {
        roleGestionBean.setClientName("");
        roleGestionBean.loadClientRoles();

        verify(roleServiceClient, never()).getAllClientRoles(anyString(), anyString());
    }

    @Test
    void testCreateRealmRole() {
        RoleDTO newRole = RoleDTO.builder().name("new-role").description("New role").build();
        RoleDTO created = RoleDTO.builder().id("1").name("new-role").description("New role").build();

        jakarta.ws.rs.core.Response response = mock(jakarta.ws.rs.core.Response.class);
        when(response.readEntity(RoleDTO.class)).thenReturn(created);

        when(roleServiceClient.createRealmRole(any(RoleDTO.class), eq(REALM_NAME)))
                .thenReturn(response);
        when(roleServiceClient.getAllRealmRoles(REALM_NAME))
                .thenReturn(Collections.singletonList(created));

        roleGestionBean.setNewRole(newRole);
        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.createRealmRole();

        verify(roleServiceClient).createRealmRole(any(RoleDTO.class), eq(REALM_NAME));
        verify(roleServiceClient).getAllRealmRoles(REALM_NAME);
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testCreateRealmRoleError() {
        RoleDTO newRole = RoleDTO.builder().name("new-role").build();
        when(roleServiceClient.createRealmRole(any(RoleDTO.class), eq(REALM_NAME)))
                .thenThrow(new RuntimeException("Error"));

        roleGestionBean.setNewRole(newRole);
        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.createRealmRole();

        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testCreateClientRole() {
        RoleDTO newRole = RoleDTO.builder().name("client-role").build();
        RoleDTO created = RoleDTO.builder().id("1").name("client-role").build();

        jakarta.ws.rs.core.Response response = mock(jakarta.ws.rs.core.Response.class);
        when(response.readEntity(RoleDTO.class)).thenReturn(created);

        when(roleServiceClient.createClientRole(eq(CLIENT_NAME), any(RoleDTO.class), eq(REALM_NAME)))
                .thenReturn(response);
        when(roleServiceClient.getAllClientRoles(eq(CLIENT_NAME), eq(REALM_NAME)))
                .thenReturn(Collections.singletonList(created));

        roleGestionBean.setNewRole(newRole);
        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.setClientName(CLIENT_NAME);
        roleGestionBean.createClientRole();

        verify(roleServiceClient).createClientRole(eq(CLIENT_NAME), any(RoleDTO.class), eq(REALM_NAME));
        verify(roleServiceClient).getAllClientRoles(eq(CLIENT_NAME), eq(REALM_NAME));
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testCreateClientRoleEmptyClient() {
        roleGestionBean.setClientName("");
        roleGestionBean.createClientRole();

        verify(roleServiceClient, never()).createClientRole(anyString(),
                any(dev.lions.user.manager.dto.role.RoleDTO.class), anyString());
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testDeleteRealmRole() {
        // Mock pour le rechargement après suppression (retourne une liste vide)
        when(roleServiceClient.getAllRealmRoles(REALM_NAME))
                .thenReturn(Collections.emptyList());
        doNothing().when(roleServiceClient).deleteRealmRole("admin", REALM_NAME);

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.deleteRealmRole("admin");

        verify(roleServiceClient).deleteRealmRole("admin", REALM_NAME);
        verify(roleServiceClient, atLeastOnce()).getAllRealmRoles(REALM_NAME);
        // addMessage est appelé au moins une fois (pour le succès ou l'erreur)
        verify(facesContext, atLeastOnce()).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testDeleteRealmRoleError() {
        doThrow(new RuntimeException("Error"))
                .when(roleServiceClient).deleteRealmRole("admin", REALM_NAME);

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.deleteRealmRole("admin");

        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testDeleteClientRole() {
        when(roleServiceClient.getAllClientRoles(eq(CLIENT_NAME), eq(REALM_NAME)))
                .thenReturn(Collections.emptyList());
        doNothing().when(roleServiceClient).deleteClientRole(CLIENT_NAME, "client-role", REALM_NAME);

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.setClientName(CLIENT_NAME);
        roleGestionBean.deleteClientRole("client-role");

        verify(roleServiceClient).deleteClientRole(CLIENT_NAME, "client-role", REALM_NAME);
        verify(roleServiceClient).getAllClientRoles(CLIENT_NAME, REALM_NAME);
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testDeleteClientRoleEmptyClient() {
        roleGestionBean.setClientName("");
        roleGestionBean.deleteClientRole("client-role");

        verify(roleServiceClient, never()).deleteClientRole(anyString(), anyString(), anyString());
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testAssignRoleToUser() {
        doNothing().when(roleServiceClient).assignRealmRoles(anyString(), anyString(),
                any(dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO.class));

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.assignRoleToUser("user-1", "admin");

        verify(roleServiceClient).assignRealmRoles(eq("user-1"), eq(REALM_NAME),
                any(dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO.class));
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testAssignRoleToUserError() {
        doThrow(new RuntimeException("Error"))
                .when(roleServiceClient).assignRealmRoles(anyString(), anyString(),
                        any(dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO.class));

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.assignRoleToUser("user-1", "admin");

        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testRevokeRoleFromUser() {
        doNothing().when(roleServiceClient).revokeRealmRoles(anyString(), anyString(),
                any(dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO.class));

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.revokeRoleFromUser("user-1", "admin");

        verify(roleServiceClient).revokeRealmRoles(eq("user-1"), eq(REALM_NAME),
                any(dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO.class));
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testRevokeRoleFromUserError() {
        doThrow(new RuntimeException("Error"))
                .when(roleServiceClient).revokeRealmRoles(anyString(), anyString(),
                        any(dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO.class));

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.revokeRoleFromUser("user-1", "admin");

        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testAssignRoleFromParams() {
        Map<String, String> params = new HashMap<>();
        params.put("userId", "user-1");
        params.put("roleName", "admin");
        when(externalContext.getRequestParameterMap()).thenReturn(params);
        doNothing().when(roleServiceClient).assignRealmRoles(anyString(), anyString(),
                any(dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO.class));

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.assignRoleFromParams();

        verify(roleServiceClient).assignRealmRoles(eq("user-1"), eq(REALM_NAME),
                any(dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO.class));
    }

    @Test
    void testAssignRoleFromParamsMissing() {
        Map<String, String> params = new HashMap<>();
        when(externalContext.getRequestParameterMap()).thenReturn(params);

        roleGestionBean.assignRoleFromParams();

        verify(roleServiceClient, never()).assignRealmRoles(anyString(), anyString(), any());
        verify(facesContext).addMessage(isNull(), any(FacesMessage.class));
    }

    @Test
    void testRevokeRoleFromParams() {
        Map<String, String> params = new HashMap<>();
        params.put("userId", "user-1");
        params.put("roleName", "admin");
        when(externalContext.getRequestParameterMap()).thenReturn(params);
        doNothing().when(roleServiceClient).revokeRealmRoles(anyString(), anyString(),
                any(dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO.class));

        roleGestionBean.setRealmName(REALM_NAME);
        roleGestionBean.revokeRoleFromParams();

        verify(roleServiceClient).revokeRealmRoles(eq("user-1"), eq(REALM_NAME),
                any(dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO.class));
    }

    @Test
    void testGetUserRolesDTOs() {
        UserDTO user = UserDTO.builder()
                .id("user-1")
                .realmRoles(List.of("admin", "user_manager"))
                .build();
        RoleDTO role1 = RoleDTO.builder().id("1").name("admin").build();
        RoleDTO role2 = RoleDTO.builder().id("2").name("user_manager").build();
        RoleDTO role3 = RoleDTO.builder().id("3").name("other").build();

        roleGestionBean.setAllRoles(List.of(role1, role2, role3));

        List<RoleDTO> result = roleGestionBean.getUserRolesDTOs(user);

        assertEquals(2, result.size());
        assertTrue(result.stream().anyMatch(r -> r.getName().equals("admin")));
        assertTrue(result.stream().anyMatch(r -> r.getName().equals("user_manager")));
    }

    @Test
    void testGetUserRolesDTOsNullUser() {
        List<RoleDTO> result = roleGestionBean.getUserRolesDTOs(null);
        assertTrue(result.isEmpty());
    }

    @Test
    void testGetUserRolesDTOsEmptyRoles() {
        UserDTO user = UserDTO.builder().id("user-1").realmRoles(Collections.emptyList()).build();
        List<RoleDTO> result = roleGestionBean.getUserRolesDTOs(user);
        assertTrue(result.isEmpty());
    }

    @Test
    void testResetForm() {
        RoleDTO role = RoleDTO.builder().name("test").build();
        roleGestionBean.setNewRole(role);
        roleGestionBean.setEditMode(true);

        roleGestionBean.resetForm();

        assertNotNull(roleGestionBean.getNewRole());
        assertFalse(roleGestionBean.isEditMode());
    }
}
