package dev.lions.unionflow.server.service; import dev.lions.unionflow.server.api.dto.communication.request.CreateConversationRequest; import dev.lions.unionflow.server.api.dto.communication.response.ConversationResponse; import dev.lions.unionflow.server.api.dto.communication.response.MessageResponse; import dev.lions.unionflow.server.entity.Conversation; import dev.lions.unionflow.server.entity.Membre; import dev.lions.unionflow.server.entity.Message; import dev.lions.unionflow.server.entity.Organisation; import dev.lions.unionflow.server.repository.ConversationRepository; import dev.lions.unionflow.server.repository.MembreRepository; import dev.lions.unionflow.server.repository.MessageRepository; import dev.lions.unionflow.server.repository.OrganisationRepository; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.transaction.Transactional; import jakarta.ws.rs.NotFoundException; import org.jboss.logging.Logger; import java.time.LocalDateTime; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; /** * Service de gestion des conversations * * @author UnionFlow Team * @version 1.0 * @since 2026-03-16 */ @ApplicationScoped public class ConversationService { private static final Logger LOG = Logger.getLogger(ConversationService.class); @Inject ConversationRepository conversationRepository; @Inject MessageRepository messageRepository; @Inject MembreRepository membreRepository; @Inject OrganisationRepository organisationRepository; /** * Liste les conversations d'un membre */ public List getConversations(UUID membreId, UUID organisationId, boolean includeArchived) { LOG.infof("Récupération conversations pour membre %s", membreId); List conversations; if (organisationId != null) { conversations = conversationRepository.findByOrganisation(organisationId); } else { conversations = conversationRepository.findByParticipant(membreId, includeArchived); } return conversations.stream() .map(c -> convertToResponse(c, membreId)) .collect(Collectors.toList()); } /** * Récupère une conversation par ID */ public ConversationResponse getConversationById(UUID conversationId, UUID membreId) { Conversation conversation = conversationRepository.findByIdAndParticipant(conversationId, membreId) .orElseThrow(() -> new NotFoundException("Conversation non trouvée ou accès refusé")); return convertToResponse(conversation, membreId); } /** * Crée une nouvelle conversation */ @Transactional public ConversationResponse createConversation(CreateConversationRequest request, UUID creatorId) { LOG.infof("Création conversation: %s (type: %s)", request.name(), request.type()); Conversation conversation = new Conversation(); conversation.setName(request.name()); conversation.setDescription(request.description()); conversation.setType(request.type()); // Ajouter les participants List participants = request.participantIds().stream() .map(id -> membreRepository.findById(id)) .filter(membre -> membre != null) .collect(Collectors.toList()); // Ajouter le créateur s'il n'est pas dans la liste Membre creator = membreRepository.findById(creatorId); if (creator != null && !participants.contains(creator)) { participants.add(creator); } conversation.setParticipants(participants); // Organisation if (request.organisationId() != null) { Organisation org = organisationRepository.findById(request.organisationId()); conversation.setOrganisation(org); } conversation.setUpdatedAt(LocalDateTime.now()); conversationRepository.persist(conversation); return convertToResponse(conversation, creatorId); } /** * Archive/désarchive une conversation */ @Transactional public void archiveConversation(UUID conversationId, UUID membreId, boolean archive) { Conversation conversation = conversationRepository.findByIdAndParticipant(conversationId, membreId) .orElseThrow(() -> new NotFoundException("Conversation non trouvée")); conversation.setIsArchived(archive); conversation.setUpdatedAt(LocalDateTime.now()); conversationRepository.persist(conversation); } /** * Marque une conversation comme lue */ @Transactional public void markAsRead(UUID conversationId, UUID membreId) { // Vérifier accès conversationRepository.findByIdAndParticipant(conversationId, membreId) .orElseThrow(() -> new NotFoundException("Conversation non trouvée")); messageRepository.markAllAsReadByConversationAndMember(conversationId, membreId); } /** * Toggle mute */ @Transactional public void toggleMute(UUID conversationId, UUID membreId) { Conversation conversation = conversationRepository.findByIdAndParticipant(conversationId, membreId) .orElseThrow(() -> new NotFoundException("Conversation non trouvée")); conversation.setIsMuted(!conversation.getIsMuted()); conversation.setUpdatedAt(LocalDateTime.now()); } /** * Toggle pin */ @Transactional public void togglePin(UUID conversationId, UUID membreId) { Conversation conversation = conversationRepository.findByIdAndParticipant(conversationId, membreId) .orElseThrow(() -> new NotFoundException("Conversation non trouvée")); conversation.setIsPinned(!conversation.getIsPinned()); conversation.setUpdatedAt(LocalDateTime.now()); } /** * Convertit Conversation en DTO */ private ConversationResponse convertToResponse(Conversation c, UUID currentUserId) { Message lastMsg = messageRepository.findLastByConversation(c.getId()); long unreadCount = messageRepository.countUnreadByConversationAndMember(c.getId(), currentUserId); return ConversationResponse.builder() .id(c.getId()) .name(c.getName()) .description(c.getDescription()) .type(c.getType()) .participantIds(c.getParticipants().stream().map(Membre::getId).collect(Collectors.toList())) .organisationId(c.getOrganisation() != null ? c.getOrganisation().getId() : null) .lastMessage(lastMsg != null ? convertMessageToResponse(lastMsg) : null) .unreadCount((int) unreadCount) .muted(Boolean.TRUE.equals(c.getIsMuted())) .pinned(Boolean.TRUE.equals(c.getIsPinned())) .archived(Boolean.TRUE.equals(c.getIsArchived())) .createdAt(c.getDateCreation()) .updatedAt(c.getUpdatedAt()) .avatarUrl(c.getAvatarUrl()) .build(); } /** * Convertit Message en DTO simple */ private MessageResponse convertMessageToResponse(Message m) { return MessageResponse.builder() .id(m.getId()) .conversationId(m.getConversation().getId()) .senderId(m.getSender().getId()) .senderName(m.getSenderName()) .senderAvatar(m.getSenderAvatar()) .content(m.getContent()) .type(m.getType()) .status(m.getStatus()) .priority(m.getPriority()) .createdAt(m.getDateCreation()) .edited(Boolean.TRUE.equals(m.getIsEdited())) .deleted(Boolean.TRUE.equals(m.getIsDeleted())) .build(); } }