RoleController.java

package com.archiweb.api;

import com.archiweb.dto.RoleDTO;
import com.archiweb.model.Role;
import com.archiweb.repository.RoleRepository;
import com.archiweb.repository.UserRepository;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/api/admin/roles")
@Tag(
        name = "Gestion des Rôles",
        description = """
        Module de gestion des rôles utilisateurs.  
        Permet aux administrateurs de :
        - Lister tous les rôles disponibles
        - Consulter les détails d'un rôle
        - Créer de nouveaux rôles
        - Modifier les rôles existants
        - Désactiver/Activer des rôles
        - Voir le nombre d'utilisateurs par rôle
        Tous les endpoints nécessitent le rôle **ADMIN**.
        """
)
public class RoleController {

    private final RoleRepository roleRepository;
    private final UserRepository userRepository;

    public RoleController(RoleRepository roleRepository, UserRepository userRepository) {
        this.roleRepository = roleRepository;
        this.userRepository = userRepository;
    }

    /**
     * Convertit un Role en RoleDTO avec le nombre d'utilisateurs
     */
    private RoleDTO toDTO(Role role) {
        RoleDTO dto = new RoleDTO();
        dto.setId(role.getId());
        dto.setName(role.getName());
        dto.setDescription(role.getDescription());
        dto.setActive(role.getActive());
        
        // Compter les utilisateurs avec ce rôle
        long userCount = userRepository.countByRole(role);
        dto.setUserCount(userCount);
        
        return dto;
    }

    // ================================================================
    // 1️⃣ Lister tous les rôles
    // ================================================================
    @Operation(
            summary = "Lister tous les rôles",
            description = "Récupère la liste complète des rôles avec leurs descriptions et le nombre d'utilisateurs."
    )
    @GetMapping
    public ResponseEntity<List<RoleDTO>> getAllRoles() {
        List<Role> roles = roleRepository.findAll();
        List<RoleDTO> roleDTOs = roles.stream()
                .map(this::toDTO)
                .collect(Collectors.toList());
        return ResponseEntity.ok(roleDTOs);
    }

    // ================================================================
    // 2️⃣ Obtenir un rôle par ID
    // ================================================================
    @Operation(
            summary = "Obtenir un rôle par ID",
            description = "Récupère les détails d'un rôle spécifique."
    )
    @GetMapping("/{id}")
    public ResponseEntity<RoleDTO> getRoleById(@PathVariable Long id) {
        return roleRepository.findById(id)
                .map(role -> ResponseEntity.ok(toDTO(role)))
                .orElse(ResponseEntity.notFound().build());
    }

    // ================================================================
    // 3️⃣ Obtenir un rôle par nom
    // ================================================================
    @Operation(
            summary = "Obtenir un rôle par nom",
            description = "Récupère les détails d'un rôle par son nom (ex: ROLE_USER, ROLE_ADMIN)."
    )
    @GetMapping("/name/{name}")
    public ResponseEntity<RoleDTO> getRoleByName(@PathVariable String name) {
        return roleRepository.findByName(name)
                .map(role -> ResponseEntity.ok(toDTO(role)))
                .orElse(ResponseEntity.notFound().build());
    }

    // ================================================================
    // 4️⃣ Créer un nouveau rôle
    // ================================================================
    @Operation(
            summary = "Créer un nouveau rôle",
            description = "Crée un nouveau rôle avec un nom unique et une description."
    )
    @PostMapping
    public ResponseEntity<RoleDTO> createRole(@RequestBody RoleDTO roleDTO) {
        // Vérifier si le rôle existe déjà
        if (roleRepository.findByName(roleDTO.getName()).isPresent()) {
            return ResponseEntity.status(HttpStatus.CONFLICT).build();
        }

        Role role = new Role();
        role.setName(roleDTO.getName());
        role.setDescription(roleDTO.getDescription());
        role.setActive(roleDTO.getActive() != null ? roleDTO.getActive() : true);

        Role savedRole = roleRepository.save(role);
        return ResponseEntity.status(HttpStatus.CREATED).body(toDTO(savedRole));
    }

    // ================================================================
    // 5️⃣ Mettre à jour un rôle
    // ================================================================
    @Operation(
            summary = "Mettre à jour un rôle",
            description = "Met à jour les informations d'un rôle existant (description, statut actif)."
    )
    @PutMapping("/{id}")
    public ResponseEntity<RoleDTO> updateRole(@PathVariable Long id, @RequestBody RoleDTO roleDTO) {
        Optional<Role> roleOpt = roleRepository.findById(id);
        if (roleOpt.isEmpty()) {
            return ResponseEntity.notFound().build();
        }
        
        Role role = roleOpt.get();
        
        // Ne pas permettre la modification du nom des rôles système
        if (!isSystemRole(role.getName())) {
            if (roleDTO.getName() != null && !roleDTO.getName().equals(role.getName())) {
                // Vérifier que le nouveau nom n'existe pas déjà
                if (roleRepository.findByName(roleDTO.getName()).isPresent()) {
                    return ResponseEntity.status(HttpStatus.CONFLICT).build();
                }
                role.setName(roleDTO.getName());
            }
        }
        
        if (roleDTO.getDescription() != null) {
            role.setDescription(roleDTO.getDescription());
        }
        
        if (roleDTO.getActive() != null) {
            // Ne pas permettre de désactiver les rôles système
            if (!isSystemRole(role.getName()) || roleDTO.getActive()) {
                role.setActive(roleDTO.getActive());
            }
        }

        Role updatedRole = roleRepository.save(role);
        return ResponseEntity.ok(toDTO(updatedRole));
    }

    // ================================================================
    // 6️⃣ Désactiver/Activer un rôle
    // ================================================================
    @Operation(
            summary = "Désactiver/Activer un rôle",
            description = "Active ou désactive un rôle. Les rôles système ne peuvent pas être désactivés."
    )
    @PatchMapping("/{id}/toggle")
    public ResponseEntity<RoleDTO> toggleRole(@PathVariable Long id) {
        Optional<Role> roleOpt = roleRepository.findById(id);
        if (roleOpt.isEmpty()) {
            return ResponseEntity.notFound().build();
        }
        
        Role role = roleOpt.get();
        
        // Ne pas permettre de désactiver les rôles système
        if (isSystemRole(role.getName())) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
        
        role.setActive(!role.getActive());
        Role updatedRole = roleRepository.save(role);
        return ResponseEntity.ok(toDTO(updatedRole));
    }

    // ================================================================
    // 7️⃣ Supprimer un rôle
    // ================================================================
    @Operation(
            summary = "Supprimer un rôle",
            description = "Supprime un rôle. Les rôles système ne peuvent pas être supprimés."
    )
    @DeleteMapping("/{id}")
    @ApiResponse(
            responseCode = "200",
            description = "Rôle supprimé avec succès"
    )
    @ApiResponse(
            responseCode = "400",
            description = "Impossible de supprimer un rôle système ou un rôle utilisé",
            content = @Content(examples = @ExampleObject(value = "{ \"error\": \"Cannot delete system role or role in use\" }"))
    )
    @ApiResponse(
            responseCode = "404",
            description = "Rôle non trouvé"
    )
    public ResponseEntity<Void> deleteRole(@PathVariable Long id) {
        Optional<Role> roleOpt = roleRepository.findById(id);
        if (roleOpt.isEmpty()) {
            return ResponseEntity.notFound().build();
        }
        
        Role role = roleOpt.get();
        
        // Ne pas permettre la suppression des rôles système
        if (isSystemRole(role.getName())) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
        
        // Vérifier si le rôle est utilisé
        long userCount = userRepository.countByRole(role);
        if (userCount > 0) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).build();
        }
        
        roleRepository.delete(role);
        return ResponseEntity.ok().build();
    }

    /**
     * Vérifie si un rôle est un rôle système (ne peut pas être modifié/supprimé)
     */
    private boolean isSystemRole(String roleName) {
        return "ROLE_USER".equals(roleName) || 
               "ROLE_ADMIN".equals(roleName) || 
               "ROLE_EMPLOYEE".equals(roleName);
    }
}