package dev.lions.user.manager.client;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.quarkus.runtime.Startup;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.enterprise.context.ApplicationScoped;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.faulttolerance.Timeout;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.admin.client.resource.RolesResource;
import org.keycloak.admin.client.resource.UsersResource;

import jakarta.ws.rs.NotFoundException;
import java.time.temporal.ChronoUnit;

/**
 * Implémentation du client Keycloak Admin
 * Utilise Circuit Breaker, Retry et Timeout pour la résilience
 */
@ApplicationScoped
@Startup
@Slf4j
public class KeycloakAdminClientImpl implements KeycloakAdminClient {

    @ConfigProperty(name = "lions.keycloak.server-url")
    String serverUrl;

    @ConfigProperty(name = "lions.keycloak.admin-realm")
    String adminRealm;

    @ConfigProperty(name = "lions.keycloak.admin-client-id")
    String adminClientId;

    @ConfigProperty(name = "lions.keycloak.admin-username")
    String adminUsername;

    @ConfigProperty(name = "lions.keycloak.admin-password")
    String adminPassword;

    @ConfigProperty(name = "lions.keycloak.connection-pool-size", defaultValue = "10")
    Integer connectionPoolSize;

    @ConfigProperty(name = "lions.keycloak.timeout-seconds", defaultValue = "30")
    Integer timeoutSeconds;

    private Keycloak keycloak;

    @PostConstruct
    void init() {
        log.info("========================================");
        log.info("Initialisation du client Keycloak Admin");
        log.info("========================================");
        log.info("Server URL: {}", serverUrl);
        log.info("Admin Realm: {}", adminRealm);
        log.info("Admin Client ID: {}", adminClientId);
        log.info("Admin Username: {}", adminUsername);
        log.info("Connection Pool Size: {}", connectionPoolSize);
        log.info("Timeout: {} secondes", timeoutSeconds);

        try {
            this.keycloak = KeycloakBuilder.builder()
                .serverUrl(serverUrl)
                .realm(adminRealm)
                .clientId(adminClientId)
                .username(adminUsername)
                .password(adminPassword)
                .build();

            log.info("✅ Client Keycloak initialisé (connexion lazy)");
            log.info("La connexion sera établie lors de la première requête API");
        } catch (Exception e) {
            log.warn("⚠️ Échec de l'initialisation du client Keycloak");
            log.warn("URL: {}", serverUrl);
            log.warn("Realm: {}", adminRealm);
            log.warn("Username: {}", adminUsername);
            log.warn("Message: {}", e.getMessage());
            // Ne pas bloquer le démarrage - la connexion sera tentée lors du premier appel
            this.keycloak = null;
        }
    }

    @Override
    @Retry(maxRetries = 3, delay = 2, delayUnit = ChronoUnit.SECONDS)
    @Timeout(value = 30, unit = ChronoUnit.SECONDS)
    @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 5000)
    public Keycloak getInstance() {
        if (keycloak == null) {
            log.warn("Instance Keycloak null, tentative de réinitialisation...");
            init();
        }
        return keycloak;
    }

    @Override
    @Retry(maxRetries = 3, delay = 2, delayUnit = ChronoUnit.SECONDS)
    @Timeout(value = 30, unit = ChronoUnit.SECONDS)
    @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 5000)
    public RealmResource getRealm(String realmName) {
        try {
            return getInstance().realm(realmName);
        } catch (Exception e) {
            log.error("Erreur lors de la récupération du realm {}: {}", realmName, e.getMessage());
            throw new RuntimeException("Impossible de récupérer le realm: " + realmName, e);
        }
    }

    @Override
    @Retry(maxRetries = 3, delay = 2, delayUnit = ChronoUnit.SECONDS)
    @Timeout(value = 30, unit = ChronoUnit.SECONDS)
    @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 5000)
    public UsersResource getUsers(String realmName) {
        return getRealm(realmName).users();
    }

    @Override
    @Retry(maxRetries = 3, delay = 2, delayUnit = ChronoUnit.SECONDS)
    @Timeout(value = 30, unit = ChronoUnit.SECONDS)
    @CircuitBreaker(requestVolumeThreshold = 10, failureRatio = 0.5, delay = 5000)
    public RolesResource getRoles(String realmName) {
        return getRealm(realmName).roles();
    }

    @Override
    public boolean isConnected() {
        try {
            if (keycloak == null) {
                return false;
            }
            keycloak.serverInfo().getInfo();
            return true;
        } catch (Exception e) {
            log.warn("Keycloak non connecté: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public boolean realmExists(String realmName) {
        try {
            getRealm(realmName).toRepresentation();
            return true;
        } catch (NotFoundException e) {
            return false;
        } catch (Exception e) {
            log.error("Erreur lors de la vérification de l'existence du realm {}: {}", realmName, e.getMessage());
            return false;
        }
    }

    @PreDestroy
    @Override
    public void close() {
        if (keycloak != null) {
            log.info("Fermeture de la connexion Keycloak...");
            keycloak.close();
            keycloak = null;
        }
    }

    @Override
    public void reconnect() {
        log.info("Reconnexion à Keycloak...");
        close();
        init();
    }
}
