using Ikar.Server.Data;
using Ikar.Server.Data.Entities;
using Ikar.Server.Infrastructure;
using Ikar.Shared;
using Microsoft.EntityFrameworkCore;

namespace Ikar.Server.Infrastructure.Bots;

public sealed class BotUpdateService(IkarDbContext dbContext)
{
    public async Task EnqueueIncomingMessageAsync(Guid messageId, CancellationToken cancellationToken = default)
    {
        var message = await dbContext.Messages
            .Include(x => x.Sender)
            .Include(x => x.Chat)
                .ThenInclude(x => x.Members)
                    .ThenInclude(x => x.User)
                        .ThenInclude(x => x.BotProfile)
            .SingleOrDefaultAsync(x => x.Id == messageId, cancellationToken);

        if (message is null ||
            message.Sender.IsBot)
        {
            return;
        }

        var eligibleBotMembers = message.Chat.Members
            .Where(member =>
                member.UserId != message.SenderId &&
                member.User.IsBot &&
                member.User.BotProfile is not null &&
                member.User.BotProfile.IsEnabled &&
                CanReceiveUpdates(message.Chat.Type, member))
            .ToList();
        if (eligibleBotMembers.Count == 0)
        {
            return;
        }

        var botIds = eligibleBotMembers
            .Select(member => member.User.BotProfile!.Id)
            .ToList();
        var existingBotIds = await dbContext.BotUpdates
            .AsNoTracking()
            .Where(
                x => botIds.Contains(x.BotId) &&
                     x.MessageId == message.Id)
            .Select(x => x.BotId)
            .ToListAsync(cancellationToken);
        var existingBotIdSet = existingBotIds.ToHashSet();

        foreach (var botMember in eligibleBotMembers)
        {
            var bot = botMember.User.BotProfile!;
            if (!existingBotIdSet.Add(bot.Id))
            {
                continue;
            }

            dbContext.BotUpdates.Add(new BotUpdate
            {
                BotId = bot.Id,
                ChatId = message.ChatId,
                MessageId = message.Id,
                CreatedAt = message.SentAt
            });
        }

        await dbContext.SaveChangesAsync(cancellationToken);
    }

    private static bool CanReceiveUpdates(ChatType chatType, ChatMember member)
    {
        if (chatType is ChatType.Direct or ChatType.Group)
        {
            return true;
        }

        return DtoMapper.BuildPermissions(member, chatType) is { CanManageMembers: true } or { CanPostMessages: true };
    }

    public async Task MarkMessagesReadByBotAsync(
        Guid botUserId,
        IReadOnlyCollection<Message> messages,
        CancellationToken cancellationToken = default)
    {
        var maxByChat = messages
            .GroupBy(message => message.ChatId)
            .Select(group => new
            {
                ChatId = group.Key,
                MaxSentAt = group.Max(message => message.SentAt)
            })
            .ToList();

        foreach (var item in maxByChat)
        {
            var membership = await dbContext.ChatMembers
                .SingleOrDefaultAsync(
                    x => x.ChatId == item.ChatId && x.UserId == botUserId,
                    cancellationToken);
            if (membership is null)
            {
                continue;
            }

            if (membership.LastDeliveredAt is null || membership.LastDeliveredAt < item.MaxSentAt)
            {
                membership.LastDeliveredAt = item.MaxSentAt;
            }

            if (membership.LastReadAt is null || membership.LastReadAt < item.MaxSentAt)
            {
                membership.LastReadAt = item.MaxSentAt;
            }
        }

        await dbContext.SaveChangesAsync(cancellationToken);
    }
}
