package dev.lions.btpxpress.adapter.http; import dev.lions.btpxpress.application.service.ComparaisonFournisseurService; import dev.lions.btpxpress.domain.core.entity.*; 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.math.BigDecimal; import java.time.LocalDate; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * API REST pour la comparaison des fournisseurs EXPOSITION: Endpoints pour l'aide à la décision et * l'optimisation des achats BTP */ @Path("/api/v1/comparaisons-fournisseurs") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class ComparaisonFournisseurResource { private static final Logger logger = LoggerFactory.getLogger(ComparaisonFournisseurResource.class); @Inject ComparaisonFournisseurService comparaisonService; // === ENDPOINTS DE CONSULTATION === @GET @Path("/") public Response findAll( @QueryParam("page") @DefaultValue("0") int page, @QueryParam("size") @DefaultValue("50") int size) { try { logger.debug("GET /api/comparaisons-fournisseurs/ - page: {}, size: {}", page, size); List comparaisons; if (page > 0 || size < 1000) { comparaisons = comparaisonService.findAll(page, size); } else { comparaisons = comparaisonService.findAll(); } return Response.ok(comparaisons).build(); } catch (Exception e) { logger.error("Erreur lors de la récupération des comparaisons", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la récupération des comparaisons: " + e.getMessage()) .build(); } } @GET @Path("/{id}") public Response findById(@PathParam("id") UUID id) { try { logger.debug("GET /api/comparaisons-fournisseurs/{}", id); ComparaisonFournisseur comparaison = comparaisonService.findByIdRequired(id); return Response.ok(comparaison).build(); } catch (NotFoundException e) { return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build(); } catch (Exception e) { logger.error("Erreur lors de la récupération de la comparaison: " + id, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la récupération de la comparaison: " + e.getMessage()) .build(); } } @GET @Path("/materiel/{materielId}") public Response findByMateriel(@PathParam("materielId") UUID materielId) { try { logger.debug("GET /api/comparaisons-fournisseurs/materiel/{}", materielId); List comparaisons = comparaisonService.findByMateriel(materielId); return Response.ok(comparaisons).build(); } catch (Exception e) { logger.error( "Erreur lors de la récupération des comparaisons pour matériel: " + materielId, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la récupération des comparaisons: " + e.getMessage()) .build(); } } @GET @Path("/fournisseur/{fournisseurId}") public Response findByFournisseur(@PathParam("fournisseurId") UUID fournisseurId) { try { logger.debug("GET /api/comparaisons-fournisseurs/fournisseur/{}", fournisseurId); List comparaisons = comparaisonService.findByFournisseur(fournisseurId); return Response.ok(comparaisons).build(); } catch (Exception e) { logger.error( "Erreur lors de la récupération des comparaisons pour fournisseur: " + fournisseurId, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la récupération des comparaisons: " + e.getMessage()) .build(); } } @GET @Path("/session/{sessionId}") public Response findBySession(@PathParam("sessionId") String sessionId) { try { logger.debug("GET /api/comparaisons-fournisseurs/session/{}", sessionId); List comparaisons = comparaisonService.findBySession(sessionId); return Response.ok(comparaisons).build(); } catch (Exception e) { logger.error("Erreur lors de la récupération des comparaisons pour session: " + sessionId, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la récupération des comparaisons: " + e.getMessage()) .build(); } } @GET @Path("/search") public Response search(@QueryParam("terme") String terme) { try { logger.debug("GET /api/comparaisons-fournisseurs/search?terme={}", terme); List resultats = comparaisonService.search(terme); return Response.ok(resultats).build(); } catch (Exception e) { logger.error("Erreur lors de la recherche avec terme: " + terme, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la recherche: " + e.getMessage()) .build(); } } // === ENDPOINTS MÉTIER SPÉCIALISÉS === @GET @Path("/meilleures-offres/{materielId}") public Response findMeilleuresOffres( @PathParam("materielId") UUID materielId, @QueryParam("limite") @DefaultValue("5") int limite) { try { logger.debug("GET /api/comparaisons-fournisseurs/meilleures-offres/{}", materielId); List meilleures = comparaisonService.findMeilleuresOffres(materielId, limite); return Response.ok(meilleures).build(); } catch (Exception e) { logger.error("Erreur lors de la récupération des meilleures offres: " + materielId, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la récupération des meilleures offres: " + e.getMessage()) .build(); } } @GET @Path("/recommandees") public Response findOffresRecommandees() { try { logger.debug("GET /api/comparaisons-fournisseurs/recommandees"); List recommandees = comparaisonService.findOffresRecommandees(); return Response.ok(recommandees).build(); } catch (Exception e) { logger.error("Erreur lors de la récupération des offres recommandées", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la récupération des offres recommandées: " + e.getMessage()) .build(); } } @GET @Path("/gamme-prix") public Response findByGammePrix( @QueryParam("prixMin") @NotNull BigDecimal prixMin, @QueryParam("prixMax") @NotNull BigDecimal prixMax) { try { logger.debug( "GET /api/comparaisons-fournisseurs/gamme-prix?prixMin={}&prixMax={}", prixMin, prixMax); List comparaisons = comparaisonService.findByGammePrix(prixMin, prixMax); return Response.ok(comparaisons).build(); } catch (BadRequestException e) { return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build(); } catch (Exception e) { logger.error("Erreur lors de la recherche par gamme de prix", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la recherche par gamme de prix: " + e.getMessage()) .build(); } } @GET @Path("/disponibles-delai") public Response findDisponiblesDansDelai( @QueryParam("maxJours") @DefaultValue("30") int maxJours) { try { logger.debug("GET /api/comparaisons-fournisseurs/disponibles-delai?maxJours={}", maxJours); List disponibles = comparaisonService.findDisponiblesDansDelai(maxJours); return Response.ok(disponibles).build(); } catch (Exception e) { logger.error("Erreur lors de la recherche par délai", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la recherche par délai: " + e.getMessage()) .build(); } } // === ENDPOINTS DE CRÉATION ET GESTION === @POST @Path("/lancer-comparaison") public Response lancerComparaison(@Valid LancerComparaisonRequest request) { try { logger.info("POST /api/comparaisons-fournisseurs/lancer-comparaison"); String sessionId = comparaisonService.lancerComparaison( request.materielId, request.quantiteDemandee, request.uniteDemandee, request.dateDebutSouhaitee, request.dateFinSouhaitee, request.lieuLivraison, request.evaluateur); return Response.status(Response.Status.CREATED) .entity(Map.of("sessionId", sessionId)) .build(); } catch (NotFoundException e) { return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build(); } catch (Exception e) { logger.error("Erreur lors du lancement de la comparaison", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors du lancement de la comparaison: " + e.getMessage()) .build(); } } @PUT @Path("/{id}") public Response updateComparaison( @PathParam("id") UUID id, @Valid UpdateComparaisonRequest request) { try { logger.info("PUT /api/comparaisons-fournisseurs/{}", id); ComparaisonFournisseurService.ComparaisonUpdateRequest updateRequest = mapToServiceRequest(request); ComparaisonFournisseur comparaison = comparaisonService.updateComparaison(id, updateRequest); return Response.ok(comparaison).build(); } catch (NotFoundException e) { return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build(); } catch (Exception e) { logger.error("Erreur lors de la mise à jour de la comparaison: " + id, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la mise à jour de la comparaison: " + e.getMessage()) .build(); } } @PUT @Path("/{id}/calculer-scores") public Response calculerScores(@PathParam("id") UUID id, @Valid CalculerScoresRequest request) { try { logger.info("PUT /api/comparaisons-fournisseurs/{}/calculer-scores", id); ComparaisonFournisseur comparaison = comparaisonService.findByIdRequired(id); comparaisonService.calculerScores(comparaison, request.poidsCriteres); return Response.ok(comparaison).build(); } catch (NotFoundException e) { return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build(); } catch (Exception e) { logger.error("Erreur lors du calcul des scores: " + id, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors du calcul des scores: " + e.getMessage()) .build(); } } @PUT @Path("/session/{sessionId}/classer") public Response classerComparaisons(@PathParam("sessionId") String sessionId) { try { logger.info("PUT /api/comparaisons-fournisseurs/session/{}/classer", sessionId); comparaisonService.classerComparaisons(sessionId); List comparaisons = comparaisonService.findBySession(sessionId); return Response.ok( Map.of("message", "Classement effectué avec succès", "comparaisons", comparaisons)) .build(); } catch (Exception e) { logger.error("Erreur lors du classement des comparaisons: " + sessionId, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors du classement des comparaisons: " + e.getMessage()) .build(); } } // === ENDPOINTS D'ANALYSE ET RAPPORTS === @GET @Path("/statistiques") public Response getStatistiques() { try { logger.debug("GET /api/comparaisons-fournisseurs/statistiques"); Map statistiques = comparaisonService.getStatistiques(); return Response.ok(statistiques).build(); } catch (Exception e) { logger.error("Erreur lors de la génération des statistiques", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la génération des statistiques: " + e.getMessage()) .build(); } } @GET @Path("/evolution-prix/{materielId}") public Response analyserEvolutionPrix( @PathParam("materielId") UUID materielId, @QueryParam("dateDebut") @NotNull LocalDate dateDebut, @QueryParam("dateFin") @NotNull LocalDate dateFin) { try { logger.debug("GET /api/comparaisons-fournisseurs/evolution-prix/{}", materielId); List evolution = comparaisonService.analyserEvolutionPrix(materielId, dateDebut, dateFin); return Response.ok(evolution).build(); } catch (Exception e) { logger.error("Erreur lors de l'analyse d'évolution des prix: " + materielId, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de l'analyse d'évolution des prix: " + e.getMessage()) .build(); } } @GET @Path("/delais-fournisseurs") public Response analyserDelaisFournisseurs() { try { logger.debug("GET /api/comparaisons-fournisseurs/delais-fournisseurs"); List delais = comparaisonService.analyserDelaisFournisseurs(); return Response.ok(delais).build(); } catch (Exception e) { logger.error("Erreur lors de l'analyse des délais fournisseurs", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de l'analyse des délais fournisseurs: " + e.getMessage()) .build(); } } @GET @Path("/rapport/{sessionId}") public Response genererRapportComparaison(@PathParam("sessionId") String sessionId) { try { logger.debug("GET /api/comparaisons-fournisseurs/rapport/{}", sessionId); Map rapport = comparaisonService.genererRapportComparaison(sessionId); return Response.ok(rapport).build(); } catch (NotFoundException e) { return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build(); } catch (Exception e) { logger.error("Erreur lors de la génération du rapport: " + sessionId, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la génération du rapport: " + e.getMessage()) .build(); } } // === ENDPOINTS UTILITAIRES === @GET @Path("/criteres-comparaison") public Response getCriteresComparaison() { try { CritereComparaison[] criteres = CritereComparaison.values(); List> criteresInfo = Arrays.stream(criteres) .map( critere -> { Map map = new HashMap<>(); map.put("code", critere.name()); map.put("libelle", critere.getLibelle()); map.put("description", critere.getDescription()); map.put("poidsDefaut", critere.getPoidsDefaut()); map.put("uniteMesure", critere.getUniteMesure()); map.put("icone", critere.getIcone()); map.put("couleur", critere.getCouleur()); return map; }) .collect(Collectors.toList()); return Response.ok(criteresInfo).build(); } catch (Exception e) { logger.error("Erreur lors de la récupération des critères", e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR) .entity("Erreur lors de la récupération des critères: " + e.getMessage()) .build(); } } // === MÉTHODES UTILITAIRES === private ComparaisonFournisseurService.ComparaisonUpdateRequest mapToServiceRequest( UpdateComparaisonRequest request) { ComparaisonFournisseurService.ComparaisonUpdateRequest serviceRequest = new ComparaisonFournisseurService.ComparaisonUpdateRequest(); serviceRequest.disponible = request.disponible; serviceRequest.quantiteDisponible = request.quantiteDisponible; serviceRequest.dateDisponibilite = request.dateDisponibilite; serviceRequest.delaiLivraisonJours = request.delaiLivraisonJours; serviceRequest.prixUnitaireHT = request.prixUnitaireHT; serviceRequest.fraisLivraison = request.fraisLivraison; serviceRequest.fraisInstallation = request.fraisInstallation; serviceRequest.fraisMaintenance = request.fraisMaintenance; serviceRequest.cautionDemandee = request.cautionDemandee; serviceRequest.remiseAppliquee = request.remiseAppliquee; serviceRequest.dureeValiditeOffre = request.dureeValiditeOffre; serviceRequest.delaiPaiement = request.delaiPaiement; serviceRequest.garantieMois = request.garantieMois; serviceRequest.maintenanceIncluse = request.maintenanceIncluse; serviceRequest.formationIncluse = request.formationIncluse; serviceRequest.noteQualite = request.noteQualite; serviceRequest.noteFiabilite = request.noteFiabilite; serviceRequest.distanceKm = request.distanceKm; serviceRequest.conditionsParticulieres = request.conditionsParticulieres; serviceRequest.avantages = request.avantages; serviceRequest.inconvenients = request.inconvenients; serviceRequest.commentairesEvaluateur = request.commentairesEvaluateur; serviceRequest.recommandations = request.recommandations; serviceRequest.poidsCriteres = request.poidsCriteres; return serviceRequest; } // === CLASSES DE REQUÊTE === public static class LancerComparaisonRequest { @NotNull public UUID materielId; @NotNull public BigDecimal quantiteDemandee; public String uniteDemandee; public LocalDate dateDebutSouhaitee; public LocalDate dateFinSouhaitee; public String lieuLivraison; public String evaluateur; } public static class UpdateComparaisonRequest { public Boolean disponible; public BigDecimal quantiteDisponible; public LocalDate dateDisponibilite; public Integer delaiLivraisonJours; public BigDecimal prixUnitaireHT; public BigDecimal fraisLivraison; public BigDecimal fraisInstallation; public BigDecimal fraisMaintenance; public BigDecimal cautionDemandee; public BigDecimal remiseAppliquee; public Integer dureeValiditeOffre; public Integer delaiPaiement; public Integer garantieMois; public Boolean maintenanceIncluse; public Boolean formationIncluse; public BigDecimal noteQualite; public BigDecimal noteFiabilite; public BigDecimal distanceKm; public String conditionsParticulieres; public String avantages; public String inconvenients; public String commentairesEvaluateur; public String recommandations; public Map poidsCriteres; } public static class CalculerScoresRequest { public Map poidsCriteres; } }