using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using Knot.Contracts.Messaging.Application.Abstractions; using Knot.Contracts.Messaging.Domain; using Knot.Modules.Conversations.Application.DTOs; using Knot.Contracts.Conversations.Domain; using Knot.Shared.Kernel; using MediatR; namespace Knot.Modules.Conversations.Application.Messages.SearchMessages; public record SearchMessagesQuery(Guid UserId, string Query, Guid? ChatId) : IQuery>; internal sealed class SearchMessagesQueryHandler : IQueryHandler> { private readonly IMessageRepository _messageRepository; private readonly IUserDisplayNameProvider _userProvider; private readonly IMessageReactionRepository _reactionRepository; public SearchMessagesQueryHandler(IMessageRepository messageRepository, IUserDisplayNameProvider userProvider, IMessageReactionRepository reactionRepository) { _messageRepository = messageRepository; _userProvider = userProvider; _reactionRepository = reactionRepository; } public async Task>> Handle(SearchMessagesQuery request, CancellationToken cancellationToken) { var messages = await _messageRepository.SearchMessagesAsync(request.Query, request.ChatId, request.UserId, cancellationToken); messages = messages.Where(message => !message.IsDeletedForUser(request.UserId)).ToList(); var userIds = messages.Select(message => message.SenderId).ToList(); userIds.AddRange(messages.Where(message => message.ForwardedFromId.HasValue).Select(message => message.ForwardedFromId!.Value)); var senders = await _userProvider.GetUsersInfoAsync(userIds.Distinct(), cancellationToken); var messageIds = messages.Select(m => m.Id).ToList(); var allReactions = await _reactionRepository.GetReactionsForMessagesAsync(messageIds, cancellationToken); var reactionsByMessage = allReactions.GroupBy(r => r.MessageId).ToDictionary(g => g.Key, g => g.ToList()); var result = messages.Select(message => { var textMessage = message as TextMessage; var mediaMessage = message as MediaMessage; var storyMessage = message as StoryMessage; return new SearchMessageDto( message.Id, message.ChatId, message.SenderId, message.Content, message.Type, message.ReplyToId, textMessage?.Quote, message.IsEdited, message.IsDeleted, message.CreatedAt, message.SequenceId, message.ForwardedFromId, null, storyMessage?.StoryId, storyMessage?.StoryMediaUrl, storyMessage?.StoryMediaType, mediaMessage?.Media.Select(media => new MediaDto(media.Id, media.Type, media.Url, media.Filename, media.Size)).ToList() ?? new List(), senders.TryGetValue(message.SenderId, out var senderUser) ? new MessageSenderDto(senderUser.Id, senderUser.Username, senderUser.DisplayName, senderUser.Avatar) : new MessageSenderDto(message.SenderId, "unknown", "Unknown", null), reactionsByMessage.TryGetValue(message.Id, out var mr) ? mr.Select(reaction => new SimpleReactionDto(reaction.UserId, reaction.Emoji)).ToList() : new List(), new List() ); }).ToList(); return Result.Success(result); } }