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.Abstractions; using Knot.Modules.Conversations.Application.DTOs; using Knot.Modules.Conversations.Domain; using Knot.Shared.Kernel; using MediatR; namespace Knot.Modules.Conversations.Application.Chats.GetChatById; public record GetChatByIdQuery(Guid UserId, Guid ChatId) : IQuery; internal sealed class GetChatByIdQueryHandler : IQueryHandler { private readonly IChatRepository _chatRepository; private readonly IUserDisplayNameProvider _userProvider; private readonly IMessageRepository _messageRepository; private readonly IMessageReactionRepository _reactionRepository; public GetChatByIdQueryHandler(IChatRepository chatRepository, IUserDisplayNameProvider userProvider, IMessageRepository messageRepository, IMessageReactionRepository reactionRepository) { _chatRepository = chatRepository; _userProvider = userProvider; _messageRepository = messageRepository; _reactionRepository = reactionRepository; } public async Task> Handle(GetChatByIdQuery request, CancellationToken cancellationToken) { var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken); if (chat == null) { return Result.Success(null); } if (!chat.Members.Any(m => m.UserId == request.UserId)) { return Result.Failure(ChatErrors.ChatsForbidden); } var userIdsToFetch = new HashSet(); foreach (var member in chat.Members) { userIdsToFetch.Add(member.UserId); } var latestMessage = await _messageRepository.GetLatestChatMessageAsync(chat.Id, cancellationToken); var latestReactions = latestMessage != null ? await _reactionRepository.GetReactionsForMessageAsync(latestMessage.Id, cancellationToken) : new List(); if (latestMessage != null) { userIdsToFetch.Add(latestMessage.SenderId); foreach (var reaction in latestReactions) { userIdsToFetch.Add(reaction.UserId); } } var usersInfo = await _userProvider.GetUsersInfoAsync(userIdsToFetch, cancellationToken); var members = new List(); foreach (var member in chat.Members) { usersInfo.TryGetValue(member.UserId, out var user); members.Add(new ChatMemberDto( member.Id, member.UserId, member.Role, member.IsPinned, user != null ? new ChatUserDto( user.Id, user.Username, user.DisplayName, user.Avatar, false, DateTime.UtcNow ) : null )); } var messagesList = new List(); if (latestMessage != null) { usersInfo.TryGetValue(latestMessage.SenderId, out var senderObj); var reactionsWithUser = new List(); foreach (var reaction in latestReactions) { usersInfo.TryGetValue(reaction.UserId, out var reactionUser); reactionsWithUser.Add(new ReactionDto( reaction.Id, reaction.Emoji, reaction.UserId, reactionUser != null ? new MessageSenderDto(reactionUser.Id, reactionUser.Username, reactionUser.DisplayName, reactionUser.Avatar) : new MessageSenderDto(reaction.UserId, "unknown", "Unknown", null) )); } var readByList = chat.Members .Where(m => m.LastReadSequenceId >= latestMessage.SequenceId && m.UserId != latestMessage.SenderId) .Select(m => new ReadByDto(m.UserId)) .ToList(); messagesList.Add(new ChatMessageDto( latestMessage.Id, latestMessage.ChatId, latestMessage.SenderId, latestMessage.Content, latestMessage.Type, latestMessage.ReplyToId, latestMessage.Quote, latestMessage.StoryId, latestMessage.StoryMediaUrl, latestMessage.StoryMediaType, latestMessage.IsEdited, latestMessage.IsDeleted, latestMessage.CreatedAt, latestMessage.SequenceId, latestMessage.Media.Select(media => new MediaDto(media.Id, media.Type, media.Url, media.Filename, media.Size)).ToList(), senderObj != null ? new MessageSenderDto( senderObj.Id, senderObj.Username, senderObj.DisplayName, senderObj.Avatar ) : new MessageSenderDto(latestMessage.SenderId, "unknown", "Unknown", null), reactionsWithUser, readByList )); } var currentMember = chat.Members.First(m => m.UserId == request.UserId); var unreadCount = (int)Math.Max(0, chat.LastMessageSequenceId - currentMember.LastReadSequenceId); var dto = new ChatDto( chat.Id, chat.Type.ToString().ToLowerInvariant(), chat.Type == ChatType.Favorites ? "Избранное" : (chat.Type == ChatType.Personal ? null : chat.Name), chat.Description, chat.Avatar, chat.CreatedAt, members, messagesList, unreadCount ); return Result.Success(dto); } }