using JobTrackerApi.Data; using JobTrackerApi.Models; namespace JobTrackerApi.Services; public sealed record SavedJobSearchRunResult( IReadOnlyList Jobs, int NewCount, bool WasInitialRun); public sealed record SavedJobSearchJobResult(DiscoveredJob Job, bool IsNew, bool IsDismissed); public sealed class SavedJobSearchRunner( JobTrackerContext db, IJobDiscoverySearchService discovery, TimeProvider timeProvider) { public async Task RunAsync(SavedJobSearch search, CancellationToken cancellationToken) { var jobs = await discovery.SearchAsync(search.Query, search.Location, cancellationToken); var now = timeProvider.GetUtcNow(); var wasInitialRun = search.LastRunAtUtc is null; var existing = search.Results.ToDictionary(item => item.ExternalJobId, StringComparer.OrdinalIgnoreCase); var results = new List(jobs.Count); var newCount = 0; foreach (var job in jobs) { var isNew = !existing.TryGetValue(job.Id, out var state); if (state is null) { state = new SavedJobSearchResult { ExternalJobId = job.Id, FirstSeenAtUtc = now, LastSeenAtUtc = now }; search.Results.Add(state); db.SavedJobSearchResults.Add(state); newCount++; } else state.LastSeenAtUtc = now; results.Add(new SavedJobSearchJobResult(job, isNew, state.IsDismissed)); } search.LastRunAtUtc = now; search.UpdatedAtUtc = now; return new SavedJobSearchRunResult(results, newCount, wasInitialRun); } }