using Microsoft.Extensions.Options; using SeaHaven.Services.Implementation; using SeaHaven.Services.Interfaces; using Api.SeaHavenIndustries.Options; namespace Api.SeaHavenIndustries.HostedServices { public class PastDueCacheHostedService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; private readonly ILogger _logger; private readonly PastDueCacheJobOptions _options; private readonly WorkOrderJobRunState _runState; private DateOnly? _lastRunDate; public PastDueCacheHostedService( IServiceScopeFactory scopeFactory, IOptions options, WorkOrderJobRunState runState, ILogger logger) { _scopeFactory = scopeFactory; _logger = logger; _options = options.Value.PastDueCache; _runState = runState; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { if (_options.Enabled && ShouldRunNow()) { var today = DateOnly.FromDateTime(DateTime.UtcNow); if (_lastRunDate != today) { if (await RunJobAsync(stoppingToken)) _lastRunDate = today; } } await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken); } } private async Task RunJobAsync(CancellationToken stoppingToken) { try { using var scope = _scopeFactory.CreateScope(); var service = scope.ServiceProvider.GetRequiredService(); await service.RefreshAsync(stoppingToken); _runState.LastPastDueCacheRunUtc = DateTime.UtcNow; _runState.LastPastDueCacheError = null; return true; } catch (Exception ex) when (ex is not OperationCanceledException) { _runState.LastPastDueCacheError = ex.Message; _logger.LogError(ex, "PastDue cache hosted job failed."); return false; } } private bool ShouldRunNow() { var now = DateTime.UtcNow; if (!TimeOnly.TryParse(_options.RunAtUtc, out var runAt)) runAt = new TimeOnly(0, 10); return TimeOnly.FromDateTime(now) >= runAt; } } }