package com.lions.dev.service; import com.lions.dev.entity.friends.Friendship; import com.lions.dev.entity.social.SocialPost; import com.lions.dev.entity.users.Users; import com.lions.dev.exception.UserNotFoundException; import com.lions.dev.repository.FriendshipRepository; import com.lions.dev.repository.SocialPostRepository; import com.lions.dev.repository.UsersRepository; import com.lions.dev.dto.events.ReactionEvent; import org.eclipse.microprofile.reactive.messaging.Channel; import org.eclipse.microprofile.reactive.messaging.Emitter; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import org.jboss.logging.Logger; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; /** * Service de gestion des posts sociaux. * * Ce service contient la logique métier pour la création, récupération, * mise à jour et suppression des posts sociaux. */ @ApplicationScoped public class SocialPostService { private static final Logger logger = Logger.getLogger(SocialPostService.class); @Inject SocialPostRepository socialPostRepository; @Inject UsersRepository usersRepository; @Inject FriendshipRepository friendshipRepository; @Inject NotificationService notificationService; @Inject @Channel("reactions") Emitter reactionEmitter; // v2.0 - Publie dans Kafka /** * Récupère tous les posts avec pagination. * * @param page Le numéro de la page (0-indexé) * @param size La taille de la page * @return Liste paginée des posts */ public List getAllPosts(int page, int size) { logger.info("[SocialPostService] Récupération de tous les posts (page: " + page + ", size: " + size + ")"); return socialPostRepository.findAllWithPagination(page, size); } /** * Récupère tous les posts d'un utilisateur. * * @param userId L'ID de l'utilisateur * @return Liste des posts de l'utilisateur * @throws UserNotFoundException Si l'utilisateur n'existe pas */ public List getPostsByUserId(UUID userId) { logger.info("[SocialPostService] Récupération des posts pour l'utilisateur ID : " + userId); Users user = usersRepository.findById(userId); if (user == null) { logger.error("[SocialPostService] Utilisateur non trouvé avec l'ID : " + userId); throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId); } return socialPostRepository.findByUserId(userId); } /** * Crée un nouveau post social. * * @param content Le contenu du post * @param userId L'ID de l'utilisateur créateur * @param imageUrl L'URL de l'image (optionnel) * @return Le post créé * @throws UserNotFoundException Si l'utilisateur n'existe pas */ @Transactional public SocialPost createPost(String content, UUID userId, String imageUrl) { logger.info("[SocialPostService] Création d'un post par l'utilisateur ID : " + userId); Users user = usersRepository.findById(userId); if (user == null) { logger.error("[SocialPostService] Utilisateur non trouvé avec l'ID : " + userId); throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId); } SocialPost post = new SocialPost(content, user); if (imageUrl != null && !imageUrl.isEmpty()) { post.setImageUrl(imageUrl); } socialPostRepository.persist(post); logger.info("[SocialPostService] Post créé avec succès : " + post.getId()); // Créer des notifications pour tous les amis try { List friendships = friendshipRepository.findFriendsByUser(user, 0, Integer.MAX_VALUE); // v2.0 - Utiliser les nouveaux noms de champs String userName = user.getFirstName() + " " + user.getLastName(); for (Friendship friendship : friendships) { Users friend = friendship.getUser().equals(user) ? friendship.getFriend() : friendship.getUser(); String notificationTitle = "Nouveau post de " + userName; String notificationMessage = userName + " a publié un nouveau post : " + (content.length() > 50 ? content.substring(0, 50) + "..." : content); notificationService.createNotification( notificationTitle, notificationMessage, "post", friend.getId(), null ); } logger.info("[SocialPostService] Notifications créées pour " + friendships.size() + " ami(s)"); } catch (Exception e) { logger.error("[SocialPostService] Erreur lors de la création des notifications : " + e.getMessage()); } return post; } /** * Récupère un post par son ID. * * @param postId L'ID du post * @return Le post trouvé */ public SocialPost getPostById(UUID postId) { logger.info("[SocialPostService] Récupération du post ID : " + postId); SocialPost post = socialPostRepository.findById(postId); if (post == null) { logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId); throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId); } return post; } /** * Met à jour un post. * * @param postId L'ID du post * @param content Le nouveau contenu * @param imageUrl La nouvelle URL d'image (optionnel) * @return Le post mis à jour */ @Transactional public SocialPost updatePost(UUID postId, String content, String imageUrl) { logger.info("[SocialPostService] Mise à jour du post ID : " + postId); SocialPost post = socialPostRepository.findById(postId); if (post == null) { logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId); throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId); } post.setContent(content); if (imageUrl != null) { post.setImageUrl(imageUrl); } socialPostRepository.persist(post); logger.info("[SocialPostService] Post mis à jour avec succès"); return post; } /** * Supprime un post. * * @param postId L'ID du post * @return true si le post a été supprimé, false sinon */ @Transactional public boolean deletePost(UUID postId) { logger.info("[SocialPostService] Suppression du post ID : " + postId); SocialPost post = socialPostRepository.findById(postId); if (post == null) { logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId); return false; } socialPostRepository.delete(post); logger.info("[SocialPostService] Post supprimé avec succès"); return true; } /** * Recherche des posts par contenu. * * @param query Le terme de recherche * @return Liste des posts correspondant à la recherche */ public List searchPosts(String query) { logger.info("[SocialPostService] Recherche de posts avec la requête : " + query); return socialPostRepository.searchByContent(query); } /** * Like un post (incrémente le compteur de likes). * * @param postId L'ID du post * @param userId L'ID de l'utilisateur qui like (v2.0) * @return Le post mis à jour */ @Transactional public SocialPost likePost(UUID postId, UUID userId) { logger.info("[SocialPostService] Like du post ID : " + postId + " par utilisateur : " + userId); SocialPost post = socialPostRepository.findById(postId); if (post == null) { logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId); throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId); } post.incrementLikes(); socialPostRepository.persist(post); // Notification pour l'auteur du post (sauf auto-like) try { Users author = post.getUser(); if (author != null && !author.getId().equals(userId)) { Users liker = usersRepository.findById(userId); String likerName = liker != null ? liker.getFirstName() + " " + liker.getLastName() : "Quelqu'un"; notificationService.createNotification( "Nouveau like", likerName + " a aimé votre post", "post", author.getId(), null ); } } catch (Exception e) { logger.error("[SocialPostService] Erreur création notification like : " + e.getMessage()); } // TEMPS RÉEL: Publier dans Kafka (v2.0) try { Map reactionData = new HashMap<>(); reactionData.put("ownerId", post.getUser().getId().toString()); // Propriétaire du post reactionData.put("likesCount", post.getLikesCount()); reactionData.put("postTitle", post.getContent().length() > 50 ? post.getContent().substring(0, 50) + "..." : post.getContent()); ReactionEvent event = new ReactionEvent( postId.toString(), // targetId "post", // targetType userId.toString(), // userId qui réagit "like", // reactionType reactionData ); reactionEmitter.send(event); logger.info("[SocialPostService] Réaction like publiée dans Kafka pour post: " + postId); } catch (Exception e) { logger.error("[SocialPostService] Erreur publication Kafka: " + e.getMessage()); // Ne pas bloquer le like si Kafka échoue } return post; } /** * Ajoute un commentaire à un post (incrémente le compteur de commentaires). * * @param postId L'ID du post * @param userId L'ID de l'utilisateur qui commente (v2.0) * @param commentContent Le contenu du commentaire (v2.0) * @return Le post mis à jour */ @Transactional public SocialPost addComment(UUID postId, UUID userId, String commentContent) { logger.info("[SocialPostService] Ajout de commentaire au post ID : " + postId + " par utilisateur : " + userId); SocialPost post = socialPostRepository.findById(postId); if (post == null) { logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId); throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId); } post.incrementComments(); socialPostRepository.persist(post); // Notification pour l'auteur du post (sauf auto-commentaire) try { Users author = post.getUser(); if (author != null && !author.getId().equals(userId)) { Users commenter = usersRepository.findById(userId); String commenterName = commenter != null ? commenter.getFirstName() + " " + commenter.getLastName() : "Quelqu'un"; String preview = commentContent != null && commentContent.length() > 60 ? commentContent.substring(0, 60) + "..." : (commentContent != null ? commentContent : ""); notificationService.createNotification( "Nouveau commentaire", commenterName + " a commenté votre post : " + preview, "post", author.getId(), null ); } } catch (Exception e) { logger.error("[SocialPostService] Erreur création notification commentaire : " + e.getMessage()); } // TEMPS RÉEL: Publier dans Kafka (v2.0) try { Users commenter = usersRepository.findById(userId); Map reactionData = new HashMap<>(); reactionData.put("ownerId", post.getUser().getId().toString()); // Propriétaire du post reactionData.put("commentsCount", post.getCommentsCount()); reactionData.put("commentContent", commentContent != null && commentContent.length() > 100 ? commentContent.substring(0, 100) + "..." : commentContent); reactionData.put("commenterName", commenter != null ? commenter.getFirstName() + " " + commenter.getLastName() : "Utilisateur"); ReactionEvent event = new ReactionEvent( postId.toString(), // targetId "post", // targetType userId.toString(), // userId qui commente "comment", // reactionType reactionData ); reactionEmitter.send(event); logger.info("[SocialPostService] Réaction comment publiée dans Kafka pour post: " + postId); } catch (Exception e) { logger.error("[SocialPostService] Erreur publication Kafka: " + e.getMessage()); // Ne pas bloquer le commentaire si Kafka échoue } return post; } /** * Partage un post (incrémente le compteur de partages). * * @param postId L'ID du post * @param userId L'ID de l'utilisateur qui partage (v2.0) * @return Le post mis à jour */ @Transactional public SocialPost sharePost(UUID postId, UUID userId) { logger.info("[SocialPostService] Partage du post ID : " + postId + " par utilisateur : " + userId); SocialPost post = socialPostRepository.findById(postId); if (post == null) { logger.error("[SocialPostService] Post non trouvé avec l'ID : " + postId); throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId); } post.incrementShares(); socialPostRepository.persist(post); // TEMPS RÉEL: Publier dans Kafka (v2.0) try { Map reactionData = new HashMap<>(); reactionData.put("ownerId", post.getUser().getId().toString()); // Propriétaire du post reactionData.put("sharesCount", post.getSharesCount()); ReactionEvent event = new ReactionEvent( postId.toString(), // targetId "post", // targetType userId.toString(), // userId qui partage "share", // reactionType reactionData ); reactionEmitter.send(event); logger.info("[SocialPostService] Réaction share publiée dans Kafka pour post: " + postId); } catch (Exception e) { logger.error("[SocialPostService] Erreur publication Kafka: " + e.getMessage()); // Ne pas bloquer le partage si Kafka échoue } return post; } /** * Récupère les posts de l'utilisateur et de ses amis (relations d'amitié acceptées). * * @param userId L'ID de l'utilisateur * @param page Le numéro de la page (0-indexé) * @param size La taille de la page * @return Liste paginée des posts de l'utilisateur et de ses amis * @throws UserNotFoundException Si l'utilisateur n'existe pas */ public List getPostsByFriends(UUID userId, int page, int size) { logger.info("[SocialPostService] Récupération des posts des amis pour l'utilisateur ID : " + userId); Users user = usersRepository.findById(userId); if (user == null) { logger.error("[SocialPostService] Utilisateur non trouvé avec l'ID : " + userId); throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId); } // Récupérer toutes les relations d'amitié acceptées List friendships = friendshipRepository.findFriendsByUser(user, 0, Integer.MAX_VALUE); // Extraire les IDs des amis List friendIds = friendships.stream() .map(friendship -> { // L'ami est soit dans 'user' soit dans 'friend', selon qui a initié la relation return friendship.getUser().equals(user) ? friendship.getFriend().getId() : friendship.getUser().getId(); }) .distinct() .collect(Collectors.toList()); logger.info("[SocialPostService] " + friendIds.size() + " ami(s) trouvé(s) pour l'utilisateur ID : " + userId); // Récupérer les posts de l'utilisateur et de ses amis return socialPostRepository.findPostsByFriends(userId, friendIds, page, size); } }