package dev.lions.user.manager.client;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.admin.client.resource.ServerInfoResource;
import org.keycloak.admin.client.resource.UsersResource;
import org.keycloak.representations.info.ServerInfoRepresentation;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import jakarta.ws.rs.NotFoundException;
import java.lang.reflect.Field;

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

@ExtendWith(MockitoExtension.class)
class KeycloakAdminClientImplTest {

    @InjectMocks
    KeycloakAdminClientImpl client;

    @Mock
    Keycloak keycloak;

    @Mock
    RealmResource realmResource;

    @Mock
    UsersResource usersResource;

    @Mock
    RolesResource rolesResource;

    @Mock
    ServerInfoResource serverInfoResource;

    @BeforeEach
    void setUp() throws Exception {
        // Inject the mock keycloak instance
        setField(client, "keycloak", keycloak);
    }

    private void setField(Object target, String fieldName, Object value) throws Exception {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(target, value);
    }

    @Test
    void testGetInstance() {
        Keycloak result = client.getInstance();
        assertNotNull(result);
        assertEquals(keycloak, result);
    }

    @Test
    void testGetInstanceReInitWhenNull() throws Exception {
        // Set keycloak to null
        setField(client, "keycloak", null);

        // Should attempt to reinitialize (will fail without config, but that's ok)
        // The method should return null since init() will fail without proper config
        Keycloak result = client.getInstance();
        // Since config values are null, keycloak will still be null
        assertNull(result);
    }

    @Test
    void testGetRealm() {
        when(keycloak.realm("test-realm")).thenReturn(realmResource);

        RealmResource result = client.getRealm("test-realm");

        assertNotNull(result);
        assertEquals(realmResource, result);
    }

    @Test
    void testGetRealmThrowsException() {
        when(keycloak.realm("bad-realm")).thenThrow(new RuntimeException("Connection failed"));

        assertThrows(RuntimeException.class, () -> client.getRealm("bad-realm"));
    }

    @Test
    void testGetUsers() {
        when(keycloak.realm("test-realm")).thenReturn(realmResource);
        when(realmResource.users()).thenReturn(usersResource);

        UsersResource result = client.getUsers("test-realm");

        assertNotNull(result);
        assertEquals(usersResource, result);
    }

    @Test
    void testGetRoles() {
        when(keycloak.realm("test-realm")).thenReturn(realmResource);
        when(realmResource.roles()).thenReturn(rolesResource);

        RolesResource result = client.getRoles("test-realm");

        assertNotNull(result);
        assertEquals(rolesResource, result);
    }

    @Test
    void testIsConnected_true() {
        when(keycloak.serverInfo()).thenReturn(serverInfoResource);
        when(serverInfoResource.getInfo()).thenReturn(new ServerInfoRepresentation());

        assertTrue(client.isConnected());
    }

    @Test
    void testIsConnected_false_exception() {
        when(keycloak.serverInfo()).thenThrow(new RuntimeException("Connection refused"));

        assertFalse(client.isConnected());
    }

    @Test
    void testIsConnected_false_null() throws Exception {
        setField(client, "keycloak", null);

        assertFalse(client.isConnected());
    }

    @Test
    void testRealmExists_true() {
        when(keycloak.realm("test-realm")).thenReturn(realmResource);
        when(realmResource.roles()).thenReturn(rolesResource);
        when(rolesResource.list()).thenReturn(java.util.Collections.emptyList());

        assertTrue(client.realmExists("test-realm"));
    }

    @Test
    void testRealmExists_notFound() {
        when(keycloak.realm("missing-realm")).thenReturn(realmResource);
        when(realmResource.roles()).thenReturn(rolesResource);
        when(rolesResource.list()).thenThrow(new NotFoundException("Realm not found"));

        assertFalse(client.realmExists("missing-realm"));
    }

    @Test
    void testRealmExists_otherException() {
        when(keycloak.realm("problem-realm")).thenReturn(realmResource);
        when(realmResource.roles()).thenReturn(rolesResource);
        when(rolesResource.list()).thenThrow(new RuntimeException("Some other error"));

        // Should return true assuming realm exists but has issues
        assertTrue(client.realmExists("problem-realm"));
    }

    @Test
    void testClose() {
        client.close();

        verify(keycloak).close();
    }

    @Test
    void testCloseWhenNull() throws Exception {
        setField(client, "keycloak", null);

        // Should not throw
        client.close();
    }
}
