using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Domain.Notifications;

namespace SolutionErp.Infrastructure.Persistence.Interceptors;

// Auto-pushes newly created Notification entities via IRealtimeNotifier
// AFTER SaveChanges succeeds. Keeps domain handlers unaware of real-time
// concerns — they just db.Notifications.Add(...) and the interceptor fires.
public class NotificationPushInterceptor(
    IRealtimeNotifier notifier,
    ILogger<NotificationPushInterceptor> logger) : SaveChangesInterceptor
{
    // Captured between Saving* and Saved* — interceptor is scoped with DbContext
    // so this list is per-unit-of-work, not shared across requests.
    private readonly List<Notification> _toPush = new();

    public override InterceptionResult<int> SavingChanges(
        DbContextEventData eventData, InterceptionResult<int> result)
    {
        Capture(eventData.Context);
        return base.SavingChanges(eventData, result);
    }

    public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
        DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
    {
        Capture(eventData.Context);
        return base.SavingChangesAsync(eventData, result, cancellationToken);
    }

    public override int SavedChanges(SaveChangesCompletedEventData eventData, int result)
    {
        _ = FlushAsync(CancellationToken.None);
        return base.SavedChanges(eventData, result);
    }

    public override async ValueTask<int> SavedChangesAsync(
        SaveChangesCompletedEventData eventData, int result, CancellationToken cancellationToken = default)
    {
        await FlushAsync(cancellationToken);
        return await base.SavedChangesAsync(eventData, result, cancellationToken);
    }

    private void Capture(DbContext? context)
    {
        if (context is null) return;
        _toPush.Clear();
        foreach (var entry in context.ChangeTracker.Entries<Notification>())
        {
            if (entry.State == EntityState.Added)
                _toPush.Add(entry.Entity);
        }
    }

    private async Task FlushAsync(CancellationToken ct)
    {
        if (_toPush.Count == 0) return;
        var batch = _toPush.ToList();
        _toPush.Clear();

        foreach (var n in batch)
        {
            try
            {
                // Same shape as NotificationDto on FE — keeps the client simple
                var payload = new
                {
                    id = n.Id,
                    type = (int)n.Type,
                    title = n.Title,
                    description = n.Description,
                    href = n.Href,
                    refId = n.RefId,
                    createdAt = n.CreatedAt,
                    readAt = (DateTime?)null,
                };
                await notifier.PushToUserAsync(n.UserId, "notification-created", payload, ct);
            }
            catch (Exception ex)
            {
                // Never propagate — real-time push is best-effort.
                logger.LogWarning(ex, "Failed to push realtime notification {NotificationId}", n.Id);
            }
        }
    }
}
