package dev.lions.user.manager.resource;

import dev.lions.user.manager.dto.role.RoleAssignmentDTO;
import dev.lions.user.manager.dto.role.RoleAssignmentRequestDTO;
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 testGetRealmRole() {
        RoleDTO role = RoleDTO.builder().id("1").name("role").build();
        when(roleService.getRoleByName("role", REALM, TypeRole.REALM_ROLE, null))
                .thenReturn(Optional.of(role));

        RoleDTO result = roleResource.getRealmRole("role", REALM);

        assertEquals(role, result);
    }

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

        assertThrows(RuntimeException.class, () -> roleResource.getRealmRole("role", REALM));
    }

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

        List<RoleDTO> result = roleResource.getAllRealmRoles(REALM);

        assertEquals(roles, result);
    }

    @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);

        RoleDTO result = roleResource.updateRealmRole("role", input, REALM);

        assertEquals(updated, result);
    }

    @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());

        roleResource.deleteRealmRole("role", REALM);

        verify(roleService).deleteRole(eq("1"), eq(REALM), eq(TypeRole.REALM_ROLE), isNull());
    }

    // ============== 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(CLIENT_ID), eq(REALM))).thenReturn(created);

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

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

    @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));

        RoleDTO result = roleResource.getClientRole(CLIENT_ID, "role", REALM);

        assertEquals(role, result);
    }

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

        List<RoleDTO> result = roleResource.getAllClientRoles(CLIENT_ID, REALM);

        assertEquals(roles, result);
    }

    @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));

        roleResource.deleteClientRole(CLIENT_ID, "role", REALM);

        verify(roleService).deleteRole(eq("1"), eq(REALM), eq(TypeRole.CLIENT_ROLE), eq(CLIENT_ID));
    }

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

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

        RoleAssignmentRequestDTO request = RoleAssignmentRequestDTO.builder()
                .roleNames(Collections.singletonList("role"))
                .build();

        roleResource.assignRealmRoles("user1", REALM, request);

        verify(roleService).assignRolesToUser(any(RoleAssignmentDTO.class));
    }

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

        RoleAssignmentRequestDTO request = RoleAssignmentRequestDTO.builder()
                .roleNames(Collections.singletonList("role"))
                .build();

        roleResource.revokeRealmRoles("user1", REALM, request);

        verify(roleService).revokeRolesFromUser(any(RoleAssignmentDTO.class));
    }

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

        RoleAssignmentRequestDTO request = RoleAssignmentRequestDTO.builder()
                .roleNames(Collections.singletonList("role"))
                .build();

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

        verify(roleService).assignRolesToUser(any(RoleAssignmentDTO.class));
    }

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

        List<RoleDTO> result = roleResource.getUserRealmRoles("user1", REALM);

        assertEquals(roles, result);
    }

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

        List<RoleDTO> result = roleResource.getUserClientRoles(CLIENT_ID, "user1", REALM);

        assertEquals(roles, result);
    }

    // ============== 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());

        RoleAssignmentRequestDTO request = RoleAssignmentRequestDTO.builder()
                .roleNames(Collections.singletonList("composite"))
                .build();

        roleResource.addComposites("role", REALM, request);

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

    @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);

        List<RoleDTO> result = roleResource.getComposites("role", REALM);

        assertEquals(composites, result);
    }
}
