using Microsoft.EntityFrameworkCore;
using ZB.MOM.WW.ScadaBridge.Commons.Entities.Notifications;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums;
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase;
using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Repositories;

namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests;

// Coverage for per-site KPI aggregation in the Notification Outbox repository
// (Task 2 of the notifications-nav-group feature).
public class NotificationOutboxRepositoryPerSiteKpiTests
{
    private static ScadaBridgeDbContext NewContext() => SqliteTestHelper.CreateInMemoryContext();

    private static Notification NewNotification(
        string sourceSiteId,
        NotificationStatus status,
        DateTimeOffset createdAt,
        DateTimeOffset? deliveredAt = null)
    {
        return new Notification(
            Guid.NewGuid().ToString(), NotificationType.Email, "Ops List", "Subject", "Body", sourceSiteId)
        {
            Status = status,
            CreatedAt = createdAt,
            DeliveredAt = deliveredAt,
        };
    }

    [Fact]
    public async Task ComputePerSiteKpisAsync_AggregatesMetricsPerSite()
    {
        await using var ctx = NewContext();
        var now = DateTimeOffset.UtcNow;

        // plant-a: 1 pending (stuck, created 20m ago), 1 parked
        ctx.Notifications.Add(NewNotification("plant-a", NotificationStatus.Pending, createdAt: now.AddMinutes(-20)));
        ctx.Notifications.Add(NewNotification("plant-a", NotificationStatus.Parked, createdAt: now.AddMinutes(-5)));
        // plant-b: 1 delivered in-window, 1 pending (fresh)
        ctx.Notifications.Add(NewNotification("plant-b", NotificationStatus.Delivered, createdAt: now.AddHours(-2), deliveredAt: now.AddMinutes(-2)));
        ctx.Notifications.Add(NewNotification("plant-b", NotificationStatus.Pending, createdAt: now.AddMinutes(-1)));
        // plant-c: 2 non-terminal rows of clearly different ages — pending 90m ago,
        // retrying 40m ago. Both predate the 10m stuck cutoff. Exercises the
        // in-memory g.Min(CreatedAt) oldest-age reduction and the Retrying branch
        // of the QueueDepth/StuckCount predicates.
        ctx.Notifications.Add(NewNotification("plant-c", NotificationStatus.Pending, createdAt: now.AddMinutes(-90)));
        ctx.Notifications.Add(NewNotification("plant-c", NotificationStatus.Retrying, createdAt: now.AddMinutes(-40)));
        await ctx.SaveChangesAsync();

        var repo = new NotificationOutboxRepository(ctx);
        var result = await repo.ComputePerSiteKpisAsync(
            stuckCutoff: now.AddMinutes(-10), deliveredSince: now.AddMinutes(-30));

        var a = result.Single(s => s.SourceSiteId == "plant-a");
        Assert.Equal(1, a.QueueDepth);
        Assert.Equal(1, a.StuckCount);
        Assert.Equal(1, a.ParkedCount);
        Assert.Equal(0, a.DeliveredLastInterval);
        Assert.NotNull(a.OldestPendingAge);

        var b = result.Single(s => s.SourceSiteId == "plant-b");
        Assert.Equal(1, b.QueueDepth);
        Assert.Equal(0, b.StuckCount);
        Assert.Equal(1, b.DeliveredLastInterval);

        // plant-c: both the Pending and Retrying rows count toward QueueDepth;
        // both predate the stuck cutoff so both are stuck. OldestPendingAge must
        // reflect the older (90m) row, not the 10m Retrying one.
        var c = result.Single(s => s.SourceSiteId == "plant-c");
        Assert.Equal(2, c.QueueDepth);
        Assert.Equal(2, c.StuckCount);
        Assert.Equal(0, c.ParkedCount);
        Assert.NotNull(c.OldestPendingAge);
        // Tolerant lower bound to absorb clock skew between seed time and the
        // `now` captured inside ComputePerSiteKpisAsync.
        Assert.True(c.OldestPendingAge >= TimeSpan.FromMinutes(85),
            $"expected OldestPendingAge >= 85m, got {c.OldestPendingAge}");
        Assert.True(c.OldestPendingAge < TimeSpan.FromMinutes(95),
            $"expected OldestPendingAge < 95m, got {c.OldestPendingAge}");
    }

    [Fact]
    public async Task ComputePerSiteKpisAsync_ReturnsEmpty_WhenNoNotifications()
    {
        await using var ctx = NewContext();
        var repo = new NotificationOutboxRepository(ctx);
        var result = await repo.ComputePerSiteKpisAsync(
            DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddMinutes(-30));
        Assert.Empty(result);
    }
}
