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.GetChats;

public record GetChatsQuery(Guid UserId) : IQuery<List<ChatDto>>;

internal sealed class GetChatsQueryHandler : IQueryHandler<GetChatsQuery, List<ChatDto>>
{
    private readonly IChatRepository _chatRepository;
    private readonly IUserDisplayNameProvider _userProvider;
    private readonly IMessageRepository _messageRepository;
    private readonly IMessageReactionRepository _reactionRepository;

    public GetChatsQueryHandler(IChatRepository chatRepository, IUserDisplayNameProvider userProvider, IMessageRepository messageRepository, IMessageReactionRepository reactionRepository)
    {
        _chatRepository = chatRepository;
        _userProvider = userProvider;
        _messageRepository = messageRepository;
        _reactionRepository = reactionRepository;
    }

    public async Task<Result<List<ChatDto>>> Handle(GetChatsQuery request, CancellationToken cancellationToken)
    {
        var userChats = await _chatRepository.GetUserChatsAsync(request.UserId, cancellationToken);
        var dtos = new List<ChatDto>();

        foreach (var chat in userChats)
        {
            var latestMessage = await _messageRepository.GetLatestChatMessageAsync(chat.Id, cancellationToken);
            var latestReactions = latestMessage != null
                ? await _reactionRepository.GetReactionsForMessageAsync(latestMessage.Id, cancellationToken)
                : new List<MessageReaction>();

            var userIdsToFetch = new HashSet<Guid>();

            foreach (var member in chat.Members)
            {
                userIdsToFetch.Add(member.UserId);
            }

            if (latestMessage != null)
            {
                userIdsToFetch.Add(latestMessage.SenderId);
                foreach (var r in latestReactions)
                {
                    userIdsToFetch.Add(r.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.FirstOrDefault(m => m.UserId == request.UserId);
            var unreadCount = currentMember != null ? (int)Math.Max(0, chat.LastMessageSequenceId - currentMember.LastReadSequenceId) : 0;

            dtos.Add(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,
                chat.IsImporting,
                chat.ImportJobId
            ));
        }

        var sorted = dtos.OrderByDescending(d => d.Messages.FirstOrDefault()?.CreatedAt ?? d.CreatedAt).ToList();
        return Result.Success(sorted);
    }
}



