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.Contracts.Conversations.Application.Abstractions;
using Knot.Modules.Conversations.Application.DTOs;
using Knot.Contracts.Conversations.Domain;
using Knot.Shared.Kernel;
using MediatR;

namespace Knot.Modules.Conversations.Application.Chats.GetChatById;

public record GetChatByIdQuery(Guid UserId, Guid ChatId) : IQuery<ChatDto?>;

internal sealed class GetChatByIdQueryHandler : IQueryHandler<GetChatByIdQuery, ChatDto?>
{
    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<Result<ChatDto?>> Handle(GetChatByIdQuery request, CancellationToken cancellationToken)
    {
        var chat = await _chatRepository.GetByIdAsync(request.ChatId, cancellationToken);
        if (chat == null)
        {
            return Result.Success<ChatDto?>(null);
        }

        if (!chat.Members.Any(m => m.UserId == request.UserId))
        {
            return Result.Failure<ChatDto?>(ChatErrors.ChatsForbidden);
        }

        var userIdsToFetch = new HashSet<Guid>();
        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<MessageReaction>();

        if (latestMessage != null)
        {
            userIdsToFetch.Add(latestMessage.SenderId);
            foreach (var reaction in latestReactions)
            {
                userIdsToFetch.Add(reaction.UserId);
            }
        }

        var pinnedMessages = await _messageRepository.GetPinnedMessagesAsync(chat.Id, cancellationToken);
        foreach (var pm in pinnedMessages) userIdsToFetch.Add(pm.SenderId);

        var usersInfo = await _userProvider.GetUsersInfoAsync(userIdsToFetch, cancellationToken);

        var members = new List<ChatMemberDto>();
        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<ChatMessageDto>();
        if (latestMessage != null)
        {
            messagesList.Add(MessageMapper.MapToDto(
                latestMessage,
                usersInfo,
                latestReactions,
                chat.Members.Where(m => m.LastReadSequenceId >= latestMessage.SequenceId && m.UserId != latestMessage.SenderId).Select(m => m.UserId)));
        }

        var pinnedDtoList = new List<PinnedMessageDto>();
        foreach (var pm in pinnedMessages)
        {
            pinnedDtoList.Add(new PinnedMessageDto(
                pm.Id,
                MessageMapper.MapToDto(pm, usersInfo, new List<MessageReaction>(), new List<Guid>())
            ));
        }

        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,
            pinnedDtoList,
            unreadCount
        );

        return Result.Success<ChatDto?>(dto);
    }
}



