using Knot.Contracts.Messaging.Application.Abstractions;
using Knot.Contracts.Messaging.Domain;
using Knot.Shared.Kernel;
using MediatR;

namespace Knot.Modules.Messaging.Infrastructure.Handlers;

public sealed class MessageSentDomainEventHandler : INotificationHandler<MessageSentDomainEvent>
{
    private readonly IMessageNotifier _hubContext;
    private readonly IMessageRepository _messageRepository;
    private readonly IUserDisplayNameProvider _displayNameProvider;

    public MessageSentDomainEventHandler(
        IMessageNotifier hubContext,
        IMessageRepository messageRepository,
        IUserDisplayNameProvider displayNameProvider)
    {
        _hubContext = hubContext;
        _messageRepository = messageRepository;
        _displayNameProvider = displayNameProvider;
    }

    public async Task Handle(MessageSentDomainEvent notification, CancellationToken cancellationToken)
    {
        var message = await _messageRepository.GetByIdAsync(notification.MessageId, cancellationToken);
        if (message is null)
        {
            return;
        }

        var userInfo = await _displayNameProvider.GetUserInfoAsync(message.SenderId, cancellationToken);
        var senderObj = userInfo != null
            ? new { Id = userInfo.Id, Username = userInfo.Username, DisplayName = userInfo.DisplayName, Avatar = userInfo.Avatar }
            : new { Id = message.SenderId, Username = "unknown", DisplayName = "Unknown", Avatar = (string?)null };

        object? forwardedFromObj = null;
        if (message.ForwardedFromId.HasValue)
        {
            var fwdUserInfo = await _displayNameProvider.GetUserInfoAsync(message.ForwardedFromId.Value, cancellationToken);
            forwardedFromObj = fwdUserInfo != null
                ? new { Id = fwdUserInfo.Id, Username = fwdUserInfo.Username, DisplayName = fwdUserInfo.DisplayName, Avatar = fwdUserInfo.Avatar }
                : new { Id = message.ForwardedFromId.Value, Username = "unknown", DisplayName = "Unknown", Avatar = (string?)null };
        }

        object? replyToObj = null;
        if (message.ReplyToId.HasValue)
        {
            var replyMsg = await _messageRepository.GetByIdAsync(message.ReplyToId.Value, cancellationToken);
            if (replyMsg != null)
            {
                var replySenderInfo = await _displayNameProvider.GetUserInfoAsync(replyMsg.SenderId, cancellationToken);
                replyToObj = new
                {
                    Id = replyMsg.Id,
                    Content = replyMsg.Content,
                    Quote = message is TextMessage tm ? tm.Quote : null,
                    media = (replyMsg as MediaMessage)?.Media.Select(rm => new { rm.Type, rm.Url }).ToList() ?? (object)Array.Empty<object>(),
                    Sender = replySenderInfo != null
                        ? new { Id = replySenderInfo.Id, Username = replySenderInfo.Username, DisplayName = replySenderInfo.DisplayName }
                        : new { Id = replyMsg.SenderId, Username = "unknown", DisplayName = "Unknown" }
                };
            }
        }

        await _hubContext.NotifyNewMessageAsync(notification.ChatId, new
        {
            id = message.Id,
            chatId = message.ChatId,
            senderId = message.SenderId,
            content = message.Content,
            type = message.Type,
            createdAt = message.CreatedAt,
            forwardedFromId = message.ForwardedFromId,
            forwardedFrom = forwardedFromObj,
            replyToId = message.ReplyToId,
            replyTo = replyToObj,
            quote = message is TextMessage tm2 ? tm2.Quote : null,
            media = (message as MediaMessage)?.Media.Select(m => new
            {
                type = m.Type,
                url = m.Url,
                filename = m.FileId,
                size = m.Size
            }).ToList() ?? (object)Array.Empty<object>(),
            sender = senderObj,
            readBy = message.ReadByUsers.Select(id => new { id }).ToList(),
            storyId = (message as StoryMessage)?.StoryId,
            storyMediaUrl = (message as StoryMessage)?.StoryMediaUrl,
            storyMediaType = (message as StoryMessage)?.StoryMediaType,
            callType = (message as CallMessage)?.CallType,
            callStatus = (message as CallMessage)?.CallStatus,
            duration = (message as CallMessage)?.Duration,
            pollOptions = (message as PollMessage)?.Options.Select(o => new { id = o.Id, text = o.Text, voteCount = o.VoteCount }).ToList(),
            pollIsMultipleChoice = (message as PollMessage)?.IsMultipleChoice,
            pollIsClosed = (message as PollMessage)?.IsClosed,
            pollIsAnonymous = (message as PollMessage)?.IsAnonymous
        }, cancellationToken);
    }
}




