package dev.lions.user.manager.resource;

import dev.lions.user.manager.dto.role.RoleAssignmentDTO;
import dev.lions.user.manager.dto.role.RoleDTO;
import dev.lions.user.manager.enums.role.TypeRole;
import dev.lions.user.manager.service.RoleService;
import jakarta.ws.rs.core.Response;
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.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 RoleResourceTest {

    @Mock
    RoleService roleService;

    @InjectMocks
    RoleResource roleResource;

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

    // ============== Realm Role Tests ==============

    @Test
    void testCreateRealmRole() {
        RoleDTO input = RoleDTO.builder().name("role").description("desc").build();
        RoleDTO created = RoleDTO.builder().id("1").name("role").description("desc").build();

        when(roleService.createRealmRole(any(), eq(REALM))).thenReturn(created);

        Response response = roleResource.createRealmRole(input, REALM);

        assertEquals(201, response.getStatus());
        assertEquals(created, response.getEntity());
    }

    @Test
    void testCreateRealmRoleConflict() {
        RoleDTO input = RoleDTO.builder().name("role").build();

        when(roleService.createRealmRole(any(), eq(REALM)))
                .thenThrow(new IllegalArgumentException("Role already exists"));

        Response response = roleResource.createRealmRole(input, REALM);

        assertEquals(409, response.getStatus());
    }

    @Test
    void testCreateRealmRoleError() {
        RoleDTO input = RoleDTO.builder().name("role").build();

        when(roleService.createRealmRole(any(), eq(REALM)))
                .thenThrow(new RuntimeException("Error"));

        Response response = roleResource.createRealmRole(input, REALM);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetRealmRole() {
        RoleDTO role = RoleDTO.builder().id("1").name("role").build();
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(role));

        Response response = roleResource.getRealmRole("role", REALM);

        assertEquals(200, response.getStatus());
        assertEquals(role, response.getEntity());
    }

    @Test
    void testGetRealmRoleNotFound() {
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.empty());

        Response response = roleResource.getRealmRole("role", REALM);

        assertEquals(404, response.getStatus());
    }

    @Test
    void testGetRealmRoleError() {
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenThrow(new RuntimeException("Error"));

        Response response = roleResource.getRealmRole("role", REALM);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetAllRealmRoles() {
        List<RoleDTO> roles = Collections.singletonList(RoleDTO.builder().name("role").build());
        when(roleService.getAllRealmRoles(REALM)).thenReturn(roles);

        Response response = roleResource.getAllRealmRoles(REALM);

        assertEquals(200, response.getStatus());
        assertEquals(roles, response.getEntity());
    }

    @Test
    void testGetAllRealmRolesError() {
        when(roleService.getAllRealmRoles(REALM)).thenThrow(new RuntimeException("Error"));

        Response response = roleResource.getAllRealmRoles(REALM);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testUpdateRealmRole() {
        RoleDTO existingRole = RoleDTO.builder().id("1").name("role").build();
        RoleDTO input = RoleDTO.builder().description("updated").build();
        RoleDTO updated = RoleDTO.builder().id("1").name("role").description("updated").build();

        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(existingRole));
        when(roleService.updateRole(eq("1"), any(), eq(REALM), eq(TypeRole.REALM_ROLE), isNull()))
                .thenReturn(updated);

        Response response = roleResource.updateRealmRole("role", input, REALM);

        assertEquals(200, response.getStatus());
        assertEquals(updated, response.getEntity());
    }

    @Test
    void testUpdateRealmRoleNotFound() {
        RoleDTO input = RoleDTO.builder().description("updated").build();

        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.empty());

        Response response = roleResource.updateRealmRole("role", input, REALM);

        assertEquals(404, response.getStatus());
    }

    @Test
    void testUpdateRealmRoleError() {
        RoleDTO existingRole = RoleDTO.builder().id("1").name("role").build();
        RoleDTO input = RoleDTO.builder().description("updated").build();

        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(existingRole));
        when(roleService.updateRole(eq("1"), any(), eq(REALM), eq(TypeRole.REALM_ROLE), isNull()))
                .thenThrow(new RuntimeException("Error"));

        Response response = roleResource.updateRealmRole("role", input, REALM);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testDeleteRealmRole() {
        RoleDTO existingRole = RoleDTO.builder().id("1").name("role").build();
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(existingRole));
        doNothing().when(roleService).deleteRole(eq("1"), eq(REALM), eq(TypeRole.REALM_ROLE), isNull());

        Response response = roleResource.deleteRealmRole("role", REALM);

        assertEquals(204, response.getStatus());
    }

    @Test
    void testDeleteRealmRoleNotFound() {
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.empty());

        Response response = roleResource.deleteRealmRole("role", REALM);

        assertEquals(404, response.getStatus());
    }

    @Test
    void testDeleteRealmRoleError() {
        RoleDTO existingRole = RoleDTO.builder().id("1").name("role").build();
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(existingRole));
        doThrow(new RuntimeException("Error")).when(roleService)
                .deleteRole(eq("1"), eq(REALM), eq(TypeRole.REALM_ROLE), isNull());

        Response response = roleResource.deleteRealmRole("role", REALM);

        assertEquals(500, response.getStatus());
    }

    // ============== Client Role Tests ==============

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

        when(roleService.createClientRole(any(RoleDTO.class), eq(REALM), eq(CLIENT_ID))).thenReturn(created);

        Response response = roleResource.createClientRole(CLIENT_ID, input, REALM);

        assertEquals(201, response.getStatus());
        assertEquals(created, response.getEntity());
    }

    @Test
    void testCreateClientRoleError() {
        RoleDTO input = RoleDTO.builder().name("role").build();

        when(roleService.createClientRole(any(RoleDTO.class), eq(REALM), eq(CLIENT_ID)))
                .thenThrow(new RuntimeException("Error"));

        Response response = roleResource.createClientRole(CLIENT_ID, input, REALM);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetClientRole() {
        RoleDTO role = RoleDTO.builder().id("1").name("role").build();
        when(roleService.getRoleByName("role", REALM, TypeRole.CLIENT_ROLE, CLIENT_ID))
                .thenReturn(Optional.of(role));

        Response response = roleResource.getClientRole(CLIENT_ID, "role", REALM);

        assertEquals(200, response.getStatus());
        assertEquals(role, response.getEntity());
    }

    @Test
    void testGetClientRoleNotFound() {
        when(roleService.getRoleByName("role", REALM, TypeRole.CLIENT_ROLE, CLIENT_ID))
                .thenReturn(Optional.empty());

        Response response = roleResource.getClientRole(CLIENT_ID, "role", REALM);

        assertEquals(404, response.getStatus());
    }

    @Test
    void testGetClientRoleError() {
        when(roleService.getRoleByName("role", REALM, TypeRole.CLIENT_ROLE, CLIENT_ID))
                .thenThrow(new RuntimeException("Error"));

        Response response = roleResource.getClientRole(CLIENT_ID, "role", REALM);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetAllClientRoles() {
        List<RoleDTO> roles = Collections.singletonList(RoleDTO.builder().name("role").build());
        when(roleService.getAllClientRoles(REALM, CLIENT_ID)).thenReturn(roles);

        Response response = roleResource.getAllClientRoles(CLIENT_ID, REALM);

        assertEquals(200, response.getStatus());
        assertEquals(roles, response.getEntity());
    }

    @Test
    void testGetAllClientRolesError() {
        when(roleService.getAllClientRoles(REALM, CLIENT_ID)).thenThrow(new RuntimeException("Error"));

        Response response = roleResource.getAllClientRoles(CLIENT_ID, REALM);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testDeleteClientRole() {
        RoleDTO existingRole = RoleDTO.builder().id("1").name("role").build();
        when(roleService.getRoleByName("role", REALM, TypeRole.CLIENT_ROLE, CLIENT_ID))
                .thenReturn(Optional.of(existingRole));
        doNothing().when(roleService).deleteRole(eq("1"), eq(REALM), eq(TypeRole.CLIENT_ROLE), eq(CLIENT_ID));

        Response response = roleResource.deleteClientRole(CLIENT_ID, "role", REALM);

        assertEquals(204, response.getStatus());
    }

    @Test
    void testDeleteClientRoleNotFound() {
        when(roleService.getRoleByName("role", REALM, TypeRole.CLIENT_ROLE, CLIENT_ID))
                .thenReturn(Optional.empty());

        Response response = roleResource.deleteClientRole(CLIENT_ID, "role", REALM);

        assertEquals(404, response.getStatus());
    }

    @Test
    void testDeleteClientRoleError() {
        RoleDTO existingRole = RoleDTO.builder().id("1").name("role").build();
        when(roleService.getRoleByName("role", REALM, TypeRole.CLIENT_ROLE, CLIENT_ID))
                .thenReturn(Optional.of(existingRole));
        doThrow(new RuntimeException("Error")).when(roleService)
                .deleteRole(eq("1"), eq(REALM), eq(TypeRole.CLIENT_ROLE), eq(CLIENT_ID));

        Response response = roleResource.deleteClientRole(CLIENT_ID, "role", REALM);

        assertEquals(500, response.getStatus());
    }

    // ============== Role Assignment Tests ==============

    @Test
    void testAssignRealmRoles() {
        doNothing().when(roleService).assignRolesToUser(any(RoleAssignmentDTO.class));

        RoleResource.RoleAssignmentRequest request = new RoleResource.RoleAssignmentRequest();
        request.roleNames = Collections.singletonList("role");

        Response response = roleResource.assignRealmRoles("user1", REALM, request);

        assertEquals(204, response.getStatus());
        verify(roleService).assignRolesToUser(any(RoleAssignmentDTO.class));
    }

    @Test
    void testAssignRealmRolesError() {
        doThrow(new RuntimeException("Error")).when(roleService).assignRolesToUser(any(RoleAssignmentDTO.class));

        RoleResource.RoleAssignmentRequest request = new RoleResource.RoleAssignmentRequest();
        request.roleNames = Collections.singletonList("role");

        Response response = roleResource.assignRealmRoles("user1", REALM, request);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testRevokeRealmRoles() {
        doNothing().when(roleService).revokeRolesFromUser(any(RoleAssignmentDTO.class));

        RoleResource.RoleAssignmentRequest request = new RoleResource.RoleAssignmentRequest();
        request.roleNames = Collections.singletonList("role");

        Response response = roleResource.revokeRealmRoles("user1", REALM, request);

        assertEquals(204, response.getStatus());
        verify(roleService).revokeRolesFromUser(any(RoleAssignmentDTO.class));
    }

    @Test
    void testRevokeRealmRolesError() {
        doThrow(new RuntimeException("Error")).when(roleService).revokeRolesFromUser(any(RoleAssignmentDTO.class));

        RoleResource.RoleAssignmentRequest request = new RoleResource.RoleAssignmentRequest();
        request.roleNames = Collections.singletonList("role");

        Response response = roleResource.revokeRealmRoles("user1", REALM, request);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testAssignClientRoles() {
        doNothing().when(roleService).assignRolesToUser(any(RoleAssignmentDTO.class));

        RoleResource.RoleAssignmentRequest request = new RoleResource.RoleAssignmentRequest();
        request.roleNames = Collections.singletonList("role");

        Response response = roleResource.assignClientRoles(CLIENT_ID, "user1", REALM, request);

        assertEquals(204, response.getStatus());
        verify(roleService).assignRolesToUser(any(RoleAssignmentDTO.class));
    }

    @Test
    void testAssignClientRolesError() {
        doThrow(new RuntimeException("Error")).when(roleService).assignRolesToUser(any(RoleAssignmentDTO.class));

        RoleResource.RoleAssignmentRequest request = new RoleResource.RoleAssignmentRequest();
        request.roleNames = Collections.singletonList("role");

        Response response = roleResource.assignClientRoles(CLIENT_ID, "user1", REALM, request);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetUserRealmRoles() {
        List<RoleDTO> roles = Collections.singletonList(RoleDTO.builder().name("role").build());
        when(roleService.getUserRealmRoles("user1", REALM)).thenReturn(roles);

        Response response = roleResource.getUserRealmRoles("user1", REALM);

        assertEquals(200, response.getStatus());
        assertEquals(roles, response.getEntity());
    }

    @Test
    void testGetUserRealmRolesError() {
        when(roleService.getUserRealmRoles("user1", REALM)).thenThrow(new RuntimeException("Error"));

        Response response = roleResource.getUserRealmRoles("user1", REALM);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetUserClientRoles() {
        List<RoleDTO> roles = Collections.singletonList(RoleDTO.builder().name("role").build());
        when(roleService.getUserClientRoles("user1", CLIENT_ID, REALM)).thenReturn(roles);

        Response response = roleResource.getUserClientRoles(CLIENT_ID, "user1", REALM);

        assertEquals(200, response.getStatus());
        assertEquals(roles, response.getEntity());
    }

    @Test
    void testGetUserClientRolesError() {
        when(roleService.getUserClientRoles("user1", CLIENT_ID, REALM)).thenThrow(new RuntimeException("Error"));

        Response response = roleResource.getUserClientRoles(CLIENT_ID, "user1", REALM);

        assertEquals(500, response.getStatus());
    }

    // ============== Composite Role Tests ==============

    @Test
    void testAddComposites() {
        RoleDTO parentRole = RoleDTO.builder().id("parent-1").name("role").build();
        RoleDTO childRole = RoleDTO.builder().id("child-1").name("composite").build();
        
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(parentRole));
        when(roleService.getRoleByName("composite", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(childRole));
        doNothing().when(roleService).addCompositeRoles(eq("parent-1"), anyList(), eq(REALM), 
                eq(TypeRole.REALM_ROLE), isNull());

        RoleResource.RoleAssignmentRequest request = new RoleResource.RoleAssignmentRequest();
        request.roleNames = Collections.singletonList("composite");

        Response response = roleResource.addComposites("role", REALM, request);

        assertEquals(204, response.getStatus());
        verify(roleService).addCompositeRoles(eq("parent-1"), anyList(), eq(REALM), 
                eq(TypeRole.REALM_ROLE), isNull());
    }

    @Test
    void testAddCompositesParentNotFound() {
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.empty());

        RoleResource.RoleAssignmentRequest request = new RoleResource.RoleAssignmentRequest();
        request.roleNames = Collections.singletonList("composite");

        Response response = roleResource.addComposites("role", REALM, request);

        assertEquals(404, response.getStatus());
    }

    @Test
    void testAddCompositesError() {
        RoleDTO parentRole = RoleDTO.builder().id("parent-1").name("role").build();
        RoleDTO childRole = RoleDTO.builder().id("child-1").name("composite").build();
        
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(parentRole));
        when(roleService.getRoleByName("composite", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(childRole));
        doThrow(new RuntimeException("Error")).when(roleService).addCompositeRoles(eq("parent-1"), anyList(), 
                eq(REALM), eq(TypeRole.REALM_ROLE), isNull());

        RoleResource.RoleAssignmentRequest request = new RoleResource.RoleAssignmentRequest();
        request.roleNames = Collections.singletonList("composite");

        Response response = roleResource.addComposites("role", REALM, request);

        assertEquals(500, response.getStatus());
    }

    @Test
    void testGetComposites() {
        RoleDTO role = RoleDTO.builder().id("1").name("role").build();
        List<RoleDTO> composites = Collections.singletonList(RoleDTO.builder().name("composite").build());
        
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(role));
        when(roleService.getCompositeRoles("1", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(composites);

        Response response = roleResource.getComposites("role", REALM);

        assertEquals(200, response.getStatus());
        assertEquals(composites, response.getEntity());
    }

    @Test
    void testGetCompositesNotFound() {
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.empty());

        Response response = roleResource.getComposites("role", REALM);

        assertEquals(404, response.getStatus());
    }

    @Test
    void testGetCompositesError() {
        RoleDTO role = RoleDTO.builder().id("1").name("role").build();
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(role));
        when(roleService.getCompositeRoles("1", REALM, TypeRole.REALM_ROLE, null))
                .thenThrow(new RuntimeException("Error"));

        Response response = roleResource.getComposites("role", REALM);

        assertEquals(500, response.getStatus());
    }

    // ============== Inner Class Tests ==============

    @Test
    void testRoleAssignmentRequestClass() {
        RoleResource.RoleAssignmentRequest request = new RoleResource.RoleAssignmentRequest();
        request.roleNames = List.of("role1", "role2");

        assertEquals(2, request.roleNames.size());
        assertTrue(request.roleNames.contains("role1"));
    }
}
