using Microsoft.Extensions.Options; using SeaHaven.Services.Configuration; using SeaHaven.Services.Interfaces; namespace Api.SeaHavenIndustries.HostedServices { public sealed class WorkOrderReconciliationHostedService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; private readonly IOptionsMonitor _options; private readonly TimeProvider _timeProvider; private readonly ILogger _logger; public WorkOrderReconciliationHostedService( IServiceScopeFactory scopeFactory, IOptionsMonitor options, TimeProvider timeProvider, ILogger logger) { _scopeFactory = scopeFactory; _options = options; _timeProvider = timeProvider; _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { var nextScheduledAt = _timeProvider.GetUtcNow(); if (_options.CurrentValue.Enabled && _options.CurrentValue.RunOnStartup) await TriggerAsync("startup", stoppingToken); while (!stoppingToken.IsCancellationRequested) { var options = _options.CurrentValue; if (options.Enabled) { var now = _timeProvider.GetUtcNow(); if (now >= nextScheduledAt) { await TriggerAsync("schedule", stoppingToken); nextScheduledAt = now.AddMinutes(options.ScheduleMinutes); } await RunPendingAsync(stoppingToken); } await Task.Delay( TimeSpan.FromSeconds(Math.Clamp(options.PollSeconds, 1, 300)), _timeProvider, stoppingToken); } } private async Task TriggerAsync(string reason, CancellationToken cancellationToken) { try { await using var scope = _scopeFactory.CreateAsyncScope(); var runner = scope.ServiceProvider.GetRequiredService(); await runner.TriggerAsync(reason, cancellationToken); } catch (Exception ex) when (ex is not OperationCanceledException) { _logger.LogError(ex, "Could not queue procurement reconciliation."); } } private async Task RunPendingAsync(CancellationToken cancellationToken) { try { await using var scope = _scopeFactory.CreateAsyncScope(); var runner = scope.ServiceProvider.GetRequiredService(); await runner.RunPendingAsync(cancellationToken); } catch (Exception ex) when (ex is not OperationCanceledException) { _logger.LogError(ex, "Could not execute procurement reconciliation."); } } } }