package dev.lions.user.manager.service.impl;

import dev.lions.user.manager.client.KeycloakAdminClient;
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.*;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.representations.info.ServerInfoRepresentation;
import org.keycloak.representations.info.SystemInfoRepresentation; // Correct import
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.Map;

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

@ExtendWith(MockitoExtension.class)
class SyncServiceImplTest {

    @Mock
    KeycloakAdminClient keycloakAdminClient;

    @Mock
    Keycloak keycloakInstance;

    @Mock
    RealmsResource realmsResource;

    @Mock
    RealmResource realmResource;

    @Mock
    UsersResource usersResource;

    @Mock
    RolesResource rolesResource;

    @Mock
    ServerInfoResource serverInfoResource;

    @InjectMocks
    SyncServiceImpl syncService;

    // Correcting inner class usage if needed, but assuming standard Keycloak
    // representations
    // ServerInfoRepresentation contains SystemInfoRepresentation

    @Test
    void testSyncUsersFromRealm() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.realm("realm")).thenReturn(realmResource);
        when(realmResource.users()).thenReturn(usersResource);
        when(usersResource.list()).thenReturn(Collections.singletonList(new UserRepresentation()));

        int count = syncService.syncUsersFromRealm("realm");
        assertEquals(1, count);
    }

    @Test
    void testSyncRolesFromRealm() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.realm("realm")).thenReturn(realmResource);
        when(realmResource.roles()).thenReturn(rolesResource);
        when(rolesResource.list()).thenReturn(Collections.singletonList(new RoleRepresentation()));

        int count = syncService.syncRolesFromRealm("realm");
        assertEquals(1, count);
    }

    @Test
    void testSyncAllRealms() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.realms()).thenReturn(realmsResource);

        RealmRepresentation realmRep = new RealmRepresentation();
        realmRep.setRealm("realm1");
        when(realmsResource.findAll()).thenReturn(Collections.singletonList(realmRep));

        // Sync logic calls realm() again
        when(keycloakInstance.realm("realm1")).thenReturn(realmResource);
        when(realmResource.users()).thenReturn(usersResource);
        when(usersResource.list()).thenReturn(Collections.emptyList());
        when(realmResource.roles()).thenReturn(rolesResource);
        when(rolesResource.list()).thenReturn(Collections.emptyList());

        Map<String, Integer> result = syncService.syncAllRealms();
        assertTrue(result.containsKey("realm1"));
        assertEquals(0, result.get("realm1"));
    }

    @Test
    void testIsKeycloakAvailable() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.serverInfo()).thenReturn(serverInfoResource);
        when(serverInfoResource.getInfo()).thenReturn(new ServerInfoRepresentation());

        assertTrue(syncService.isKeycloakAvailable());
    }

    @Test
    void testGetKeycloakHealthInfo() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.serverInfo()).thenReturn(serverInfoResource);

        ServerInfoRepresentation info = new ServerInfoRepresentation();
        SystemInfoRepresentation systemInfo = new SystemInfoRepresentation();
        systemInfo.setVersion("1.0");
        info.setSystemInfo(systemInfo);

        when(serverInfoResource.getInfo()).thenReturn(info);

        when(keycloakInstance.realms()).thenReturn(realmsResource);
        when(realmsResource.findAll()).thenReturn(Collections.emptyList());

        Map<String, Object> health = syncService.getKeycloakHealthInfo();
        assertTrue((Boolean) health.get("overallHealthy"));
        assertEquals("1.0", health.get("keycloakVersion"));
    }

    @Test
    void testSyncUsersFromRealm_Exception() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.realm("realm")).thenReturn(realmResource);
        when(realmResource.users()).thenReturn(usersResource);
        when(usersResource.list()).thenThrow(new RuntimeException("Connection error"));

        assertThrows(RuntimeException.class, () -> syncService.syncUsersFromRealm("realm"));
    }

    @Test
    void testSyncRolesFromRealm_Exception() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.realm("realm")).thenReturn(realmResource);
        when(realmResource.roles()).thenReturn(rolesResource);
        when(rolesResource.list()).thenThrow(new RuntimeException("Connection error"));

        assertThrows(RuntimeException.class, () -> syncService.syncRolesFromRealm("realm"));
    }

    @Test
    void testSyncAllRealms_WithException() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.realms()).thenReturn(realmsResource);

        RealmRepresentation realmRep = new RealmRepresentation();
        realmRep.setRealm("realm1");
        when(realmsResource.findAll()).thenReturn(Collections.singletonList(realmRep));

        // Mock exception during sync
        when(keycloakInstance.realm("realm1")).thenReturn(realmResource);
        when(realmResource.users()).thenReturn(usersResource);
        when(usersResource.list()).thenThrow(new RuntimeException("Sync error"));

        Map<String, Integer> result = syncService.syncAllRealms();
        assertTrue(result.containsKey("realm1"));
        assertEquals(0, result.get("realm1")); // Should be 0 on error
    }

    @Test
    void testSyncAllRealms_ExceptionInFindAll() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.realms()).thenReturn(realmsResource);
        when(realmsResource.findAll()).thenThrow(new RuntimeException("Connection error"));

        Map<String, Integer> result = syncService.syncAllRealms();
        assertTrue(result.isEmpty());
    }

    // Note: checkDataConsistency doesn't actually throw exceptions in the current implementation
    // The try-catch block is there for future use, but currently always succeeds
    // So we test the success path in testCheckDataConsistency_Success

    @Test
    void testForceSyncRealm_Exception() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.realm("realm")).thenReturn(realmResource);
        when(realmResource.users()).thenReturn(usersResource);
        when(usersResource.list()).thenThrow(new RuntimeException("Sync error"));

        Map<String, Object> stats = syncService.forceSyncRealm("realm");
        assertFalse((Boolean) stats.get("success"));
        assertNotNull(stats.get("error"));
        assertNotNull(stats.get("durationMs"));
    }

    @Test
    void testIsKeycloakAvailable_Exception() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.serverInfo()).thenReturn(serverInfoResource);
        when(serverInfoResource.getInfo()).thenThrow(new RuntimeException("Connection refused"));

        assertFalse(syncService.isKeycloakAvailable());
    }

    @Test
    void testGetKeycloakHealthInfo_Exception() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.serverInfo()).thenReturn(serverInfoResource);
        when(serverInfoResource.getInfo()).thenThrow(new RuntimeException("Connection error"));

        Map<String, Object> health = syncService.getKeycloakHealthInfo();
        assertFalse((Boolean) health.get("overallHealthy"));
        assertFalse((Boolean) health.get("keycloakAccessible"));
        assertNotNull(health.get("errorMessage"));
    }

    @Test
    void testGetKeycloakHealthInfo_RealmsException() {
        when(keycloakAdminClient.getInstance()).thenReturn(keycloakInstance);
        when(keycloakInstance.serverInfo()).thenReturn(serverInfoResource);

        ServerInfoRepresentation info = new ServerInfoRepresentation();
        SystemInfoRepresentation systemInfo = new SystemInfoRepresentation();
        systemInfo.setVersion("1.0");
        info.setSystemInfo(systemInfo);

        when(serverInfoResource.getInfo()).thenReturn(info);

        when(keycloakInstance.realms()).thenReturn(realmsResource);
        when(realmsResource.findAll()).thenThrow(new RuntimeException("Realms error"));

        Map<String, Object> health = syncService.getKeycloakHealthInfo();
        assertTrue((Boolean) health.get("overallHealthy")); // Still healthy if server is accessible
        assertFalse((Boolean) health.get("realmsAccessible"));
    }

    @Test
    void testCheckDataConsistency_Success() {
        Map<String, Object> report = syncService.checkDataConsistency("realm");
        assertEquals("realm", report.get("realmName"));
        assertEquals("ok", report.get("status"));
        assertEquals("Cohérence vérifiée", report.get("message"));
    }

    @Test
    void testGetLastSyncStatus() {
        Map<String, Object> status = syncService.getLastSyncStatus("realm");
        assertEquals("realm", status.get("realmName"));
        assertEquals("completed", status.get("status"));
        assertNotNull(status.get("lastSyncTime"));
    }
}
