using Knot.Shared.Kernel;
using Knot.Contracts.Messaging.Domain;
using Knot.Modules.Conversations.Application.DTOs;

namespace Knot.Modules.Conversations.Application.DTOs;

public static class MessageMapper
{
    public static ChatMessageDto MapToDto(
        Message message,
        IReadOnlyDictionary<Guid, UserInfo> usersInfo,
        IEnumerable<MessageReaction> reactions,
        IEnumerable<Guid> readByUsers,
        Guid? currentUserId = null)
    {
        usersInfo.TryGetValue(message.SenderId, out var senderObj);

        var reactionsWithUser = new List<ReactionDto>();
        foreach (var reaction in reactions)
        {
            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 textMessage = message as TextMessage;
        var mediaMessage = message as MediaMessage;
        var storyMessage = message as StoryMessage;
        var callMessage = message as CallMessage;

        return new ChatMessageDto(
            message.Id,
            message.ChatId,
            message.SenderId,
            message.Content,
            message.Type,
            message.ReplyToId,
            textMessage?.Quote,
            storyMessage?.StoryId,
            storyMessage?.StoryMediaUrl,
            storyMessage?.StoryMediaType,
            message.IsEdited,
            message.IsDeleted,
            message.CreatedAt,
            message.SequenceId,
            mediaMessage?.Media.Select(media => new MediaDto(media.Id, media.Type, media.Url, media.Filename, media.Size, media.Duration)).ToList() ?? new List<MediaDto>(),
            senderObj != null ? new MessageSenderDto(
                senderObj.Id,
                senderObj.Username,
                senderObj.DisplayName,
                senderObj.Avatar
            ) : new MessageSenderDto(message.SenderId, "unknown", "Unknown", null),
            reactionsWithUser,
            readByUsers.Select(id => new ReadByDto(id)).ToList(),
            callMessage?.CallType,
            callMessage?.CallStatus,
            callMessage?.Duration,
            message is PollMessage pm ? pm.Options.Select(o => {
                var voters = pm.IsAnonymous == false
                    ? pm.Votes
                        .Where(v => v.OptionId == o.Id)
                        .Select(v => {
                            usersInfo.TryGetValue(v.UserId, out var vu);
                            return vu != null
                                ? new MessageSenderDto(vu.Id, vu.Username, vu.DisplayName, vu.Avatar)
                                : new MessageSenderDto(v.UserId, "unknown", "Unknown", null);
                        })
                        .ToList()
                    : null;
                return new PollOptionDto(o.Id, o.Text, o.VoteCount, voters, pm.IsAnonymous == false ? pm.Votes.Where(v => v.OptionId == o.Id).Select(v => v.UserId).ToList() : null);
            }).ToList() : null,
            (message as PollMessage)?.IsMultipleChoice,
            (message as PollMessage)?.IsAnonymous,
            (message as PollMessage)?.IsClosed,
            (message is PollMessage poll && currentUserId.HasValue) ? poll.Votes.Where(v => v.UserId == currentUserId.Value).Select(v => v.OptionId).ToList() : null
        );
    }
}
