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

namespace Ikar.Server.Infrastructure.Push;

public sealed class PushNotificationDispatcher(
    IkarDbContext dbContext,
    IPushNotificationSender pushNotificationSender,
    ILogger<PushNotificationDispatcher> logger)
{
    public async Task DispatchMessageAsync(Chat chat, Message message, CancellationToken cancellationToken = default)
    {
        var now = DateTimeOffset.UtcNow;
        var recipientIds = chat.Members
            .Where(x => x.UserId != message.SenderId && !IsMuted(x, now))
            .Select(x => x.UserId)
            .Distinct()
            .ToList();

        if (recipientIds.Count == 0)
        {
            logger.LogDebug(
                "Skipping push dispatch for message {MessageId} because chat {ChatId} has no recipients.",
                message.Id,
                chat.Id);
            return;
        }

        var devices = await dbContext.PushDevices
            .Include(x => x.User)
                .ThenInclude(x => x.PrivacySettings)
            .Where(x => recipientIds.Contains(x.UserId) && x.NotificationsEnabled)
            .ToListAsync(cancellationToken);

        if (devices.Count == 0)
        {
            logger.LogDebug(
                "Skipping push dispatch for message {MessageId} because {RecipientCount} recipient(s) have no enabled push devices.",
                message.Id,
                recipientIds.Count);
            return;
        }

        try
        {
            var previewDevices = devices
                .Where(device => device.User.PrivacySettings?.ShowMessagePreview != false)
                .ToList();
            if (previewDevices.Count > 0)
            {
                await pushNotificationSender.SendMessageAsync(chat, message, previewDevices, cancellationToken);
            }

            var hiddenPreviewDevices = devices
                .Where(device => device.User.PrivacySettings?.ShowMessagePreview == false)
                .ToList();
            if (hiddenPreviewDevices.Count > 0)
            {
                await pushNotificationSender.SendMessageAsync(
                    chat,
                    BuildHiddenPreviewMessage(message),
                    hiddenPreviewDevices,
                    cancellationToken);
            }
        }
        catch (Exception ex)
        {
            logger.LogWarning(ex, "Push dispatch failed for message {MessageId}.", message.Id);
            throw;
        }
    }

    public async Task DispatchMessageReactionAsync(
        Chat chat,
        Message message,
        User reactor,
        string emoji,
        CancellationToken cancellationToken = default)
    {
        if (message.SenderId == reactor.Id || string.IsNullOrWhiteSpace(emoji))
        {
            logger.LogDebug(
                "Skipping reaction push for message {MessageId}: self reaction or empty emoji.",
                message.Id);
            return;
        }

        var authorMembership = chat.Members.FirstOrDefault(x => x.UserId == message.SenderId);
        if (authorMembership is null || IsMuted(authorMembership, DateTimeOffset.UtcNow))
        {
            logger.LogDebug(
                "Skipping reaction push for message {MessageId} because recipient muted chat {ChatId} or is no longer a member.",
                message.Id,
                chat.Id);
            return;
        }

        var devices = await dbContext.PushDevices
            .Where(x => x.UserId == message.SenderId && x.NotificationsEnabled)
            .ToListAsync(cancellationToken);

        if (devices.Count == 0)
        {
            logger.LogDebug(
                "Skipping reaction push for message {MessageId} because recipient has no enabled push devices.",
                message.Id);
            return;
        }

        try
        {
            await pushNotificationSender.SendMessageReactionAsync(
                chat,
                message,
                reactor,
                emoji,
                devices,
                cancellationToken);
        }
        catch (Exception ex)
        {
            logger.LogWarning(
                ex,
                "Push dispatch failed for reaction to message {MessageId}.",
                message.Id);
            throw;
        }
    }

    private static bool IsMuted(ChatMember member, DateTimeOffset now) =>
        member.MutedUntil is not null && member.MutedUntil > now;

    private static Message BuildHiddenPreviewMessage(Message source)
    {
        var sender = new User
        {
            Id = source.Sender.Id,
            Username = source.Sender.Username,
            DisplayName = source.Sender.DisplayName,
            PhoneNumber = null,
            IsBot = source.Sender.IsBot
        };

        return new Message
        {
            Id = source.Id,
            ChatId = source.ChatId,
            Chat = source.Chat,
            SenderId = source.SenderId,
            Sender = sender,
            Text = "Новое сообщение",
            SentAt = source.SentAt,
            PostedAsChannel = source.PostedAsChannel,
            AuthorSignature = source.AuthorSignature,
            DeletedAt = source.DeletedAt
        };
    }
}
