package dev.lions.btpxpress.adapter.http; import dev.lions.btpxpress.application.service.DisponibiliteService; import dev.lions.btpxpress.domain.core.entity.Disponibilite; import dev.lions.btpxpress.domain.core.entity.TypeDisponibilite; import jakarta.inject.Inject; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.List; import java.util.UUID; import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.media.Content; import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; import org.eclipse.microprofile.openapi.annotations.tags.Tag; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Resource REST pour la gestion des disponibilités - Architecture 2025 RH: API complète de gestion * des disponibilités employés */ @Path("/api/v1/disponibilites") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Tag(name = "Disponibilités", description = "Gestion des disponibilités et absences des employés") public class DisponibiliteResource { private static final Logger logger = LoggerFactory.getLogger(DisponibiliteResource.class); @Inject DisponibiliteService disponibiliteService; // === ENDPOINTS DE CONSULTATION === @GET @Operation( summary = "Lister toutes les disponibilités", description = "Récupère la liste paginée de toutes les disponibilités avec filtres optionnels") @APIResponse( responseCode = "200", description = "Liste des disponibilités récupérée avec succès", content = @Content(schema = @Schema(implementation = Disponibilite.class))) public Response getAllDisponibilites( @Parameter(description = "Numéro de page (0-indexé)", example = "0") @QueryParam("page") @DefaultValue("0") int page, @Parameter(description = "Taille de page", example = "20") @QueryParam("size") @DefaultValue("20") int size, @Parameter(description = "Filtrer par employé (UUID)") @QueryParam("employeId") UUID employeId, @Parameter(description = "Filtrer par type de disponibilité") @QueryParam("type") String type, @Parameter(description = "Filtrer par statut d'approbation") @QueryParam("approuvee") Boolean approuvee) { logger.debug("Récupération des disponibilités - page: {}, taille: {}", page, size); List disponibilites; if (employeId != null) { disponibilites = disponibiliteService.findByEmployeId(employeId); } else if (type != null) { TypeDisponibilite typeEnum = TypeDisponibilite.valueOf(type.toUpperCase()); disponibilites = disponibiliteService.findByType(typeEnum); } else if (approuvee != null && !approuvee) { disponibilites = disponibiliteService.findEnAttente(); } else if (approuvee != null && approuvee) { disponibilites = disponibiliteService.findApprouvees(); } else { disponibilites = disponibiliteService.findAll(page, size); } return Response.ok(disponibilites).build(); } @GET @Path("/{id}") @Operation( summary = "Récupérer une disponibilité par ID", description = "Récupère les détails d'une disponibilité spécifique") @APIResponse( responseCode = "200", description = "Disponibilité trouvée", content = @Content(schema = @Schema(implementation = Disponibilite.class))) @APIResponse(responseCode = "404", description = "Disponibilité non trouvée") public Response getDisponibiliteById( @Parameter(description = "Identifiant unique de la disponibilité", required = true) @PathParam("id") UUID id) { logger.debug("Récupération de la disponibilité avec l'ID: {}", id); return disponibiliteService .findById(id) .map(disponibilite -> Response.ok(disponibilite).build()) .orElse(Response.status(Response.Status.NOT_FOUND).build()); } @GET @Path("/actuelles") @Operation( summary = "Lister les disponibilités actuelles", description = "Récupère toutes les disponibilités actuellement actives") @APIResponse( responseCode = "200", description = "Disponibilités actuelles récupérées", content = @Content(schema = @Schema(implementation = Disponibilite.class))) public Response getDisponibilitesActuelles() { logger.debug("Récupération des disponibilités actuelles"); List disponibilites = disponibiliteService.findActuelles(); return Response.ok(disponibilites).build(); } @GET @Path("/futures") @Operation( summary = "Lister les disponibilités futures", description = "Récupère toutes les disponibilités programmées pour le futur") @APIResponse( responseCode = "200", description = "Disponibilités futures récupérées", content = @Content(schema = @Schema(implementation = Disponibilite.class))) public Response getDisponibilitesFutures() { logger.debug("Récupération des disponibilités futures"); List disponibilites = disponibiliteService.findFutures(); return Response.ok(disponibilites).build(); } @GET @Path("/en-attente") @Operation( summary = "Lister les demandes en attente", description = "Récupère toutes les demandes de disponibilité en attente d'approbation") @APIResponse( responseCode = "200", description = "Demandes en attente récupérées", content = @Content(schema = @Schema(implementation = Disponibilite.class))) public Response getDemandesEnAttente() { logger.debug("Récupération des demandes en attente"); List disponibilites = disponibiliteService.findEnAttente(); return Response.ok(disponibilites).build(); } @GET @Path("/periode") @Operation( summary = "Lister les disponibilités pour une période", description = "Récupère toutes les disponibilités dans une période donnée") @APIResponse( responseCode = "200", description = "Disponibilités de la période récupérées", content = @Content(schema = @Schema(implementation = Disponibilite.class))) public Response getDisponibilitesPourPeriode( @Parameter(description = "Date de début (yyyy-mm-dd)", required = true) @QueryParam("dateDebut") @NotNull LocalDate dateDebut, @Parameter(description = "Date de fin (yyyy-mm-dd)", required = true) @QueryParam("dateFin") @NotNull LocalDate dateFin) { logger.debug("Récupération des disponibilités pour la période {} - {}", dateDebut, dateFin); List disponibilites = disponibiliteService.findPourPeriode(dateDebut, dateFin); return Response.ok(disponibilites).build(); } // === ENDPOINTS DE GESTION CRUD === @POST @Operation( summary = "Créer une nouvelle disponibilité", description = "Créé une nouvelle demande de disponibilité pour un employé") @APIResponse( responseCode = "201", description = "Disponibilité créée avec succès", content = @Content(schema = @Schema(implementation = Disponibilite.class))) @APIResponse(responseCode = "400", description = "Données invalides ou conflit détecté") public Response createDisponibilite(@Valid @NotNull CreateDisponibiliteRequest request) { logger.info("Création d'une nouvelle disponibilité pour l'employé: {}", request.employeId); Disponibilite disponibilite = disponibiliteService.createDisponibilite( request.employeId, request.dateDebut, request.dateFin, request.type, request.motif); return Response.status(Response.Status.CREATED).entity(disponibilite).build(); } @PUT @Path("/{id}") @Operation( summary = "Mettre à jour une disponibilité", description = "Met à jour les informations d'une disponibilité existante") @APIResponse( responseCode = "200", description = "Disponibilité mise à jour avec succès", content = @Content(schema = @Schema(implementation = Disponibilite.class))) @APIResponse(responseCode = "404", description = "Disponibilité non trouvée") @APIResponse(responseCode = "400", description = "Données invalides") public Response updateDisponibilite( @Parameter(description = "Identifiant de la disponibilité", required = true) @PathParam("id") UUID id, @Valid @NotNull UpdateDisponibiliteRequest request) { logger.info("Mise à jour de la disponibilité: {}", id); Disponibilite disponibilite = disponibiliteService.updateDisponibilite( id, request.dateDebut, request.dateFin, request.motif); return Response.ok(disponibilite).build(); } @POST @Path("/{id}/approuver") @Operation( summary = "Approuver une demande de disponibilité", description = "Approuve une demande de disponibilité en attente") @APIResponse( responseCode = "200", description = "Disponibilité approuvée avec succès", content = @Content(schema = @Schema(implementation = Disponibilite.class))) @APIResponse(responseCode = "404", description = "Disponibilité non trouvée") @APIResponse(responseCode = "400", description = "Disponibilité déjà approuvée") public Response approuverDisponibilite( @Parameter(description = "Identifiant de la disponibilité", required = true) @PathParam("id") UUID id) { logger.info("Approbation de la disponibilité: {}", id); Disponibilite disponibilite = disponibiliteService.approuverDisponibilite(id); return Response.ok(disponibilite).build(); } @POST @Path("/{id}/rejeter") @Operation( summary = "Rejeter une demande de disponibilité", description = "Rejette une demande de disponibilité avec une raison") @APIResponse( responseCode = "200", description = "Disponibilité rejetée avec succès", content = @Content(schema = @Schema(implementation = Disponibilite.class))) @APIResponse(responseCode = "404", description = "Disponibilité non trouvée") @APIResponse(responseCode = "400", description = "Impossible de rejeter") public Response rejeterDisponibilite( @Parameter(description = "Identifiant de la disponibilité", required = true) @PathParam("id") UUID id, @Valid @NotNull RejetDisponibiliteRequest request) { logger.info("Rejet de la disponibilité: {}", id); Disponibilite disponibilite = disponibiliteService.rejeterDisponibilite(id, request.raisonRejet); return Response.ok(disponibilite).build(); } @DELETE @Path("/{id}") @Operation( summary = "Supprimer une disponibilité", description = "Supprime définitivement une disponibilité") @APIResponse(responseCode = "204", description = "Disponibilité supprimée avec succès") @APIResponse(responseCode = "404", description = "Disponibilité non trouvée") @APIResponse(responseCode = "400", description = "Impossible de supprimer") public Response deleteDisponibilite( @Parameter(description = "Identifiant de la disponibilité", required = true) @PathParam("id") UUID id) { logger.info("Suppression de la disponibilité: {}", id); disponibiliteService.deleteDisponibilite(id); return Response.noContent().build(); } // === ENDPOINTS DE VALIDATION === @GET @Path("/employe/{employeId}/disponible") @Operation( summary = "Vérifier la disponibilité d'un employé", description = "Vérifie si un employé est disponible pour une période donnée") @APIResponse(responseCode = "200", description = "Statut de disponibilité retourné") public Response checkEmployeDisponibilite( @Parameter(description = "Identifiant de l'employé", required = true) @PathParam("employeId") UUID employeId, @Parameter(description = "Date/heure de début", required = true) @QueryParam("dateDebut") @NotNull LocalDateTime dateDebut, @Parameter(description = "Date/heure de fin", required = true) @QueryParam("dateFin") @NotNull LocalDateTime dateFin) { logger.debug("Vérification de disponibilité pour l'employé {}", employeId); boolean estDisponible = disponibiliteService.isEmployeDisponible(employeId, dateDebut, dateFin); return Response.ok( new Object() { public final boolean disponible = estDisponible; public final String message = estDisponible ? "Employé disponible pour cette période" : "Employé indisponible pour cette période"; }) .build(); } @GET @Path("/employe/{employeId}/conflits") @Operation( summary = "Rechercher les conflits de disponibilité", description = "Trouve les conflits de disponibilité pour un employé et une période") @APIResponse( responseCode = "200", description = "Conflits trouvés", content = @Content(schema = @Schema(implementation = Disponibilite.class))) public Response getConflits( @Parameter(description = "Identifiant de l'employé", required = true) @PathParam("employeId") UUID employeId, @Parameter(description = "Date/heure de début", required = true) @QueryParam("dateDebut") @NotNull LocalDateTime dateDebut, @Parameter(description = "Date/heure de fin", required = true) @QueryParam("dateFin") @NotNull LocalDateTime dateFin, @Parameter(description = "ID à exclure de la recherche") @QueryParam("excludeId") UUID excludeId) { logger.debug("Recherche de conflits pour l'employé {}", employeId); List conflits = disponibiliteService.getConflicts(employeId, dateDebut, dateFin, excludeId); return Response.ok(conflits).build(); } // === ENDPOINTS STATISTIQUES === @GET @Path("/statistiques") @Operation( summary = "Obtenir les statistiques des disponibilités", description = "Récupère les statistiques globales des disponibilités") @APIResponse(responseCode = "200", description = "Statistiques récupérées avec succès") public Response getStatistiques() { logger.debug("Récupération des statistiques des disponibilités"); Object statistiques = disponibiliteService.getStatistics(); return Response.ok(statistiques).build(); } @GET @Path("/statistiques/par-type") @Operation( summary = "Statistiques par type de disponibilité", description = "Récupère les statistiques détaillées par type") @APIResponse(responseCode = "200", description = "Statistiques par type récupérées") public Response getStatistiquesParType() { logger.debug("Récupération des statistiques par type"); List stats = disponibiliteService.getStatsByType(); return Response.ok(stats).build(); } @GET @Path("/statistiques/par-employe") @Operation( summary = "Statistiques par employé", description = "Récupère les statistiques de disponibilité par employé") @APIResponse(responseCode = "200", description = "Statistiques par employé récupérées") public Response getStatistiquesParEmploye() { logger.debug("Récupération des statistiques par employé"); List stats = disponibiliteService.getStatsByEmployee(); return Response.ok(stats).build(); } // === CLASSES DE REQUÊTE === public static class CreateDisponibiliteRequest { @Schema(description = "Identifiant unique de l'employé", required = true) public UUID employeId; @Schema( description = "Date et heure de début de la disponibilité", required = true, example = "2024-03-15T08:00:00") public LocalDateTime dateDebut; @Schema( description = "Date et heure de fin de la disponibilité", required = true, example = "2024-03-20T18:00:00") public LocalDateTime dateFin; @Schema( description = "Type de disponibilité", required = true, enumeration = { "CONGE_PAYE", "CONGE_SANS_SOLDE", "ARRET_MALADIE", "FORMATION", "ABSENCE", "HORAIRE_REDUIT" }) public String type; @Schema(description = "Motif ou raison de la disponibilité", example = "Congés annuels") public String motif; } public static class UpdateDisponibiliteRequest { @Schema(description = "Nouvelle date de début") public LocalDateTime dateDebut; @Schema(description = "Nouvelle date de fin") public LocalDateTime dateFin; @Schema(description = "Nouveau motif") public String motif; } public static class RejetDisponibiliteRequest { @Schema(description = "Raison du rejet de la demande", required = true) public String raisonRejet; } }