using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Knot.Contracts.Conversations.Domain;
using Knot.Contracts.Messaging.Application.Abstractions;
using Knot.Contracts.Messaging.Domain;
using Knot.Modules.Conversations.Application.DTOs;
using Knot.Shared.Kernel;
using MediatR;

namespace Knot.Modules.Conversations.Application.Messages.SearchMessages;

public record SearchMessagesQuery(Guid UserId, string Query, Guid? ChatId) : IQuery<List<SearchMessageDto>>;

internal sealed class SearchMessagesQueryHandler : IQueryHandler<SearchMessagesQuery, List<SearchMessageDto>>
{
    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<Result<List<SearchMessageDto>>> 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<MediaDto>(),
                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<SimpleReactionDto>(),
                message.ReadByUsers.Select(id => new ReadByDto(id)).ToList()
            );
        }).ToList();

        return Result.Success(result);
    }
}



