using Microsoft.Extensions.Options; using SeaHaven.Services.Helpers; using SeaHaven.Services.Implementation; using SeaHaven.Services.Interfaces; using Api.SeaHavenIndustries.Options; namespace Api.SeaHavenIndustries.HostedServices { public class WorkOrderWeekRolledHostedService : BackgroundService { private readonly IServiceScopeFactory _scopeFactory; private readonly ILogger _logger; private readonly WeekRolledJobOptions _options; private readonly WorkOrderJobRunState _runState; private DateOnly? _lastRunDate; public WorkOrderWeekRolledHostedService( IServiceScopeFactory scopeFactory, IOptions options, WorkOrderJobRunState runState, ILogger logger) { _scopeFactory = scopeFactory; _logger = logger; _options = options.Value.WeekRolled; _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 { var sourceWeekStart = WorkOrderOperationalWeek.GetPreviousOperationalWeekStart(DateTime.UtcNow); using var scope = _scopeFactory.CreateScope(); var service = scope.ServiceProvider.GetRequiredService(); await service.ProcessWeekRolledAsync(sourceWeekStart, stoppingToken); _runState.LastWeekRolledRunUtc = DateTime.UtcNow; _runState.LastWeekRolledError = null; return true; } catch (Exception ex) when (ex is not OperationCanceledException) { _runState.LastWeekRolledError = ex.Message; _logger.LogError(ex, "WeekRolled hosted job failed."); return false; } } private bool ShouldRunNow() { var now = DateTime.UtcNow; if (!Enum.TryParse(_options.DayOfWeek, ignoreCase: true, out var targetDay)) targetDay = DayOfWeek.Monday; if (now.DayOfWeek != targetDay) return false; if (!TimeOnly.TryParse(_options.RunAtUtc, out var runAt)) runAt = new TimeOnly(0, 5); return TimeOnly.FromDateTime(now) >= runAt; } } }