package dev.lions.btpxpress.adapter.http; import dev.lions.btpxpress.application.service.AbonnementService; import dev.lions.btpxpress.domain.core.entity.Abonnement; import dev.lions.btpxpress.domain.core.entity.StatutAbonnement; import dev.lions.btpxpress.domain.core.entity.TypeAbonnement; import io.quarkus.security.Authenticated; 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.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.eclipse.microprofile.openapi.annotations.Operation; 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 abonnements * Architecture 2025 : API complète pour la gestion des abonnements */ @Path("/api/v1/abonnements") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) @Tag(name = "Abonnements", description = "Gestion des abonnements d'entreprise") public class AbonnementResource { private static final Logger logger = LoggerFactory.getLogger(AbonnementResource.class); @Inject AbonnementService abonnementService; // === ENDPOINTS DE LECTURE === @GET @Operation(summary = "Récupérer tous les abonnements") @APIResponse(responseCode = "200", description = "Liste des abonnements récupérée avec succès") public Response getAllAbonnements( @Parameter(description = "Statut") @QueryParam("statut") String statut, @Parameter(description = "Type d'abonnement") @QueryParam("type") String type) { logger.debug("GET /abonnements - statut: {}, type: {}", statut, type); List abonnements; if (statut != null && !statut.trim().isEmpty()) { try { StatutAbonnement statutEnum = StatutAbonnement.valueOf(statut.toUpperCase()); abonnements = abonnementService.findByStatut(statutEnum); } catch (IllegalArgumentException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(Map.of("error", "Statut invalide: " + statut)) .build(); } } else if (type != null && !type.trim().isEmpty()) { try { TypeAbonnement typeEnum = TypeAbonnement.valueOf(type.toUpperCase()); abonnements = abonnementService.findByType(typeEnum); } catch (IllegalArgumentException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(Map.of("error", "Type invalide: " + type)) .build(); } } else { abonnements = abonnementService.findAll(); } Map response = new HashMap<>(); response.put("abonnements", abonnements); response.put("total", abonnements.size()); return Response.ok(response).build(); } @GET @Path("/{id}") @Operation(summary = "Récupérer un abonnement par ID") @APIResponse(responseCode = "200", description = "Abonnement trouvé") @APIResponse(responseCode = "404", description = "Abonnement non trouvé") public Response getAbonnementById(@Parameter(description = "ID de l'abonnement") @PathParam("id") UUID id) { logger.debug("GET /abonnements/{}", id); Abonnement abonnement = abonnementService.findByIdRequired(id); return Response.ok(abonnement).build(); } @GET @Path("/actifs") @Operation(summary = "Récupérer tous les abonnements actifs") @APIResponse(responseCode = "200", description = "Liste des abonnements actifs") public Response getAbonnementsActifs() { logger.debug("GET /abonnements/actifs"); List abonnements = abonnementService.findActifs(); Map response = new HashMap<>(); response.put("abonnements", abonnements); response.put("total", abonnements.size()); return Response.ok(response).build(); } @GET @Path("/expires") @Operation(summary = "Récupérer tous les abonnements expirés") @APIResponse(responseCode = "200", description = "Liste des abonnements expirés") public Response getAbonnementsExpires() { logger.debug("GET /abonnements/expires"); List abonnements = abonnementService.findExpires(); Map response = new HashMap<>(); response.put("abonnements", abonnements); response.put("total", abonnements.size()); return Response.ok(response).build(); } @GET @Path("/bientot-expires") @Operation(summary = "Récupérer les abonnements qui arrivent à expiration") @APIResponse(responseCode = "200", description = "Liste des abonnements bientôt expirés") public Response getAbonnementsBientotExpires( @Parameter(description = "Nombre de jours") @QueryParam("jours") @DefaultValue("7") int jours) { logger.debug("GET /abonnements/bientot-expires - jours: {}", jours); List abonnements = abonnementService.findBientotExpires(jours); Map response = new HashMap<>(); response.put("abonnements", abonnements); response.put("total", abonnements.size()); return Response.ok(response).build(); } @GET @Path("/entreprise/{entrepriseId}") @Operation(summary = "Récupérer l'abonnement actif d'une entreprise") @APIResponse(responseCode = "200", description = "Abonnement actif trouvé") @APIResponse(responseCode = "404", description = "Aucun abonnement actif pour cette entreprise") public Response getAbonnementActifByEntreprise( @Parameter(description = "ID de l'entreprise") @PathParam("entrepriseId") UUID entrepriseId) { logger.debug("GET /abonnements/entreprise/{}", entrepriseId); return abonnementService .findAbonnementActifByEntreprise(entrepriseId) .map(abonnement -> Response.ok(abonnement).build()) .orElse( Response.status(Response.Status.NOT_FOUND) .entity(Map.of("error", "Aucun abonnement actif pour cette entreprise")) .build()); } @GET @Path("/entreprise/{entrepriseId}/historique") @Operation(summary = "Récupérer l'historique des abonnements d'une entreprise") @APIResponse(responseCode = "200", description = "Historique récupéré avec succès") public Response getHistoriqueByEntreprise( @Parameter(description = "ID de l'entreprise") @PathParam("entrepriseId") UUID entrepriseId) { logger.debug("GET /abonnements/entreprise/{}/historique", entrepriseId); List abonnements = abonnementService.findByEntreprise(entrepriseId); Map response = new HashMap<>(); response.put("abonnements", abonnements); response.put("total", abonnements.size()); return Response.ok(response).build(); } @GET @Path("/statistics") @Operation(summary = "Récupérer les statistiques des abonnements") @APIResponse(responseCode = "200", description = "Statistiques récupérées avec succès") public Response getStatistics() { logger.debug("GET /abonnements/statistics"); Map stats = abonnementService.getStatistics(); return Response.ok(stats).build(); } @GET @Path("/plans") @Operation(summary = "Récupérer les plans tarifaires disponibles") @APIResponse(responseCode = "200", description = "Plans récupérés avec succès") public Response getPlans() { logger.debug("GET /abonnements/plans"); Map plans = abonnementService.getPlans(); return Response.ok(plans).build(); } // === ENDPOINTS DE CRÉATION === @POST @Authenticated @Operation(summary = "Créer un nouvel abonnement") @APIResponse(responseCode = "201", description = "Abonnement créé avec succès") @APIResponse(responseCode = "400", description = "Données invalides") @APIResponse(responseCode = "409", description = "Un abonnement actif existe déjà") public Response createAbonnement(@Valid @NotNull Abonnement abonnement) { logger.info("POST /abonnements - Création d'un abonnement"); Abonnement created = abonnementService.create(abonnement); return Response.status(Response.Status.CREATED).entity(created).build(); } @POST @Path("/mensuel") @Authenticated @Operation(summary = "Créer un abonnement mensuel") @APIResponse(responseCode = "201", description = "Abonnement mensuel créé avec succès") @APIResponse(responseCode = "400", description = "Données invalides") public Response createAbonnementMensuel( @Parameter(description = "ID de l'entreprise") @QueryParam("entrepriseId") @NotNull UUID entrepriseId, @Parameter(description = "Type d'abonnement") @QueryParam("type") @NotNull String type, @Parameter(description = "Méthode de paiement") @QueryParam("methodePaiement") String methodePaiement) { logger.info( "POST /abonnements/mensuel - entrepriseId: {}, type: {}", entrepriseId, type); try { TypeAbonnement typeEnum = TypeAbonnement.valueOf(type.toUpperCase()); Abonnement created = abonnementService.createAbonnementMensuel(entrepriseId, typeEnum, methodePaiement); return Response.status(Response.Status.CREATED).entity(created).build(); } catch (IllegalArgumentException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(Map.of("error", "Type d'abonnement invalide: " + type)) .build(); } } @POST @Path("/annuel") @Authenticated @Operation(summary = "Créer un abonnement annuel") @APIResponse(responseCode = "201", description = "Abonnement annuel créé avec succès") @APIResponse(responseCode = "400", description = "Données invalides") public Response createAbonnementAnnuel( @Parameter(description = "ID de l'entreprise") @QueryParam("entrepriseId") @NotNull UUID entrepriseId, @Parameter(description = "Type d'abonnement") @QueryParam("type") @NotNull String type, @Parameter(description = "Méthode de paiement") @QueryParam("methodePaiement") String methodePaiement) { logger.info("POST /abonnements/annuel - entrepriseId: {}, type: {}", entrepriseId, type); try { TypeAbonnement typeEnum = TypeAbonnement.valueOf(type.toUpperCase()); Abonnement created = abonnementService.createAbonnementAnnuel(entrepriseId, typeEnum, methodePaiement); return Response.status(Response.Status.CREATED).entity(created).build(); } catch (IllegalArgumentException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(Map.of("error", "Type d'abonnement invalide: " + type)) .build(); } } // === ENDPOINTS DE MISE À JOUR === @PUT @Path("/{id}") @Authenticated @Operation(summary = "Mettre à jour un abonnement") @APIResponse(responseCode = "200", description = "Abonnement mis à jour avec succès") @APIResponse(responseCode = "404", description = "Abonnement non trouvé") public Response updateAbonnement( @Parameter(description = "ID de l'abonnement") @PathParam("id") UUID id, @Valid @NotNull Abonnement abonnementUpdate) { logger.info("PUT /abonnements/{}", id); Abonnement updated = abonnementService.update(id, abonnementUpdate); return Response.ok(updated).build(); } @POST @Path("/{id}/renouveler") @Authenticated @Operation(summary = "Renouveler un abonnement") @APIResponse(responseCode = "200", description = "Abonnement renouvelé avec succès") @APIResponse(responseCode = "404", description = "Abonnement non trouvé") public Response renouvelerAbonnement( @Parameter(description = "ID de l'abonnement") @PathParam("id") UUID id, @Parameter(description = "Renouvellement annuel") @QueryParam("annuel") @DefaultValue("false") boolean annuel) { logger.info("POST /abonnements/{}/renouveler - annuel: {}", id, annuel); Abonnement renewed = abonnementService.renouveler(id, annuel); return Response.ok(renewed).build(); } @PUT @Path("/{id}/changer-type") @Authenticated @Operation(summary = "Changer le type d'abonnement (upgrade/downgrade)") @APIResponse(responseCode = "200", description = "Type changé avec succès") @APIResponse(responseCode = "404", description = "Abonnement non trouvé") @APIResponse(responseCode = "400", description = "Type invalide") public Response changerType( @Parameter(description = "ID de l'abonnement") @PathParam("id") UUID id, @Parameter(description = "Nouveau type") @QueryParam("type") @NotNull String type) { logger.info("PUT /abonnements/{}/changer-type - nouveauType: {}", id, type); try { TypeAbonnement typeEnum = TypeAbonnement.valueOf(type.toUpperCase()); Abonnement updated = abonnementService.changerType(id, typeEnum); return Response.ok(updated).build(); } catch (IllegalArgumentException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(Map.of("error", "Type d'abonnement invalide: " + type)) .build(); } } @PUT @Path("/{id}/toggle-auto-renew") @Authenticated @Operation(summary = "Activer/désactiver le renouvellement automatique") @APIResponse(responseCode = "200", description = "Renouvellement automatique modifié") @APIResponse(responseCode = "404", description = "Abonnement non trouvé") public Response toggleAutoRenew(@Parameter(description = "ID de l'abonnement") @PathParam("id") UUID id) { logger.info("PUT /abonnements/{}/toggle-auto-renew", id); Abonnement updated = abonnementService.toggleAutoRenouvellement(id); return Response.ok(updated).build(); } @POST @Path("/{id}/annuler") @Authenticated @Operation(summary = "Annuler un abonnement") @APIResponse(responseCode = "204", description = "Abonnement annulé avec succès") @APIResponse(responseCode = "404", description = "Abonnement non trouvé") public Response annulerAbonnement(@Parameter(description = "ID de l'abonnement") @PathParam("id") UUID id) { logger.info("POST /abonnements/{}/annuler", id); abonnementService.annuler(id); return Response.status(Response.Status.NO_CONTENT).build(); } @POST @Path("/{id}/suspendre") @Authenticated @Operation(summary = "Suspendre un abonnement") @APIResponse(responseCode = "204", description = "Abonnement suspendu avec succès") @APIResponse(responseCode = "404", description = "Abonnement non trouvé") public Response suspendreAbonnement(@Parameter(description = "ID de l'abonnement") @PathParam("id") UUID id) { logger.info("POST /abonnements/{}/suspendre", id); abonnementService.suspendre(id); return Response.status(Response.Status.NO_CONTENT).build(); } @POST @Path("/{id}/reactiver") @Authenticated @Operation(summary = "Réactiver un abonnement suspendu") @APIResponse(responseCode = "204", description = "Abonnement réactivé avec succès") @APIResponse(responseCode = "404", description = "Abonnement non trouvé") public Response reactiverAbonnement(@Parameter(description = "ID de l'abonnement") @PathParam("id") UUID id) { logger.info("POST /abonnements/{}/reactiver", id); abonnementService.reactiver(id); return Response.status(Response.Status.NO_CONTENT).build(); } // === ENDPOINTS DE SUPPRESSION === @DELETE @Path("/{id}") @Authenticated @Operation(summary = "Supprimer définitivement un abonnement") @APIResponse(responseCode = "204", description = "Abonnement supprimé définitivement") @APIResponse(responseCode = "404", description = "Abonnement non trouvé") public Response deleteAbonnement(@Parameter(description = "ID de l'abonnement") @PathParam("id") UUID id) { logger.info("DELETE /abonnements/{}", id); abonnementService.deletePermanently(id); return Response.status(Response.Status.NO_CONTENT).build(); } }