package dev.lions.services; import dev.lions.events.ProjectUpdateEvent; import dev.lions.exceptions.BusinessException; import dev.lions.models.Project; import dev.lions.models.Testimonial; import dev.lions.repositories.ProjectRepository; import dev.lions.utils.ImageProcessor; import dev.lions.config.ApplicationConfig; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.event.Event; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; import java.util.Set; import lombok.Builder; import lombok.extern.slf4j.Slf4j; import java.util.List; import java.util.Optional; import java.time.LocalDateTime; /** * Service gérant la logique métier des projets. * Cette classe assure la gestion complète du cycle de vie des projets, * incluant leur création, mise à jour, recherche et validation. */ @Slf4j @ApplicationScoped @Builder public class ProjectService { @Inject ProjectRepository projectRepository; @Inject ImageProcessor imageProcessor; @Inject ApplicationConfig config; @Inject Event projectUpdateEvent; /** * Crée un nouveau projet avec son image associée. * * @param project Données du projet * @param imageData Image du projet en bytes * @return Projet créé */ @Transactional public Project createProject(@Valid @NotNull Project project, byte[] imageData) { log.info("Création d'un nouveau projet : {}", project.getTitle()); try { validateProject(project); processProjectImage(project, imageData); Project savedProject = projectRepository.save(project); notifyProjectCreation(savedProject); log.info("Projet créé avec succès - ID: {}", savedProject.getId()); return savedProject; } catch (BusinessException be) { log.warn("Validation échouée pour le projet", be); throw be; } catch (Exception e) { log.error("Erreur lors de la création du projet", e); throw new BusinessException("Impossible de créer le projet", e); } } /** * Met à jour un projet existant. * * @param project Projet à mettre à jour * @param imageData Nouvelle image optionnelle * @return Projet mis à jour */ @Transactional public Project updateProject(@Valid @NotNull Project project, byte[] imageData) { log.info("Mise à jour du projet : {}", project.getId()); try { validateProject(project); if (imageData != null) { processProjectImage(project, imageData); } Project updatedProject = projectRepository.update(project); notifyProjectUpdate(updatedProject); log.info("Projet mis à jour avec succès - ID: {}", updatedProject.getId()); return updatedProject; } catch (Exception e) { log.error("Erreur lors de la mise à jour du projet", e); throw new BusinessException("Impossible de mettre à jour le projet", e); } } /** * Traite et stocke l'image du projet. */ private void processProjectImage(Project project, byte[] imageData) { if (imageData == null || imageData.length == 0) { throw new BusinessException("L'image du projet est requise"); } String imageUrl = imageProcessor.processAndStoreProjectImage( imageData, project.getTitle(), config.getImageStoragePath() ); project.setImageUrl(imageUrl); } /** * Récupère un projet par son identifiant. */ public Optional findById(@NotNull String projectId) { log.debug("Recherche du projet : {}", projectId); return projectRepository.findById(projectId); } /** * Vérifie l'existence d'un projet. */ public boolean existsById(@NotNull String projectId) { return projectRepository.existsById(projectId); } /** * Récupère les projets filtrés par tag. */ public List getFilteredProjects(String filter) { log.debug("Filtrage des projets avec le critère : {}", filter); if ("all".equalsIgnoreCase(filter)) { return projectRepository.findAll(); } return projectRepository.findByTags(Set.of(filter.toLowerCase())); } /** * Récupère les projets mis en avant. */ public List getFeaturedProjects() { log.debug("Récupération des projets mis en avant"); return projectRepository.findByFeatured(true); } /** * Récupère les projets récents. */ public List getRecentProjects(int limit) { log.debug("Récupération des {} projets les plus récents", limit); return projectRepository.findRecentProjects(limit); } /** * Récupère le nombre total de projets. */ public long getProjectCount() { log.debug("Récupération du nombre total de projets"); return projectRepository.count(); } /** * Récupère les témoignages mis en avant. */ public List getFeaturedTestimonials() { log.debug("Récupération des témoignages mis en avant"); return projectRepository.findByFeatured(true).stream() .filter(project -> !project.getTestimonials().isEmpty()) .map(this::createTestimonialFromProject) .limit(3) .toList(); } /** * Valide les données d'un projet. */ private void validateProject(Project project) { if (project.getTitle() == null || project.getTitle().trim().isEmpty()) { throw new BusinessException("Le titre du projet est requis"); } if (project.getDescription() == null || project.getDescription().trim().isEmpty()) { throw new BusinessException("La description du projet est requise"); } if (project.getShortDescription() == null || project.getShortDescription().trim().isEmpty()) { throw new BusinessException("La description courte du projet est requise"); } } /** * Crée un témoignage à partir d'un projet. */ private Testimonial createTestimonialFromProject(Project project) { return Testimonial.builder() .clientName(project.getClientName()) .content(project.getTestimonials().get(0)) .projectTitle(project.getTitle()) .date(project.getCompletionDate()) .build(); } /** * Notifie la création d'un projet. */ private void notifyProjectCreation(Project project) { projectUpdateEvent.fire(new ProjectUpdateEvent( project.getId(), "CREATE", LocalDateTime.now() )); } /** * Notifie la mise à jour d'un projet. */ private void notifyProjectUpdate(Project project) { projectUpdateEvent.fire(new ProjectUpdateEvent( project.getId(), "UPDATE", LocalDateTime.now() )); } }