package dev.lions.unionflow.client.view;

import dev.lions.unionflow.client.service.MembreService;
import dev.lions.unionflow.server.api.dto.membre.response.MembreResponse;
import io.quarkus.oidc.IdToken;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.SessionScoped;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.eclipse.microprofile.rest.client.inject.RestClient;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.logging.Logger;

/**
 * Gestion de la session utilisateur avec Keycloak OIDC
 *
 * <p>
 * Utilise {@code @IdToken JsonWebToken} pour accéder aux claims du token
 * et {@link SecurityIdentity} pour les vérifications d'authentification,
 * car l'application est en mode OIDC {@code web-app} (authorization code flow).
 *
 * @author UnionFlow Team
 * @version 2.1
 */
@Named("userSession")
@SessionScoped
public class UserSession implements Serializable {

    private static final long serialVersionUID = 1L;
    private static final Logger LOGGER = Logger.getLogger(UserSession.class.getName());

    @Inject
    @IdToken
    private JsonWebToken jwt;

    @Inject
    SecurityIdentity securityIdentity;

    @Inject
    @RestClient
    private MembreService membreService;

    private String username;
    private boolean authenticated = false;
    private String typeCompte;
    private List<String> roles;
    private List<String> permissions;
    private CurrentUser currentUser;
    private EntiteInfo entite;

    // Multi-org context
    private UUID activeOrganisationId;
    private String activeOrganisationNom;
    private List<MembreService.OrganisationSwitcherDTO> mesOrganisations;
    private boolean organisationsChargees = false;

    // Guard thread-safety pour le lazy-load de l'entité
    private volatile boolean entiteEnChargement = false;

    /**
     * Guard contre la réentrance CDI : positionné à true sur le thread courant pendant
     * l'exécution de {@code @PostConstruct init()}, afin d'éviter que
     * {@link dev.lions.unionflow.client.security.AuthHeaderFactory} ne déclenche
     * la création d'une nouvelle instance UserSession en résolvant le proxy CDI
     * pendant l'initialisation (StackOverflowError).
     */
    public static final ThreadLocal<Boolean> INITIALIZING_ON_THREAD = new ThreadLocal<>();

    public UserSession() {
        // Session par défaut non authentifiée
        clearSession();
    }

    /**
     * Initialise automatiquement la session après injection des dépendances
     * Appelé automatiquement par CDI si l'utilisateur est authentifié
     */
    @PostConstruct
    public void init() {
        INITIALIZING_ON_THREAD.set(Boolean.TRUE);
        try {
            if (securityIdentity != null && !securityIdentity.isAnonymous()) {
                LOGGER.info("Initialisation automatique de la session utilisateur (authentifié via OIDC)");
                initializeFromOidcToken();
            } else {
                LOGGER.info("Utilisateur non authentifié, session reste vide");
            }
        } finally {
            INITIALIZING_ON_THREAD.remove();
        }
    }

    /**
     * Initialise la session depuis le token OIDC Keycloak
     * Appelé automatiquement après l'authentification
     */
    public void initializeFromOidcToken() {
        if (jwt != null && jwt.getName() != null) {
            this.authenticated = true;
            this.username = jwt.getClaim("preferred_username");
            if (this.username == null) {
                this.username = jwt.getName();
            }

            // Récupérer les informations du token
            String email = jwt.getClaim("email");
            String givenName = jwt.getClaim("given_name");
            String familyName = jwt.getClaim("family_name");

            // Récupérer les rôles depuis le token
            this.roles = extractRolesFromToken();
            LOGGER.info("Rôles extraits du token: " + (this.roles != null ? this.roles.toString() : "null"));
            this.typeCompte = determineTypeCompte();
            LOGGER.info("Session utilisateur initialisée - Username: " + this.username + ", Type: " + this.typeCompte);

            // Mettre à jour les informations utilisateur
            this.currentUser = new CurrentUser();
            this.currentUser.setUsername(this.username);
            this.currentUser.setEmail(email);
            this.currentUser.setPrenom(givenName);
            this.currentUser.setNom(familyName);

            // Générer un ID depuis le subject du token
            String subject = jwt.getSubject();
            if (subject != null) {
                try {
                    this.currentUser.setId(UUID.fromString(subject));
                } catch (IllegalArgumentException e) {
                    // Si le subject n'est pas un UUID, générer un UUID déterministe
                    this.currentUser.setId(UUID.nameUUIDFromBytes(subject.getBytes()));
                }
            }

            // Charger l'organisation depuis le backend via /api/membres/me
            chargerEntiteDepuisBackend();
        }
    }

    /**
     * Appelle GET /api/membres/me pour récupérer l'organisationId du membre connecté
     * et peupler l'entite en session. Silencieux en cas d'erreur.
     */
    private void chargerEntiteDepuisBackend() {
        try {
            MembreResponse moi = membreService.obtenirMembreConnecte();
            if (moi != null && moi.getOrganisationId() != null) {
                EntiteInfo entiteInfo = new EntiteInfo();
                entiteInfo.setId(moi.getOrganisationId());
                entiteInfo.setNom(moi.getOrganisationNom() != null ? moi.getOrganisationNom() : "");
                this.entite = entiteInfo;
                LOGGER.info("Entité chargée depuis /api/membres/me : " + entiteInfo.getId()
                        + " (" + entiteInfo.getNom() + ")");

                // Charger aussi la liste des organisations pour le switcher
                chargerMesOrganisations();

                // Si aucune org active encore sélectionnée, utiliser celle de /me
                if (this.activeOrganisationId == null) {
                    this.activeOrganisationId = moi.getOrganisationId();
                    this.activeOrganisationNom = moi.getOrganisationNom();
                }
            } else {
                LOGGER.info("Aucune organisation trouvée pour l'utilisateur connecté (SUPER_ADMIN ou non rattaché)");
            }
        } catch (Exception e) {
            LOGGER.warning("Impossible de charger l'entité depuis le backend : " + e.getMessage());
        }
    }

    /**
     * Convertit un objet JSON en String de manière sécurisée
     * Gère les cas où l'objet est un JsonStringImpl, String, ou autre type
     */
    private String convertToString(Object obj) {
        if (obj == null) {
            return null;
        }
        if (obj instanceof String) {
            String str = (String) obj;
            // Nettoyer les guillemets qui pourraient être présents
            str = str.trim();
            if (str.startsWith("'") && str.endsWith("'") && str.length() > 1) {
                str = str.substring(1, str.length() - 1);
            }
            if (str.startsWith("\"") && str.endsWith("\"") && str.length() > 1) {
                str = str.substring(1, str.length() - 1);
            }
            return str.trim();
        }
        // Gérer JsonStringImpl et autres types JSON
        String str = obj.toString();
        // Nettoyer les guillemets qui pourraient être présents
        str = str.trim();
        if (str.startsWith("'") && str.endsWith("'") && str.length() > 1) {
            str = str.substring(1, str.length() - 1);
        }
        if (str.startsWith("\"") && str.endsWith("\"") && str.length() > 1) {
            str = str.substring(1, str.length() - 1);
        }
        return str.trim();
    }

    /**
     * Extrait et convertit une liste de rôles depuis un objet JSON
     */
    private List<String> extractRolesFromList(Object rolesObj) {
        List<String> roles = new ArrayList<>();
        if (rolesObj instanceof List) {
            @SuppressWarnings("unchecked")
            List<Object> rolesList = (List<Object>) rolesObj;
            for (Object roleObj : rolesList) {
                String role = convertToString(roleObj);
                if (role != null && !role.isEmpty()) {
                    // S'assurer que c'est vraiment un String en créant une nouvelle instance
                    roles.add(new String(role));
                    LOGGER.fine("Rôle converti: '" + role + "' (type: " + role.getClass().getName() + ")");
                }
            }
        }
        return roles;
    }

    /**
     * Extrait les rôles depuis le token JWT
     */
    private List<String> extractRolesFromToken() {
        List<String> extractedRoles = new ArrayList<>();

        // Rôles dans "realm_access.roles"
        try {
            Object realmAccess = jwt.getClaim("realm_access");
            if (realmAccess instanceof java.util.Map) {
                @SuppressWarnings("unchecked")
                java.util.Map<String, Object> realmMap = (java.util.Map<String, Object>) realmAccess;
                Object rolesObj = realmMap.get("roles");
                List<String> realmRoles = extractRolesFromList(rolesObj);
                if (!realmRoles.isEmpty()) {
                    extractedRoles.addAll(realmRoles);
                    LOGGER.info("Rôles extraits depuis realm_access.roles: " + realmRoles);
                }
            } else if (realmAccess instanceof List) {
                // Fallback: si realm_access est directement une liste de rôles
                List<String> realmRoles = extractRolesFromList(realmAccess);
                if (!realmRoles.isEmpty()) {
                    extractedRoles.addAll(realmRoles);
                    LOGGER.info("Rôles extraits depuis realm_access (liste): " + realmRoles);
                }
            }
        } catch (Exception e) {
            LOGGER.warning("Erreur lors de l'extraction des rôles realm: " + e.getMessage());
        }

        // Rôles dans "resource_access"
        try {
            Object resourceAccess = jwt.getClaim("resource_access");
            if (resourceAccess instanceof java.util.Map) {
                @SuppressWarnings("unchecked")
                java.util.Map<String, Object> resourceMap = (java.util.Map<String, Object>) resourceAccess;
                for (Object value : resourceMap.values()) {
                    if (value instanceof java.util.Map) {
                        @SuppressWarnings("unchecked")
                        java.util.Map<String, Object> clientMap = (java.util.Map<String, Object>) value;
                        Object rolesObj = clientMap.get("roles");
                        List<String> clientRoles = extractRolesFromList(rolesObj);
                        if (!clientRoles.isEmpty()) {
                            extractedRoles.addAll(clientRoles);
                            LOGGER.info("Rôles extraits depuis resource_access: " + clientRoles);
                        }
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.warning("Erreur lors de l'extraction des rôles client: " + e.getMessage());
        }

        // Fallback: essayer d'extraire les rôles depuis le claim "roles" directement
        if (extractedRoles.isEmpty()) {
            try {
                Object rolesClaim = jwt.getClaim("roles");
                List<String> directRoles = extractRolesFromList(rolesClaim);
                if (!directRoles.isEmpty()) {
                    extractedRoles.addAll(directRoles);
                    LOGGER.info("Rôles extraits depuis claim 'roles': " + directRoles);
                }
            } catch (Exception e) {
                LOGGER.warning("Erreur lors de l'extraction des rôles depuis claim 'roles': " + e.getMessage());
            }
        }

        if (extractedRoles.isEmpty()) {
            LOGGER.warning("⚠️ AUCUN RÔLE EXTRAIT DU TOKEN JWT - Vérifier la configuration Keycloak");
            LOGGER.warning("Vérifier que le mapper 'realm roles' est configuré dans Keycloak Client");
            LOGGER.warning("Vérifier que 'Add to access token' est activé pour le mapper");
        } else {
            LOGGER.info("✅ Total des rôles extraits: " + extractedRoles);
        }
        return extractedRoles;
    }

    /**
     * Détermine le type de compte depuis les rôles
     */
    private String determineTypeCompte() {
        // Utiliser this.roles pour s'assurer qu'on utilise la bonne variable d'instance
        List<String> rolesToCheck = this.roles;

        if (rolesToCheck == null || rolesToCheck.isEmpty()) {
            LOGGER.warning("Aucun rôle trouvé, type de compte par défaut: MEMBRE");
            return "MEMBRE";
        }

        LOGGER.info("Détermination du type de compte depuis les rôles: " + rolesToCheck);
        LOGGER.info("Nombre de rôles: " + rolesToCheck.size());

        // Vérifier le type des éléments de la liste
        if (!rolesToCheck.isEmpty()) {
            Object firstRole = rolesToCheck.get(0);
            LOGGER.info("Type du premier rôle: " + (firstRole != null ? firstRole.getClass().getName() : "null"));
            LOGGER.info("Premier rôle (toString): '" + firstRole + "'");
            LOGGER.info("Premier rôle (length): " + (firstRole != null ? firstRole.toString().length() : 0));
            // Vérifier les caractères du premier rôle
            if (firstRole != null) {
                String firstRoleStr = firstRole.toString();
                LOGGER.info("Premier rôle (bytes): " + java.util.Arrays.toString(firstRoleStr.getBytes()));
            }
        }

        // Vérifier SUPER_ADMIN et SUPER_ADMINISTRATEUR en parcourant la liste (plus
        // robuste que contains)
        for (String role : rolesToCheck) {
            if (role != null) {
                // Nettoyer la chaîne : retirer les guillemets et espaces
                String roleStr = role.toString().trim();
                // Retirer les guillemets simples et doubles au début et à la fin
                if (roleStr.startsWith("'") && roleStr.endsWith("'")) {
                    roleStr = roleStr.substring(1, roleStr.length() - 1);
                }
                if (roleStr.startsWith("\"") && roleStr.endsWith("\"")) {
                    roleStr = roleStr.substring(1, roleStr.length() - 1);
                }
                roleStr = roleStr.trim();

                LOGGER.info("Vérification du rôle: '" + roleStr + "' (longueur: " + roleStr.length() + ", original: '"
                        + role + "')");

                // Vérifier SUPER_ADMINISTRATEUR (rôle Keycloak)
                if ("SUPER_ADMINISTRATEUR".equals(roleStr) ||
                        "SUPER_ADMINISTRATEUR".equalsIgnoreCase(roleStr) ||
                        "super-administrateur".equalsIgnoreCase(roleStr)) {
                    LOGGER.info("✅ Type de compte détecté: SUPER_ADMIN (rôle Keycloak trouvé: '" + roleStr + "')");
                    return "SUPER_ADMIN";
                }

                // Vérifier SUPER_ADMIN (alias)
                if ("SUPER_ADMIN".equals(roleStr) || "super-admin".equalsIgnoreCase(roleStr)) {
                    LOGGER.info("✅ Type de compte détecté: SUPER_ADMIN (rôle trouvé: '" + roleStr + "')");
                    return "SUPER_ADMIN";
                }
            }
        }

        // Fallback: utiliser contains() pour compatibilité
        boolean hasSuperAdmin = rolesToCheck.contains("SUPER_ADMIN");
        boolean hasSuperAdminLower = rolesToCheck.contains("super-admin");
        boolean hasSuperAdministrateur = rolesToCheck.contains("SUPER_ADMINISTRATEUR");
        boolean hasSuperAdministrateurLower = rolesToCheck.contains("super-administrateur");

        LOGGER.info("Contient 'SUPER_ADMIN' (contains): " + hasSuperAdmin);
        LOGGER.info("Contient 'super-admin' (contains): " + hasSuperAdminLower);
        LOGGER.info("Contient 'SUPER_ADMINISTRATEUR' (contains): " + hasSuperAdministrateur);
        LOGGER.info("Contient 'super-administrateur' (contains): " + hasSuperAdministrateurLower);

        if (hasSuperAdmin || hasSuperAdminLower || hasSuperAdministrateur || hasSuperAdministrateurLower) {
            LOGGER.info("✅ Type de compte détecté: SUPER_ADMIN (via contains)");
            return "SUPER_ADMIN";
        }

        // Vérifier le rôle ADMIN global (équivalent SUPER_ADMIN)
        if (rolesToCheck.contains("ADMIN")) {
            LOGGER.info("Type de compte détecté: SUPER_ADMIN (via rôle ADMIN)");
            return "SUPER_ADMIN";
        }

        // Vérifier ADMIN_ORGANISATION et rôles du bureau
        if (rolesToCheck.contains("ADMIN_ORGANISATION")) {
            LOGGER.info("Type de compte détecté: ADMIN_ORGANISATION");
            return "ADMIN_ORGANISATION";
        }
        if (rolesToCheck.contains("PRESIDENT")) {
            LOGGER.info("Type de compte détecté: ADMIN_ORGANISATION (via rôle PRESIDENT)");
            return "ADMIN_ORGANISATION";
        }

        // Modérateur plateforme (rôle Keycloak MODERATOR)
        if (rolesToCheck.contains("MODERATOR") || rolesToCheck.contains("MODERATEUR")) {
            LOGGER.info("Type de compte détecté: MODERATEUR (rôle plateforme)");
            return "MODERATEUR";
        }

        // Consultant (accès lecture seule étendu)
        if (rolesToCheck.contains("CONSULTANT")) {
            LOGGER.info("Type de compte détecté: CONSULTANT");
            return "CONSULTANT";
        }

        // Gestionnaire RH
        if (rolesToCheck.contains("GESTIONNAIRE_RH")) {
            LOGGER.info("Type de compte détecté: GESTIONNAIRE_RH");
            return "GESTIONNAIRE_RH";
        }

        // Modérateurs / responsables fonctionnels
        for (String moderatorRole : java.util.Arrays.asList(
                "TRESORIER", "SECRETAIRE", "RESPONSABLE_MEMBRES",
                "RESPONSABLE_TECHNIQUE", "RESPONSABLE_SOCIAL",
                "RESPONSABLE_EVENEMENTS", "RESPONSABLE_CREDIT",
                "ORGANISATEUR_EVENEMENT", "GESTIONNAIRE_MEMBRE",
                "VICE_PRESIDENT")) {
            if (rolesToCheck.contains(moderatorRole)) {
                LOGGER.info("Type de compte détecté: MODERATEUR (via rôle " + moderatorRole + ")");
                return "MODERATEUR";
            }
        }

        // Membre actif (participation aux activités)
        if (rolesToCheck.contains("ACTIVEMEMBER") || rolesToCheck.contains("MEMBRE_ACTIF")) {
            LOGGER.info("Type de compte détecté: MEMBRE_ACTIF");
            return "MEMBRE_ACTIF";
        }

        // Visiteur (lecture seule)
        if (rolesToCheck.contains("VISITOR") || rolesToCheck.contains("VISITEUR")) {
            LOGGER.info("Type de compte détecté: VISITEUR");
            return "VISITEUR";
        }

        LOGGER.info("Aucun rôle spécialisé trouvé, type de compte par défaut: MEMBRE");
        return "MEMBRE";
    }

    public void clearSession() {
        this.authenticated = false;
        this.username = null;
        this.typeCompte = null;
        this.roles = null;
        this.permissions = null;
        this.currentUser = null;
        this.entite = null;
        this.activeOrganisationId = null;
        this.activeOrganisationNom = null;
        this.mesOrganisations = null;
        this.organisationsChargees = false;

        LOGGER.info("Session utilisateur effacée");
    }

    // =========================================================
    //  Multi-org context switching
    // =========================================================

    /**
     * Retourne la liste des organisations actives du membre connecté.
     * Chargement paresseux : un seul appel backend par session.
     */
    public List<MembreService.OrganisationSwitcherDTO> getMesOrganisations() {
        if (!organisationsChargees && authenticated && !isSuperAdmin()) {
            chargerMesOrganisations();
        }
        return mesOrganisations != null ? mesOrganisations : Collections.emptyList();
    }

    /**
     * Charge les organisations depuis GET /api/membres/mes-organisations.
     */
    private void chargerMesOrganisations() {
        try {
            List<MembreService.OrganisationSwitcherDTO> orgs = membreService.mesOrganisations();
            this.mesOrganisations = orgs != null ? orgs : Collections.emptyList();
            this.organisationsChargees = true;

            // Auto-select si une seule org
            if (this.activeOrganisationId == null && this.mesOrganisations.size() == 1) {
                appliquerOrganisationActive(this.mesOrganisations.get(0));
            }

            LOGGER.info("Organisations chargées : " + this.mesOrganisations.size());
        } catch (Exception e) {
            LOGGER.warning("Impossible de charger mes organisations : " + e.getMessage());
            this.mesOrganisations = Collections.emptyList();
            this.organisationsChargees = true;
        }
    }

    /**
     * Appelé depuis le sélecteur dans la topbar — change l'organisation active
     * et rafraîchit la page courante.
     *
     * @param orgIdStr UUID de l'organisation cible (String depuis la vue JSF)
     */
    public void changerOrganisationActive(String orgIdStr) {
        if (orgIdStr == null || orgIdStr.isBlank()) {
            return;
        }
        try {
            UUID orgId = UUID.fromString(orgIdStr.trim());
            // Trouver le DTO correspondant
            MembreService.OrganisationSwitcherDTO cible = getMesOrganisations().stream()
                .filter(o -> orgId.toString().equals(o.getOrganisationId()))
                .findFirst()
                .orElse(null);

            if (cible != null) {
                appliquerOrganisationActive(cible);
                LOGGER.info("Organisation active changée : " + activeOrganisationNom + " (" + activeOrganisationId + ")");
            } else {
                LOGGER.warning("Organisation introuvable dans la liste : " + orgIdStr);
                FacesContext fc = FacesContext.getCurrentInstance();
                if (fc != null) {
                    fc.addMessage(null, new FacesMessage(FacesMessage.SEVERITY_WARN,
                        "Organisation introuvable", "Vous n'êtes pas membre de cette organisation."));
                }
            }
        } catch (IllegalArgumentException e) {
            LOGGER.warning("UUID organisation invalide : " + orgIdStr);
        }
    }

    private void appliquerOrganisationActive(MembreService.OrganisationSwitcherDTO org) {
        this.activeOrganisationId = UUID.fromString(org.getOrganisationId());
        this.activeOrganisationNom = org.getNom();
        // Mettre à jour l'entite pour compatibilité avec le code existant
        if (this.entite == null) {
            this.entite = new EntiteInfo();
        }
        this.entite.setId(this.activeOrganisationId);
        this.entite.setNom(org.getNom());
        this.entite.setType(org.getTypeOrganisation());
    }

    public boolean hasMultipleOrganisations() {
        return getMesOrganisations().size() > 1;
    }

    /**
     * Retourne le type de l'organisation active (ex: ASSOCIATION, TONTINE, MUTUELLE...).
     * Cherche dans la liste des organisations de l'utilisateur.
     */
    public String getActiveOrganisationType() {
        if (activeOrganisationId == null) return null;
        return getMesOrganisations().stream()
            .filter(o -> activeOrganisationId.toString().equals(o.getOrganisationId()))
            .map(MembreService.OrganisationSwitcherDTO::getTypeOrganisation)
            .findFirst()
            .orElse(entite != null ? entite.getType() : null);
    }

    /**
     * Vérifie si un module est actif pour l'organisation active.
     * Les modules actifs sont stockés sous forme de chaîne CSV dans la réponse du backend.
     *
     * @param module Nom du module ex: "TONTINE", "EPARGNE", "CREDIT"
     */
    public boolean hasModuleActif(String module) {
        if (module == null || activeOrganisationId == null) return false;
        return getMesOrganisations().stream()
            .filter(o -> activeOrganisationId.toString().equals(o.getOrganisationId()))
            .map(MembreService.OrganisationSwitcherDTO::getModulesActifs)
            .filter(m -> m != null && !m.isBlank())
            .anyMatch(modules -> {
                for (String m : modules.split(",")) {
                    if (module.equalsIgnoreCase(m.trim())) return true;
                }
                return false;
            });
    }

    public UUID getActiveOrganisationId() {
        return activeOrganisationId;
    }

    public void setActiveOrganisationId(UUID activeOrganisationId) {
        this.activeOrganisationId = activeOrganisationId;
    }

    public String getActiveOrganisationNom() {
        if (activeOrganisationNom != null) return activeOrganisationNom;
        if (entite != null && entite.getNom() != null) return entite.getNom();
        return "—";
    }

    public void setActiveOrganisationNom(String activeOrganisationNom) {
        this.activeOrganisationNom = activeOrganisationNom;
    }

    // Méthodes de vérification des rôles et permissions
    public boolean hasRole(String role) {
        if (roles == null || roles.isEmpty()) {
            return false;
        }

        // Vérification directe
        if (roles.contains(role)) {
            return true;
        }

        // Vérification case-insensitive
        for (String r : roles) {
            if (r != null && r.equalsIgnoreCase(role)) {
                return true;
            }
        }

        // Mapping spécial pour SUPER_ADMIN
        if ("SUPER_ADMIN".equals(role)) {
            return roles.contains("SUPER_ADMINISTRATEUR") ||
                    roles.stream().anyMatch(r -> r != null && r.equalsIgnoreCase("SUPER_ADMINISTRATEUR"));
        }

        // Mapping spécial pour SUPER_ADMINISTRATEUR
        if ("SUPER_ADMINISTRATEUR".equals(role)) {
            return roles.contains("SUPER_ADMIN") ||
                    roles.stream().anyMatch(r -> r != null && r.equalsIgnoreCase("SUPER_ADMIN"));
        }

        return false;
    }

    public boolean hasPermission(String permission) {
        return permissions != null && permissions.contains(permission);
    }

    public boolean isSuperAdmin() {
        return "SUPER_ADMIN".equals(typeCompte) ||
                hasRole("SUPER_ADMIN") ||
                hasRole("SUPER_ADMINISTRATEUR");
    }

    public boolean isAdmin() {
        return isSuperAdmin()
                || "ADMIN_ORGANISATION".equals(typeCompte)
                || hasRole("ADMIN_ORGANISATION")
                || hasRole("PRESIDENT");
    }

    public boolean isMembre() {
        // Tout utilisateur authentifié (admin inclus) peut accéder aux pages membre
        return typeCompte != null || (securityIdentity != null && !securityIdentity.isAnonymous());
    }

    public boolean isModerateur() {
        return "MODERATEUR".equals(typeCompte)
                || hasRole("MODERATOR") || hasRole("MODERATEUR")
                || hasRole("TRESORIER") || hasRole("SECRETAIRE")
                || hasRole("RESPONSABLE_MEMBRES") || hasRole("RESPONSABLE_TECHNIQUE")
                || hasRole("RESPONSABLE_SOCIAL") || hasRole("RESPONSABLE_EVENEMENTS")
                || hasRole("RESPONSABLE_CREDIT") || hasRole("ORGANISATEUR_EVENEMENT")
                || hasRole("GESTIONNAIRE_MEMBRE") || hasRole("VICE_PRESIDENT");
    }

    public boolean isMembreActif() {
        return "MEMBRE_ACTIF".equals(typeCompte) || hasRole("MEMBRE_ACTIF") || hasRole("ACTIVEMEMBER");
    }

    public boolean isConsultant() {
        return "CONSULTANT".equals(typeCompte) || hasRole("CONSULTANT");
    }

    public boolean isGestionnaireRh() {
        return "GESTIONNAIRE_RH".equals(typeCompte) || hasRole("GESTIONNAIRE_RH");
    }

    public boolean isVisiteur() {
        return "VISITEUR".equals(typeCompte) || hasRole("VISITOR") || hasRole("VISITEUR");
    }

    public boolean hasReadOnlyAccess() {
        return isVisiteur() || isConsultant();
    }

    // Méthode pour obtenir le rôle principal
    public String getRole() {
        if (isSuperAdmin()) {
            return "SUPER_ADMIN";
        }
        if (isAdmin()) {
            return "ADMIN";
        }
        if (typeCompte != null) {
            return typeCompte;
        }
        if (roles != null && !roles.isEmpty()) {
            return roles.get(0);
        }
        return "MEMBER";
    }

    // Getters et Setters
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public boolean isAuthenticated() {
        // Vérifier via SecurityIdentity (correct pour le mode web-app OIDC)
        if (securityIdentity != null && !securityIdentity.isAnonymous() && !authenticated) {
            initializeFromOidcToken();
        }
        return authenticated || (securityIdentity != null && !securityIdentity.isAnonymous());
    }

    public void setAuthenticated(boolean authenticated) {
        this.authenticated = authenticated;
    }

    public String getTypeCompte() {
        // Si le type de compte n'est pas encore déterminé, l'initialiser
        if (typeCompte == null && securityIdentity != null && !securityIdentity.isAnonymous()) {
            LOGGER.info("getTypeCompte() appelé avant initialisation, initialisation en cours...");
            initializeFromOidcToken();
        }
        return typeCompte;
    }

    public void setTypeCompte(String typeCompte) {
        this.typeCompte = typeCompte;
    }

    public List<String> getRoles() {
        return roles;
    }

    public void setRoles(List<String> roles) {
        this.roles = roles;
    }

    public List<String> getPermissions() {
        return permissions;
    }

    public void setPermissions(List<String> permissions) {
        this.permissions = permissions;
    }

    public CurrentUser getCurrentUser() {
        return currentUser;
    }

    public void setCurrentUser(CurrentUser currentUser) {
        this.currentUser = currentUser;
    }

    public EntiteInfo getEntite() {
        // Lazy-load thread-safe : double-checked locking pour éviter les appels concurrents
        if (entite == null && authenticated && !isSuperAdmin() && !entiteEnChargement) {
            synchronized (this) {
                if (entite == null && !entiteEnChargement) {
                    entiteEnChargement = true;
                    try {
                        LOGGER.info("getEntite() — entite null, tentative de rechargement depuis /api/membres/me");
                        chargerEntiteDepuisBackend();
                    } finally {
                        entiteEnChargement = false;
                    }
                }
            }
        }
        return entite;
    }

    public void setEntite(EntiteInfo entite) {
        this.entite = entite;
    }

    // Classes internes
    public static class CurrentUser implements Serializable {
        private UUID id;
        private String nom;
        private String prenom;
        private String email;
        private String username;

        public String getNomComplet() {
            if (prenom != null && nom != null) {
                return prenom + " " + nom;
            }
            return nom != null ? nom : username;
        }

        public String getInitiales() {
            StringBuilder initiales = new StringBuilder();
            if (prenom != null && !prenom.isEmpty()) {
                initiales.append(prenom.charAt(0));
            }
            if (nom != null && !nom.isEmpty()) {
                initiales.append(nom.charAt(0));
            }
            return initiales.toString().toUpperCase();
        }

        // Getters et Setters
        public UUID getId() {
            return id;
        }

        public void setId(UUID id) {
            this.id = id;
        }

        public String getNom() {
            return nom;
        }

        public void setNom(String nom) {
            this.nom = nom;
        }

        public String getPrenom() {
            return prenom;
        }

        public void setPrenom(String prenom) {
            this.prenom = prenom;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }

        public String getUsername() {
            return username;
        }

        public void setUsername(String username) {
            this.username = username;
        }
    }

    public static class EntiteInfo implements Serializable {
        private UUID id;
        private String nom;
        private String type;
        private String pays;
        private String ville;

        public String getDescription() {
            StringBuilder desc = new StringBuilder();
            if (nom != null) {
                desc.append(nom);
            }
            if (ville != null && pays != null) {
                desc.append(" (").append(ville).append(", ").append(pays).append(")");
            }
            return desc.toString();
        }

        // Getters et Setters
        public UUID getId() {
            return id;
        }

        public void setId(UUID id) {
            this.id = id;
        }

        public String getNom() {
            return nom;
        }

        public void setNom(String nom) {
            this.nom = nom;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getPays() {
            return pays;
        }

        public void setPays(String pays) {
            this.pays = pays;
        }

        public String getVille() {
            return ville;
        }

        public void setVille(String ville) {
            this.ville = ville;
        }
    }
}
