using Jiaowu.Api.Domain.System;
using Jiaowu.Api.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;

namespace Jiaowu.Api.Infrastructure.BackgroundJobs;

public sealed record BackgroundJobBacklogSnapshot(
    int Pending,
    int Publishing,
    int Published,
    int Processing,
    int ExpiredLeases,
    DateTime? OldestUnfinishedAt,
    double? OldestUnfinishedAgeSeconds);

public sealed class BackgroundJobMonitoringService(AppDbContext db)
{
    public async Task<BackgroundJobBacklogSnapshot> GetSnapshotAsync(
        CancellationToken cancellationToken)
    {
        var counts = await db.BackgroundJobOutboxMessages.AsNoTracking()
            .Where(x => x.State != BackgroundJobOutboxState.Completed)
            .GroupBy(x => x.State)
            .Select(group => new { State = group.Key, Count = group.Count() })
            .ToListAsync(cancellationToken);
        var countByState = counts.ToDictionary(x => x.State, x => x.Count);
        var now = DateTime.UtcNow;
        var expiredLeases = await db.BackgroundJobOutboxMessages.AsNoTracking()
            .CountAsync(
                x => (x.State == BackgroundJobOutboxState.Publishing ||
                      x.State == BackgroundJobOutboxState.Processing) &&
                     x.LeaseExpiresAt != null &&
                     x.LeaseExpiresAt < now,
                cancellationToken);
        var oldestUnfinishedAt = await db.BackgroundJobOutboxMessages
            .AsNoTracking()
            .Where(x => x.State != BackgroundJobOutboxState.Completed)
            .Select(x => (DateTime?)x.CreatedAt)
            .MinAsync(cancellationToken);
        double? ageSeconds = oldestUnfinishedAt.HasValue
            ? Math.Max(0, (now - oldestUnfinishedAt.Value).TotalSeconds)
            : null;

        return new BackgroundJobBacklogSnapshot(
            GetCount(BackgroundJobOutboxState.Pending),
            GetCount(BackgroundJobOutboxState.Publishing),
            GetCount(BackgroundJobOutboxState.Published),
            GetCount(BackgroundJobOutboxState.Processing),
            expiredLeases,
            oldestUnfinishedAt,
            ageSeconds);

        int GetCount(BackgroundJobOutboxState state) =>
            countByState.GetValueOrDefault(state);
    }
}
