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 MongoDB.Driver;

namespace Knot.Modules.Messaging.Infrastructure.Persistence;

public sealed class MessageQueryService : IMessageQueryService
{
    private readonly IMongoCollection<Message> _messages;

    public MessageQueryService(IMongoDatabase database)
    {
        _messages = database.GetCollection<Message>("messages");
    }

    public async Task<List<MessageInfo>> GetAllMessagesAsync(CancellationToken cancellationToken)
    {
        var messages = await _messages.Find(_ => true).ToListAsync(cancellationToken);
        return messages.Select(m => new MessageInfo(
            m.Id,
            m.ChatId,
            m.State.HasFlag(MessageState.IsDeleted),
            m is MediaMessage mm && mm.Media.Any() ? mm.Media.First().Url : null,
            m is MediaMessage mm2 && mm2.Media.Any() ? mm2.Media.Select(media => new Contracts.Messaging.Application.Abstractions.MediaInfo(media.Url)).ToList() : null
        )).ToList();
    }

    public async Task<List<MessageInfo>> GetOrphanedMessagesAsync(HashSet<Guid> activeChatIds, CancellationToken cancellationToken)
    {
        if (activeChatIds == null || activeChatIds.Count == 0)
        {
            var allMessages = await _messages.Find(_ => true).ToListAsync(cancellationToken);
            return allMessages.Select(m => new MessageInfo(
                m.Id,
                m.ChatId,
                m.State.HasFlag(MessageState.IsDeleted),
                m is MediaMessage mm && mm.Media.Any() ? mm.Media.First().Url : null,
                m is MediaMessage mm2 && mm2.Media.Any() ? mm2.Media.Select(media => new Contracts.Messaging.Application.Abstractions.MediaInfo(media.Url)).ToList() : null
            )).ToList();
        }

        var builder = Builders<Message>.Filter;
        var inFilter = builder.In(m => m.ChatId, activeChatIds);
        var filter = builder.Not(inFilter);
        var messages = await _messages.Find(filter).ToListAsync(cancellationToken);
        return messages.Select(m => new MessageInfo(
            m.Id,
            m.ChatId,
            m.State.HasFlag(MessageState.IsDeleted),
            m is MediaMessage mm && mm.Media.Any() ? mm.Media.First().Url : null,
            m is MediaMessage mm2 && mm2.Media.Any() ? mm2.Media.Select(media => new Contracts.Messaging.Application.Abstractions.MediaInfo(media.Url)).ToList() : null
        )).ToList();
    }

    public async Task DeleteMessagesAsync(List<Guid> messageIds, CancellationToken cancellationToken)
    {
        var filter = Builders<Message>.Filter.In(m => m.Id, messageIds);
        await _messages.DeleteManyAsync(filter, cancellationToken);
    }
}
