namespace Campaign_Tracker.Server.ExtensionData; /// /// Implements by scanning every record supplied /// by the registered implementations and /// validating each one's legacy link through (AC #4). /// When no providers are registered (i.e. no extension records exist yet), the report /// reflects zero records and 100% consistency. /// public sealed class LegacyLinkIntegrityService : ILegacyLinkIntegrityCheck { private readonly IEnumerable _providers; private readonly ILegacyLinkValidator _validator; private readonly TimeProvider _timeProvider; public LegacyLinkIntegrityService( IEnumerable providers, ILegacyLinkValidator validator, TimeProvider timeProvider) { _providers = providers; _validator = validator; _timeProvider = timeProvider; } public async Task CheckAsync(CancellationToken cancellationToken = default) { var failures = new List(); var seenLinks = new Dictionary(StringComparer.OrdinalIgnoreCase); var providers = _providers.ToArray(); int total = 0; foreach (var provider in providers) { var records = await provider.GetAllAsync(cancellationToken); foreach (var record in records) { total++; var result = await _validator.ValidateAsync(record.LegacyLink, cancellationToken); if (!result.IsValid) { failures.Add(new LegacyLinkIntegrityFailure( record.RecordType, record.RecordId, record.LegacyLink, result.Error ?? "Unknown validation error")); } var linkKey = $"{record.LegacyLink.Type}:{record.LegacyLink.Value}"; if (seenLinks.TryGetValue(linkKey, out var existing) && (!string.Equals(existing.RecordType, record.RecordType, StringComparison.OrdinalIgnoreCase) || !string.Equals(existing.RecordId, record.RecordId, StringComparison.OrdinalIgnoreCase))) { failures.Add(new LegacyLinkIntegrityFailure( record.RecordType, record.RecordId, record.LegacyLink, $"Legacy reference is also linked by {existing.RecordType} '{existing.RecordId}', creating an ambiguous active-record join.")); } else { seenLinks[linkKey] = record; } } } int consistent = total - failures.Count; double pct = total == 0 ? 100.0 : (consistent / (double)total) * 100.0; return new LegacyLinkIntegrityReport( CheckedAt: _timeProvider.GetUtcNow(), ProviderCount: providers.Length, TotalRecords: total, ConsistentRecords: consistent, FailedRecords: failures.Count, ConsistencyPercentage: Math.Round(pct, 4), Failures: failures); } }