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

import dev.lions.user.manager.client.KeycloakAdminClient;
import dev.lions.user.manager.dto.role.RoleAssignmentDTO;
import dev.lions.user.manager.dto.role.RoleDTO;
import dev.lions.user.manager.enums.role.TypeRole;
import dev.lions.user.manager.mapper.RoleMapper;
import dev.lions.user.manager.service.RoleService;
import org.keycloak.admin.client.resource.UserResource;
import org.keycloak.representations.idm.UserRepresentation;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.NotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.keycloak.admin.client.resource.*;
import org.keycloak.representations.idm.RoleRepresentation;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * Implémentation du service de gestion des rôles Keycloak
 */
@ApplicationScoped
@Slf4j
public class RoleServiceImpl implements RoleService {

    @Inject
    KeycloakAdminClient keycloakAdminClient;

    // ==================== CRUD Realm Roles ====================

    @Override
    public RoleDTO createRealmRole(@Valid @NotNull RoleDTO roleDTO, @NotBlank String realmName) {
        log.info("Création du rôle realm: {} dans le realm: {}", roleDTO.getName(), realmName);

        RolesResource rolesResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .roles();

        // Vérifier si le rôle existe déjà
        try {
            rolesResource.get(roleDTO.getName()).toRepresentation();
            throw new IllegalArgumentException("Le rôle " + roleDTO.getName() + " existe déjà");
        } catch (NotFoundException e) {
            // OK, le rôle n'existe pas
        }

        RoleRepresentation roleRep = RoleMapper.toRepresentation(roleDTO);
        rolesResource.create(roleRep);

        // Récupérer le rôle créé avec son ID
        RoleRepresentation createdRole = rolesResource.get(roleDTO.getName()).toRepresentation();
        return RoleMapper.toDTO(createdRole, realmName, TypeRole.REALM_ROLE);
    }

    // Méthodes privées helper pour utilisation interne
    private Optional<RoleDTO> getRealmRoleById(String roleId, String realmName) {
        log.debug("Récupération du rôle realm par ID: {} dans le realm: {}", roleId, realmName);

        try {
            RolesResource rolesResource = keycloakAdminClient.getInstance()
                .realm(realmName)
                .roles();

            // Keycloak ne permet pas de récupérer un rôle par ID directement, on doit lister tous les rôles
            List<RoleRepresentation> roles = rolesResource.list();
            return roles.stream()
                .filter(r -> r.getId().equals(roleId))
                .findFirst()
                .map(r -> RoleMapper.toDTO(r, realmName, TypeRole.REALM_ROLE));
        } catch (Exception e) {
            log.error("Erreur lors de la récupération du rôle realm {}", roleId, e);
            return Optional.empty();
        }
    }

    private Optional<RoleDTO> getRealmRoleByName(String roleName, String realmName) {
        log.debug("Récupération du rôle realm par nom: {} dans le realm: {}", roleName, realmName);

        try {
            RoleRepresentation roleRep = keycloakAdminClient.getInstance()
                .realm(realmName)
                .roles()
                .get(roleName)
                .toRepresentation();

            return Optional.of(RoleMapper.toDTO(roleRep, realmName, TypeRole.REALM_ROLE));
        } catch (NotFoundException e) {
            log.warn("Rôle realm {} non trouvé dans le realm {}", roleName, realmName);
            return Optional.empty();
        }
    }

    @Override
    public RoleDTO updateRole(@NotBlank String roleId,
                              @Valid @NotNull RoleDTO role,
                              @NotBlank String realmName,
                              @NotNull TypeRole typeRole,
                              String clientName) {
        log.info("Mise à jour du rôle {} (type: {}) dans le realm: {}", roleId, typeRole, realmName);

        if (typeRole == TypeRole.REALM_ROLE) {
            // Trouver le nom du rôle par son ID
            Optional<RoleDTO> existingRole = getRealmRoleById(roleId, realmName);
            if (existingRole.isEmpty()) {
                throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
            }
            String roleName = existingRole.get().getName();

            RoleResource roleResource = keycloakAdminClient.getInstance()
                .realm(realmName)
                .roles()
                .get(roleName);

            RoleRepresentation roleRep = roleResource.toRepresentation();

            // Mettre à jour uniquement les champs modifiables
            if (role.getDescription() != null) {
                roleRep.setDescription(role.getDescription());
            }

            roleResource.update(roleRep);

            // Retourner le rôle mis à jour
            return RoleMapper.toDTO(roleResource.toRepresentation(), realmName, TypeRole.REALM_ROLE);
        } else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
            // Pour les rôles client, trouver le nom par ID puis mettre à jour
            Optional<RoleDTO> existingRole = getRoleById(roleId, realmName, typeRole, clientName);
            if (existingRole.isEmpty()) {
                throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
            }
            String roleName = existingRole.get().getName();

            ClientsResource clientsResource = keycloakAdminClient.getInstance()
                .realm(realmName)
                .clients();

            List<org.keycloak.representations.idm.ClientRepresentation> clients =
                clientsResource.findByClientId(clientName);

            if (clients.isEmpty()) {
                throw new IllegalArgumentException("Client " + clientName + " non trouvé");
            }

            String internalClientId = clients.get(0).getId();
            RoleResource roleResource = clientsResource.get(internalClientId)
                .roles()
                .get(roleName);

            RoleRepresentation roleRep = roleResource.toRepresentation();

            if (role.getDescription() != null) {
                roleRep.setDescription(role.getDescription());
            }

            roleResource.update(roleRep);

            RoleDTO result = RoleMapper.toDTO(roleResource.toRepresentation(), realmName, TypeRole.CLIENT_ROLE);
            result.setClientId(clientName);
            return result;
        }

        throw new IllegalArgumentException("Type de rôle non supporté pour la mise à jour: " + typeRole);
    }

    @Override
    public void deleteRole(@NotBlank String roleId,
                          @NotBlank String realmName,
                          @NotNull TypeRole typeRole,
                          String clientName) {
        log.info("Suppression du rôle {} (type: {}) dans le realm: {}", roleId, typeRole, realmName);

        if (typeRole == TypeRole.REALM_ROLE) {
            // Trouver le nom du rôle par son ID
            Optional<RoleDTO> existingRole = getRealmRoleById(roleId, realmName);
            if (existingRole.isEmpty()) {
                throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
            }
            String roleName = existingRole.get().getName();

            keycloakAdminClient.getInstance()
                .realm(realmName)
                .roles()
                .deleteRole(roleName);
        } else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
            // Trouver le nom du rôle par son ID
            Optional<RoleDTO> existingRole = getRoleById(roleId, realmName, typeRole, clientName);
            if (existingRole.isEmpty()) {
                throw new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId);
            }
            String roleName = existingRole.get().getName();

            ClientsResource clientsResource = keycloakAdminClient.getInstance()
                .realm(realmName)
                .clients();

            List<org.keycloak.representations.idm.ClientRepresentation> clients =
                clientsResource.findByClientId(clientName);

            if (clients.isEmpty()) {
                throw new IllegalArgumentException("Client " + clientName + " non trouvé");
            }

            String internalClientId = clients.get(0).getId();
            clientsResource.get(internalClientId).roles().deleteRole(roleName);
        } else {
            throw new IllegalArgumentException("Type de rôle non supporté pour la suppression: " + typeRole);
        }
    }

    @Override
    public List<RoleDTO> getAllRealmRoles(@NotBlank String realmName) {
        log.info("Récupération de tous les rôles realm du realm: {}", realmName);

        try {
            // Vérifier que le realm existe
            if (!keycloakAdminClient.realmExists(realmName)) {
                log.error("Le realm {} n'existe pas", realmName);
                throw new IllegalArgumentException("Le realm '" + realmName + "' n'existe pas");
            }

            List<RoleRepresentation> roleReps = keycloakAdminClient.getInstance()
                .realm(realmName)
                .roles()
                .list();

            log.info("Récupération réussie: {} rôles trouvés dans le realm {}", roleReps.size(), realmName);
            return RoleMapper.toDTOList(roleReps, realmName, TypeRole.REALM_ROLE);
        } catch (Exception e) {
            log.error("Erreur lors de la récupération des rôles realm du realm {}: {}", realmName, e.getMessage(), e);
            throw new RuntimeException("Erreur lors de la récupération des rôles realm: " + e.getMessage(), e);
        }
    }

    // ==================== CRUD Client Roles ====================

    @Override
    public RoleDTO createClientRole(@Valid @NotNull RoleDTO roleDTO, @NotBlank String realmName,
                                     @NotBlank String clientName) {
        log.info("Création du rôle client: {} pour le client: {} dans le realm: {}",
                 roleDTO.getName(), clientName, realmName);

        ClientsResource clientsResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .clients();

        // Trouver le client par clientId (on utilise clientName comme clientId)
        List<org.keycloak.representations.idm.ClientRepresentation> clients =
            clientsResource.findByClientId(clientName);

        if (clients.isEmpty()) {
            throw new IllegalArgumentException("Client " + clientName + " non trouvé");
        }

        String internalClientId = clients.get(0).getId();
        RolesResource rolesResource = clientsResource.get(internalClientId).roles();

        // Vérifier si le rôle existe déjà
        try {
            rolesResource.get(roleDTO.getName()).toRepresentation();
            throw new IllegalArgumentException("Le rôle " + roleDTO.getName() + " existe déjà pour ce client");
        } catch (NotFoundException e) {
            // OK, le rôle n'existe pas
        }

        RoleRepresentation roleRep = RoleMapper.toRepresentation(roleDTO);
        rolesResource.create(roleRep);

        // Récupérer le rôle créé
        RoleRepresentation createdRole = rolesResource.get(roleDTO.getName()).toRepresentation();
        RoleDTO result = RoleMapper.toDTO(createdRole, realmName, TypeRole.CLIENT_ROLE);
        result.setClientId(clientName);

        return result;
    }

    // Méthode privée helper pour utilisation interne
    private Optional<RoleDTO> getClientRoleByName(String roleName, String clientId,
                                                   String realmName) {
        log.debug("Récupération du rôle client: {} pour le client: {} dans le realm: {}",
                  roleName, clientId, realmName);

        try {
            ClientsResource clientsResource = keycloakAdminClient.getInstance()
                .realm(realmName)
                .clients();

            List<org.keycloak.representations.idm.ClientRepresentation> clients =
                clientsResource.findByClientId(clientId);

            if (clients.isEmpty()) {
                return Optional.empty();
            }

            String internalClientId = clients.get(0).getId();
            RoleRepresentation roleRep = clientsResource.get(internalClientId)
                .roles()
                .get(roleName)
                .toRepresentation();

            RoleDTO roleDTO = RoleMapper.toDTO(roleRep, realmName, TypeRole.CLIENT_ROLE);
            roleDTO.setClientId(clientId);

            return Optional.of(roleDTO);
        } catch (NotFoundException e) {
            log.warn("Rôle client {} non trouvé pour le client {} dans le realm {}",
                     roleName, clientId, realmName);
            return Optional.empty();
        }
    }


    @Override
    public List<RoleDTO> getAllClientRoles(@NotBlank String realmName, @NotBlank String clientName) {
        log.debug("Récupération de tous les rôles du client: {} dans le realm: {}", clientName, realmName);

        ClientsResource clientsResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .clients();

        List<org.keycloak.representations.idm.ClientRepresentation> clients =
            clientsResource.findByClientId(clientName);

        if (clients.isEmpty()) {
            return List.of();
        }

        String internalClientId = clients.get(0).getId();
        List<RoleRepresentation> roleReps = clientsResource.get(internalClientId)
            .roles()
            .list();

        List<RoleDTO> roles = RoleMapper.toDTOList(roleReps, realmName, TypeRole.CLIENT_ROLE);
        roles.forEach(role -> role.setClientId(clientName));

        return roles;
    }

    @Override
    public Optional<RoleDTO> getRoleById(@NotBlank String roleId,
                                         @NotBlank String realmName,
                                         @NotNull TypeRole typeRole,
                                         String clientName) {
        log.debug("Récupération du rôle par ID: {} (type: {}) dans le realm: {}", roleId, typeRole, realmName);

        if (typeRole == TypeRole.REALM_ROLE) {
            return getRealmRoleById(roleId, realmName);
        } else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
            // Pour les rôles client, on doit lister tous les rôles du client et trouver par ID
            try {
                ClientsResource clientsResource = keycloakAdminClient.getInstance()
                    .realm(realmName)
                    .clients();

                List<org.keycloak.representations.idm.ClientRepresentation> clients =
                    clientsResource.findByClientId(clientName);

                if (clients.isEmpty()) {
                    return Optional.empty();
                }

                String internalClientId = clients.get(0).getId();
                List<RoleRepresentation> roles = clientsResource.get(internalClientId)
                    .roles()
                    .list();

                return roles.stream()
                    .filter(r -> r.getId().equals(roleId))
                    .findFirst()
                    .map(r -> {
                        RoleDTO roleDTO = RoleMapper.toDTO(r, realmName, TypeRole.CLIENT_ROLE);
                        roleDTO.setClientId(clientName);
                        return roleDTO;
                    });
            } catch (Exception e) {
                log.error("Erreur lors de la récupération du rôle client {}", roleId, e);
                return Optional.empty();
            }
        }
        return Optional.empty();
    }

    @Override
    public Optional<RoleDTO> getRoleByName(@NotBlank String roleName,
                                          @NotBlank String realmName,
                                          @NotNull TypeRole typeRole,
                                          String clientName) {
        log.debug("Récupération du rôle par nom: {} (type: {}) dans le realm: {}", roleName, typeRole, realmName);

        if (typeRole == TypeRole.REALM_ROLE) {
            return getRealmRoleByName(roleName, realmName);
        } else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
            return getClientRoleByName(roleName, clientName, realmName);
        }
        return Optional.empty();
    }

    // ==================== Attribution de rôles ====================

    @Override
    public void assignRolesToUser(@Valid @NotNull RoleAssignmentDTO assignment) {
        log.info("Attribution de {} rôles {} à l'utilisateur {} dans le realm {}",
                 assignment.getRoleNames().size(), assignment.getTypeRole(), assignment.getUserId(), assignment.getRealmName());

        if (assignment.getTypeRole() == TypeRole.REALM_ROLE) {
            assignRealmRolesToUser(assignment.getUserId(), assignment.getRoleNames(), assignment.getRealmName());
        } else if (assignment.getTypeRole() == TypeRole.CLIENT_ROLE && assignment.getClientName() != null) {
            assignClientRolesToUser(assignment.getUserId(), assignment.getClientName(), assignment.getRoleNames(), assignment.getRealmName());
        } else {
            throw new IllegalArgumentException("Données d'attribution invalides pour le type de rôle: " + assignment.getTypeRole());
        }
    }

    @Override
    public void revokeRolesFromUser(@Valid @NotNull RoleAssignmentDTO assignment) {
        log.info("Révocation de {} rôles {} pour l'utilisateur {} dans le realm {}",
                 assignment.getRoleNames().size(), assignment.getTypeRole(), assignment.getUserId(), assignment.getRealmName());

        if (assignment.getTypeRole() == TypeRole.REALM_ROLE) {
            revokeRealmRolesFromUser(assignment.getUserId(), assignment.getRoleNames(), assignment.getRealmName());
        } else if (assignment.getTypeRole() == TypeRole.CLIENT_ROLE && assignment.getClientName() != null) {
            revokeClientRolesFromUser(assignment.getUserId(), assignment.getClientName(), assignment.getRoleNames(), assignment.getRealmName());
        } else {
            throw new IllegalArgumentException("Données de révocation invalides pour le type de rôle: " + assignment.getTypeRole());
        }
    }

    private void assignRealmRolesToUser(String userId, List<String> roleNames,
                                        String realmName) {
        log.info("Attribution de {} rôles realm à l'utilisateur {} dans le realm {}",
                 roleNames.size(), userId, realmName);

        UserResource userResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .users()
            .get(userId);

        RolesResource rolesResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .roles();

        List<RoleRepresentation> rolesToAssign = roleNames.stream()
            .map(roleName -> {
                try {
                    return rolesResource.get(roleName).toRepresentation();
                } catch (NotFoundException e) {
                    log.warn("Rôle {} non trouvé, ignoré", roleName);
                    return null;
                }
            })
            .filter(role -> role != null)
            .collect(Collectors.toList());

        if (!rolesToAssign.isEmpty()) {
            userResource.roles().realmLevel().add(rolesToAssign);
        }
    }

    private void revokeRealmRolesFromUser(String userId, List<String> roleNames,
                                          String realmName) {
        log.info("Révocation de {} rôles realm pour l'utilisateur {} dans le realm {}",
                 roleNames.size(), userId, realmName);

        UserResource userResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .users()
            .get(userId);

        RolesResource rolesResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .roles();

        List<RoleRepresentation> rolesToRevoke = roleNames.stream()
            .map(roleName -> {
                try {
                    return rolesResource.get(roleName).toRepresentation();
                } catch (NotFoundException e) {
                    log.warn("Rôle {} non trouvé, ignoré", roleName);
                    return null;
                }
            })
            .filter(role -> role != null)
            .collect(Collectors.toList());

        if (!rolesToRevoke.isEmpty()) {
            userResource.roles().realmLevel().remove(rolesToRevoke);
        }
    }

    private void assignClientRolesToUser(String userId, String clientId,
                                         List<String> roleNames, String realmName) {
        log.info("Attribution de {} rôles du client {} à l'utilisateur {} dans le realm {}",
                 roleNames.size(), clientId, userId, realmName);

        UserResource userResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .users()
            .get(userId);

        ClientsResource clientsResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .clients();

        List<org.keycloak.representations.idm.ClientRepresentation> clients =
            clientsResource.findByClientId(clientId);

        if (clients.isEmpty()) {
            throw new IllegalArgumentException("Client " + clientId + " non trouvé");
        }

        String internalClientId = clients.get(0).getId();
        RolesResource rolesResource = clientsResource.get(internalClientId).roles();

        List<RoleRepresentation> rolesToAssign = roleNames.stream()
            .map(roleName -> {
                try {
                    return rolesResource.get(roleName).toRepresentation();
                } catch (NotFoundException e) {
                    log.warn("Rôle client {} non trouvé, ignoré", roleName);
                    return null;
                }
            })
            .filter(role -> role != null)
            .collect(Collectors.toList());

        if (!rolesToAssign.isEmpty()) {
            userResource.roles().clientLevel(internalClientId).add(rolesToAssign);
        }
    }

    private void revokeClientRolesFromUser(String userId, String clientId,
                                           List<String> roleNames, String realmName) {
        log.info("Révocation de {} rôles du client {} pour l'utilisateur {} dans le realm {}",
                 roleNames.size(), clientId, userId, realmName);

        UserResource userResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .users()
            .get(userId);

        ClientsResource clientsResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .clients();

        List<org.keycloak.representations.idm.ClientRepresentation> clients =
            clientsResource.findByClientId(clientId);

        if (clients.isEmpty()) {
            throw new IllegalArgumentException("Client " + clientId + " non trouvé");
        }

        String internalClientId = clients.get(0).getId();
        RolesResource rolesResource = clientsResource.get(internalClientId).roles();

        List<RoleRepresentation> rolesToRevoke = roleNames.stream()
            .map(roleName -> {
                try {
                    return rolesResource.get(roleName).toRepresentation();
                } catch (NotFoundException e) {
                    log.warn("Rôle client {} non trouvé, ignoré", roleName);
                    return null;
                }
            })
            .filter(role -> role != null)
            .collect(Collectors.toList());

        if (!rolesToRevoke.isEmpty()) {
            userResource.roles().clientLevel(internalClientId).remove(rolesToRevoke);
        }
    }

    @Override
    public List<RoleDTO> getUserRealmRoles(@NotBlank String userId, @NotBlank String realmName) {
        log.debug("Récupération des rôles realm de l'utilisateur {} dans le realm {}", userId, realmName);

        List<RoleRepresentation> roleReps = keycloakAdminClient.getInstance()
            .realm(realmName)
            .users()
            .get(userId)
            .roles()
            .realmLevel()
            .listAll();

        return RoleMapper.toDTOList(roleReps, realmName, TypeRole.REALM_ROLE);
    }

    @Override
    public List<RoleDTO> getUserClientRoles(@NotBlank String userId,
                                            @NotBlank String realmName,
                                            @NotBlank String clientName) {
        log.debug("Récupération des rôles du client {} pour l'utilisateur {} dans le realm {}",
                  clientName, userId, realmName);

        ClientsResource clientsResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .clients();

        List<org.keycloak.representations.idm.ClientRepresentation> clients =
            clientsResource.findByClientId(clientName);

        if (clients.isEmpty()) {
            return List.of();
        }

        String internalClientId = clients.get(0).getId();
        List<RoleRepresentation> roleReps = keycloakAdminClient.getInstance()
            .realm(realmName)
            .users()
            .get(userId)
            .roles()
            .clientLevel(internalClientId)
            .listAll();

        List<RoleDTO> roles = RoleMapper.toDTOList(roleReps, realmName, TypeRole.CLIENT_ROLE);
        roles.forEach(role -> role.setClientId(clientName));

        return roles;
    }

    @Override
    public List<RoleDTO> getAllUserRoles(@NotBlank String userId, @NotBlank String realmName) {
        log.debug("Récupération de tous les rôles de l'utilisateur {} dans le realm {}", userId, realmName);

        List<RoleDTO> allRoles = new ArrayList<>();
        
        // Ajouter les rôles realm
        allRoles.addAll(getUserRealmRoles(userId, realmName));

        // Ajouter les rôles client pour tous les clients
        try {
            var clientsResource = keycloakAdminClient.getInstance()
                .realm(realmName)
                .clients();
            
            List<org.keycloak.representations.idm.ClientRepresentation> clients = clientsResource.findAll();
            
            for (org.keycloak.representations.idm.ClientRepresentation client : clients) {
                String clientId = client.getClientId();
                allRoles.addAll(getUserClientRoles(userId, realmName, clientId));
            }
        } catch (Exception e) {
            log.warn("Erreur lors de la récupération des rôles client pour l'utilisateur {}", userId, e);
        }

        return allRoles;
    }

    // ==================== Rôles composites ====================

    @Override
    public void addCompositeRoles(@NotBlank String parentRoleId,
                                  @NotNull List<String> childRoleIds,
                                  @NotBlank String realmName,
                                  @NotNull TypeRole typeRole,
                                  String clientName) {
        log.info("Ajout de {} rôles composites au rôle {} (type: {}) dans le realm {}",
                 childRoleIds.size(), parentRoleId, typeRole, realmName);

        // Trouver le nom du rôle parent par son ID
        Optional<RoleDTO> parentRole = getRoleById(parentRoleId, realmName, typeRole, clientName);
        if (parentRole.isEmpty()) {
            throw new jakarta.ws.rs.NotFoundException("Rôle parent non trouvé: " + parentRoleId);
        }
        String parentRoleName = parentRole.get().getName();

        RolesResource rolesResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .roles();

        if (typeRole == TypeRole.REALM_ROLE) {
            RoleResource roleResource = rolesResource.get(parentRoleName);

            // Convertir les IDs en noms de rôles
            List<String> childRoleNames = childRoleIds.stream()
                .map(childRoleId -> {
                    Optional<RoleDTO> childRole = getRealmRoleById(childRoleId, realmName);
                    return childRole.map(RoleDTO::getName).orElse(null);
                })
                .filter(name -> name != null)
                .collect(Collectors.toList());

            List<RoleRepresentation> compositesToAdd = childRoleNames.stream()
                .map(compositeName -> {
                    try {
                        return rolesResource.get(compositeName).toRepresentation();
                    } catch (NotFoundException e) {
                        log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
                        return null;
                    }
                })
                .filter(role -> role != null)
                .collect(Collectors.toList());

            if (!compositesToAdd.isEmpty()) {
                roleResource.addComposites(compositesToAdd);
            }
        } else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
            // Pour les rôles client, utiliser le client
            ClientsResource clientsResource = keycloakAdminClient.getInstance()
                .realm(realmName)
                .clients();

            List<org.keycloak.representations.idm.ClientRepresentation> clients =
                clientsResource.findByClientId(clientName);

            if (clients.isEmpty()) {
                throw new IllegalArgumentException("Client " + clientName + " non trouvé");
            }

            String internalClientId = clients.get(0).getId();
            RolesResource clientRolesResource = clientsResource.get(internalClientId).roles();
            RoleResource roleResource = clientRolesResource.get(parentRoleName);

            // Convertir les IDs en noms de rôles
            List<String> childRoleNames = childRoleIds.stream()
                .map(childRoleId -> {
                    Optional<RoleDTO> childRole = getRoleById(childRoleId, realmName, typeRole, clientName);
                    return childRole.map(RoleDTO::getName).orElse(null);
                })
                .filter(name -> name != null)
                .collect(Collectors.toList());

            List<RoleRepresentation> compositesToAdd = childRoleNames.stream()
                .map(compositeName -> {
                    try {
                        return clientRolesResource.get(compositeName).toRepresentation();
                    } catch (NotFoundException e) {
                        log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
                        return null;
                    }
                })
                .filter(role -> role != null)
                .collect(Collectors.toList());

            if (!compositesToAdd.isEmpty()) {
                roleResource.addComposites(compositesToAdd);
            }
        }
    }

    @Override
    public void removeCompositeRoles(@NotBlank String parentRoleId,
                                    @NotNull List<String> childRoleIds,
                                    @NotBlank String realmName,
                                    @NotNull TypeRole typeRole,
                                    String clientName) {
        log.info("Suppression de {} rôles composites du rôle {} (type: {}) dans le realm {}",
                 childRoleIds.size(), parentRoleId, typeRole, realmName);

        // Trouver le nom du rôle parent par son ID
        Optional<RoleDTO> parentRole = getRoleById(parentRoleId, realmName, typeRole, clientName);
        if (parentRole.isEmpty()) {
            throw new jakarta.ws.rs.NotFoundException("Rôle parent non trouvé: " + parentRoleId);
        }
        String parentRoleName = parentRole.get().getName();

        RolesResource rolesResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .roles();

        if (typeRole == TypeRole.REALM_ROLE) {
            RoleResource roleResource = rolesResource.get(parentRoleName);

            // Convertir les IDs en noms de rôles
            List<String> childRoleNames = childRoleIds.stream()
                .map(childRoleId -> {
                    Optional<RoleDTO> childRole = getRealmRoleById(childRoleId, realmName);
                    return childRole.map(RoleDTO::getName).orElse(null);
                })
                .filter(name -> name != null)
                .collect(Collectors.toList());

            List<RoleRepresentation> compositesToRemove = childRoleNames.stream()
                .map(compositeName -> {
                    try {
                        return rolesResource.get(compositeName).toRepresentation();
                    } catch (NotFoundException e) {
                        log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
                        return null;
                    }
                })
                .filter(role -> role != null)
                .collect(Collectors.toList());

            if (!compositesToRemove.isEmpty()) {
                roleResource.deleteComposites(compositesToRemove);
            }
        } else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
            ClientsResource clientsResource = keycloakAdminClient.getInstance()
                .realm(realmName)
                .clients();

            List<org.keycloak.representations.idm.ClientRepresentation> clients =
                clientsResource.findByClientId(clientName);

            if (clients.isEmpty()) {
                throw new IllegalArgumentException("Client " + clientName + " non trouvé");
            }

            String internalClientId = clients.get(0).getId();
            RolesResource clientRolesResource = clientsResource.get(internalClientId).roles();
            RoleResource roleResource = clientRolesResource.get(parentRoleName);

            // Convertir les IDs en noms de rôles
            List<String> childRoleNames = childRoleIds.stream()
                .map(childRoleId -> {
                    Optional<RoleDTO> childRole = getRoleById(childRoleId, realmName, typeRole, clientName);
                    return childRole.map(RoleDTO::getName).orElse(null);
                })
                .filter(name -> name != null)
                .collect(Collectors.toList());

            List<RoleRepresentation> compositesToRemove = childRoleNames.stream()
                .map(compositeName -> {
                    try {
                        return clientRolesResource.get(compositeName).toRepresentation();
                    } catch (NotFoundException e) {
                        log.warn("Rôle composite {} non trouvé, ignoré", compositeName);
                        return null;
                    }
                })
                .filter(role -> role != null)
                .collect(Collectors.toList());

            if (!compositesToRemove.isEmpty()) {
                roleResource.deleteComposites(compositesToRemove);
            }
        }
    }


    @Override
    public List<RoleDTO> getCompositeRoles(@NotBlank String roleId,
                                            @NotBlank String realmName,
                                            @NotNull TypeRole typeRole,
                                            String clientName) {
        log.debug("Récupération des rôles composites du rôle {} dans le realm {}", roleId, realmName);

        // Pour récupérer par ID, on doit d'abord trouver le nom du rôle
        // Comme Keycloak ne permet pas de récupérer directement par ID, on doit lister et trouver
        RolesResource rolesResource = keycloakAdminClient.getInstance()
            .realm(realmName)
            .roles();
        
        RoleRepresentation roleRep = rolesResource.list().stream()
            .filter(r -> r.getId().equals(roleId))
            .findFirst()
            .orElseThrow(() -> new jakarta.ws.rs.NotFoundException("Rôle non trouvé: " + roleId));

        java.util.Set<RoleRepresentation> compositesSet = rolesResource
            .get(roleRep.getName())
            .getRoleComposites();
        
        List<RoleRepresentation> composites = new ArrayList<>(compositesSet);

        return RoleMapper.toDTOList(composites, realmName, TypeRole.COMPOSITE_ROLE);
    }

    // ==================== Vérification de permissions ====================

    @Override
    public boolean userHasRole(@NotBlank String userId,
                               @NotBlank String roleName,
                               @NotBlank String realmName,
                               @NotNull TypeRole typeRole,
                               String clientName) {
        log.debug("Vérification si l'utilisateur {} a le rôle {} (type: {}) dans le realm {}",
                  userId, roleName, typeRole, realmName);

        if (typeRole == TypeRole.REALM_ROLE) {
            List<RoleRepresentation> userRoles = keycloakAdminClient.getInstance()
                .realm(realmName)
                .users()
                .get(userId)
                .roles()
                .realmLevel()
                .listEffective(); // Incluant les rôles hérités via composites

            return userRoles.stream()
                .anyMatch(role -> role.getName().equals(roleName));
        } else if (typeRole == TypeRole.CLIENT_ROLE && clientName != null) {
            ClientsResource clientsResource = keycloakAdminClient.getInstance()
                .realm(realmName)
                .clients();

            List<org.keycloak.representations.idm.ClientRepresentation> clients =
                clientsResource.findByClientId(clientName);

            if (clients.isEmpty()) {
                return false;
            }

            String internalClientId = clients.get(0).getId();
            List<RoleRepresentation> userClientRoles = keycloakAdminClient.getInstance()
                .realm(realmName)
                .users()
                .get(userId)
                .roles()
                .clientLevel(internalClientId)
                .listEffective();

            return userClientRoles.stream()
                .anyMatch(role -> role.getName().equals(roleName));
        }

        return false;
    }

    @Override
    public boolean roleExists(@NotBlank String roleName,
                              @NotBlank String realmName,
                              @NotNull TypeRole typeRole,
                              String clientName) {
        log.debug("Vérification de l'existence du rôle {} (type: {}) dans le realm {}",
                  roleName, typeRole, realmName);

        return getRoleByName(roleName, realmName, typeRole, clientName).isPresent();
    }

    @Override
    public long countUsersWithRole(@NotBlank String roleId,
                                   @NotBlank String realmName,
                                   @NotNull TypeRole typeRole,
                                   String clientName) {
        log.debug("Comptage des utilisateurs ayant le rôle {} (type: {}) dans le realm {}",
                  roleId, typeRole, realmName);

        // Trouver le nom du rôle par son ID
        Optional<RoleDTO> role = getRoleById(roleId, realmName, typeRole, clientName);
        if (role.isEmpty()) {
            return 0;
        }

        String roleName = role.get().getName();

        try {
            // Keycloak ne fournit pas directement cette fonctionnalité via l'API Admin
            // On doit lister tous les utilisateurs et vérifier leurs rôles
            // C'est coûteux mais nécessaire
            List<UserRepresentation> users = keycloakAdminClient.getInstance()
                .realm(realmName)
                .users()
                .list();

            long count = 0;
            for (UserRepresentation user : users) {
                if (userHasRole(user.getId(), roleName, realmName, typeRole, clientName)) {
                    count++;
                }
            }

            return count;
        } catch (Exception e) {
            log.error("Erreur lors du comptage des utilisateurs avec le rôle {}", roleId, e);
            return 0;
        }
    }

    // Méthodes privées pour compatibilité interne (utilisées par les nouvelles méthodes publiques)
    private boolean userHasRealmRole(String userId, String roleName,
                                    String realmName) {
        return userHasRole(userId, roleName, realmName, TypeRole.REALM_ROLE, null);
    }

    private boolean userHasClientRole(String userId, String clientId,
                                     String roleName, String realmName) {
        return userHasRole(userId, roleName, realmName, TypeRole.CLIENT_ROLE, clientId);
    }
}
