package dev.lions.unionflow.server.security;

import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.jboss.logging.Logger;

import java.util.Set;
import java.util.stream.Collectors;

/**
 * Service pour l'intégration avec Keycloak et la gestion de la sécurité
 * Fournit des méthodes utilitaires pour accéder aux informations de l'utilisateur connecté
 * 
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-15
 */
@ApplicationScoped
public class KeycloakService {

    private static final Logger LOG = Logger.getLogger(KeycloakService.class);

    @Inject
    SecurityIdentity securityIdentity;

    @Inject
    JsonWebToken jwt;

    /**
     * Récupère l'email de l'utilisateur actuellement connecté
     * 
     * @return l'email de l'utilisateur ou null si non connecté
     */
    public String getCurrentUserEmail() {
        if (securityIdentity == null || securityIdentity.isAnonymous()) {
            LOG.debug("Aucun utilisateur connecté");
            return null;
        }

        try {
            // Essayer d'abord avec le claim 'email'
            if (jwt != null && jwt.containsClaim("email")) {
                String email = jwt.getClaim("email");
                LOG.debugf("Email récupéré depuis JWT: %s", email);
                return email;
            }

            // Fallback sur le nom principal
            String principal = securityIdentity.getPrincipal().getName();
            LOG.debugf("Email récupéré depuis principal: %s", principal);
            return principal;

        } catch (Exception e) {
            LOG.warnf("Erreur lors de la récupération de l'email utilisateur: %s", e.getMessage());
            return null;
        }
    }

    /**
     * Récupère l'ID utilisateur Keycloak de l'utilisateur actuellement connecté
     * 
     * @return l'ID utilisateur Keycloak ou null si non connecté
     */
    public String getCurrentUserId() {
        if (securityIdentity == null || securityIdentity.isAnonymous()) {
            return null;
        }

        try {
            if (jwt != null && jwt.containsClaim("sub")) {
                String userId = jwt.getClaim("sub");
                LOG.debugf("ID utilisateur récupéré: %s", userId);
                return userId;
            }
        } catch (Exception e) {
            LOG.warnf("Erreur lors de la récupération de l'ID utilisateur: %s", e.getMessage());
        }

        return null;
    }

    /**
     * Récupère le nom complet de l'utilisateur actuellement connecté
     * 
     * @return le nom complet ou null si non disponible
     */
    public String getCurrentUserFullName() {
        if (securityIdentity == null || securityIdentity.isAnonymous()) {
            return null;
        }

        try {
            if (jwt != null) {
                // Essayer le claim 'name' en premier
                if (jwt.containsClaim("name")) {
                    return jwt.getClaim("name");
                }

                // Construire à partir de given_name et family_name
                String givenName = jwt.containsClaim("given_name") ? jwt.getClaim("given_name") : "";
                String familyName = jwt.containsClaim("family_name") ? jwt.getClaim("family_name") : "";
                
                if (!givenName.isEmpty() || !familyName.isEmpty()) {
                    return (givenName + " " + familyName).trim();
                }

                // Fallback sur preferred_username
                if (jwt.containsClaim("preferred_username")) {
                    return jwt.getClaim("preferred_username");
                }
            }
        } catch (Exception e) {
            LOG.warnf("Erreur lors de la récupération du nom complet: %s", e.getMessage());
        }

        return getCurrentUserEmail(); // Fallback sur l'email
    }

    /**
     * Récupère le prénom de l'utilisateur actuellement connecté
     * 
     * @return le prénom ou null si non disponible
     */
    public String getCurrentUserFirstName() {
        if (securityIdentity == null || securityIdentity.isAnonymous()) {
            return null;
        }

        try {
            if (jwt != null && jwt.containsClaim("given_name")) {
                return jwt.getClaim("given_name");
            }
        } catch (Exception e) {
            LOG.warnf("Erreur lors de la récupération du prénom: %s", e.getMessage());
        }

        return null;
    }

    /**
     * Récupère le nom de famille de l'utilisateur actuellement connecté
     * 
     * @return le nom de famille ou null si non disponible
     */
    public String getCurrentUserLastName() {
        if (securityIdentity == null || securityIdentity.isAnonymous()) {
            return null;
        }

        try {
            if (jwt != null && jwt.containsClaim("family_name")) {
                return jwt.getClaim("family_name");
            }
        } catch (Exception e) {
            LOG.warnf("Erreur lors de la récupération du nom de famille: %s", e.getMessage());
        }

        return null;
    }

    /**
     * Vérifie si l'utilisateur actuel possède un rôle spécifique
     * 
     * @param role le nom du rôle à vérifier
     * @return true si l'utilisateur possède le rôle
     */
    public boolean hasRole(String role) {
        if (securityIdentity == null || securityIdentity.isAnonymous()) {
            return false;
        }

        try {
            boolean hasRole = securityIdentity.hasRole(role);
            LOG.debugf("Vérification du rôle '%s' pour l'utilisateur: %s", role, hasRole);
            return hasRole;
        } catch (Exception e) {
            LOG.warnf("Erreur lors de la vérification du rôle '%s': %s", role, e.getMessage());
            return false;
        }
    }

    /**
     * Vérifie si l'utilisateur actuel possède au moins un des rôles spécifiés
     * 
     * @param roles les rôles à vérifier
     * @return true si l'utilisateur possède au moins un des rôles
     */
    public boolean hasAnyRole(String... roles) {
        if (roles == null || roles.length == 0) {
            return false;
        }

        for (String role : roles) {
            if (hasRole(role)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Vérifie si l'utilisateur actuel possède tous les rôles spécifiés
     * 
     * @param roles les rôles à vérifier
     * @return true si l'utilisateur possède tous les rôles
     */
    public boolean hasAllRoles(String... roles) {
        if (roles == null || roles.length == 0) {
            return true;
        }

        for (String role : roles) {
            if (!hasRole(role)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Récupère tous les rôles de l'utilisateur actuel
     * 
     * @return ensemble des rôles de l'utilisateur
     */
    public Set<String> getCurrentUserRoles() {
        if (securityIdentity == null || securityIdentity.isAnonymous()) {
            return Set.of();
        }

        try {
            Set<String> roles = securityIdentity.getRoles();
            LOG.debugf("Rôles de l'utilisateur actuel: %s", roles);
            return roles;
        } catch (Exception e) {
            LOG.warnf("Erreur lors de la récupération des rôles: %s", e.getMessage());
            return Set.of();
        }
    }

    /**
     * Vérifie si l'utilisateur actuel est un administrateur
     * 
     * @return true si l'utilisateur est administrateur
     */
    public boolean isAdmin() {
        return hasAnyRole("admin", "administrator", "super_admin");
    }

    /**
     * Vérifie si l'utilisateur actuel est connecté (non anonyme)
     * 
     * @return true si l'utilisateur est connecté
     */
    public boolean isAuthenticated() {
        return securityIdentity != null && !securityIdentity.isAnonymous();
    }

    /**
     * Récupère une claim spécifique du JWT
     * 
     * @param claimName nom de la claim
     * @return valeur de la claim ou null si non trouvée
     */
    public <T> T getClaim(String claimName, Class<T> claimType) {
        if (jwt == null || !jwt.containsClaim(claimName)) {
            return null;
        }

        try {
            return jwt.getClaim(claimName);
        } catch (Exception e) {
            LOG.warnf("Erreur lors de la récupération de la claim '%s': %s", claimName, e.getMessage());
            return null;
        }
    }

    /**
     * Récupère les groupes de l'utilisateur depuis le JWT
     * 
     * @return ensemble des groupes de l'utilisateur
     */
    public Set<String> getCurrentUserGroups() {
        if (jwt == null) {
            return Set.of();
        }

        try {
            if (jwt.containsClaim("groups")) {
                Object groups = jwt.getClaim("groups");
                if (groups instanceof Set) {
                    return ((Set<?>) groups).stream()
                            .map(Object::toString)
                            .collect(Collectors.toSet());
                }
            }
        } catch (Exception e) {
            LOG.warnf("Erreur lors de la récupération des groupes: %s", e.getMessage());
        }

        return Set.of();
    }

    /**
     * Vérifie si l'utilisateur appartient à un groupe spécifique
     * 
     * @param groupName nom du groupe
     * @return true si l'utilisateur appartient au groupe
     */
    public boolean isMemberOfGroup(String groupName) {
        return getCurrentUserGroups().contains(groupName);
    }

    /**
     * Récupère l'organisation de l'utilisateur depuis le JWT
     * 
     * @return ID de l'organisation ou null si non disponible
     */
    public String getCurrentUserOrganization() {
        return getClaim("organization", String.class);
    }

    /**
     * Log les informations de l'utilisateur actuel (pour debug)
     */
    public void logCurrentUserInfo() {
        if (!LOG.isDebugEnabled()) {
            return;
        }

        LOG.debugf("=== Informations utilisateur actuel ===");
        LOG.debugf("Email: %s", getCurrentUserEmail());
        LOG.debugf("ID: %s", getCurrentUserId());
        LOG.debugf("Nom complet: %s", getCurrentUserFullName());
        LOG.debugf("Rôles: %s", getCurrentUserRoles());
        LOG.debugf("Groupes: %s", getCurrentUserGroups());
        LOG.debugf("Organisation: %s", getCurrentUserOrganization());
        LOG.debugf("Authentifié: %s", isAuthenticated());
        LOG.debugf("Admin: %s", isAdmin());
        LOG.debugf("=====================================");
    }
}
