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);
}
}