using InboxIntel.Application.Abstractions;
using InboxIntel.Infrastructure.Configuration;
using InboxIntel.Infrastructure.Persistence;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace InboxIntel.Infrastructure.Notifications;
///
/// Hourly tick that sends the inbox digest to opted-in users once their
/// configured frequency has elapsed. Mirrors .
///
public class DigestWorker : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly DigestOptions _options;
private readonly ILogger _logger;
public DigestWorker(IServiceScopeFactory scopeFactory, IOptions options, ILogger logger)
{
_scopeFactory = scopeFactory;
_options = options.Value;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("DigestWorker started; send hour = {Hour}:00 UTC, frequency = {Days}d", _options.SendHourUtc, _options.FrequencyDays);
while (!stoppingToken.IsCancellationRequested)
{
try
{
if (DateTimeOffset.UtcNow.Hour == _options.SendHourUtc)
await SendDueDigestsAsync(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "DigestWorker tick failed");
}
await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
}
}
private async Task SendDueDigestsAsync(CancellationToken ct)
{
using var scope = _scopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService();
var digest = scope.ServiceProvider.GetRequiredService();
var emailSender = scope.ServiceProvider.GetRequiredService();
if (!emailSender.IsEnabled) return;
var cutoff = DateTimeOffset.UtcNow.AddDays(-_options.FrequencyDays);
var dueUserIds = await db.Users
.Where(u => u.DigestEnabled && (u.LastDigestSentUtc == null || u.LastDigestSentUtc <= cutoff))
.Select(u => u.Id)
.ToListAsync(ct);
foreach (var userId in dueUserIds)
{
try { await digest.SendDigestAsync(userId, ct); }
catch (Exception ex) { _logger.LogError(ex, "Digest send failed for user {UserId}", userId); }
}
}
}