using ScadaLink.Commons.Entities.Audit;
using ScadaLink.Commons.Interfaces.Repositories;
using ScadaLink.Commons.Types;
using ScadaLink.Commons.Types.Audit;
using ScadaLink.HealthMonitoring;
namespace ScadaLink.CentralUI.Services;
///
/// Default implementation โ a thin pass-through
/// to . Default page size is 100 (the
/// AuditResultsGrid default for #23 M7).
///
public sealed class AuditLogQueryService : IAuditLogQueryService
{
// M7 Bundle E (T13): trailing window for the Health dashboard's Audit KPI tiles.
// Hard-coded here rather than configurable because the requirement
// (Component-AuditLog.md ยง"Health & KPIs") fixes "rows/min over the last hour"
// and "% errors over the last hour" as the KPI definition.
private static readonly TimeSpan KpiWindow = TimeSpan.FromHours(1);
private readonly IAuditLogRepository _repository;
private readonly ICentralHealthAggregator _healthAggregator;
public AuditLogQueryService(
IAuditLogRepository repository,
ICentralHealthAggregator healthAggregator)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_healthAggregator = healthAggregator ?? throw new ArgumentNullException(nameof(healthAggregator));
}
public int DefaultPageSize => 100;
public Task> QueryAsync(
AuditLogQueryFilter filter,
AuditLogPaging? paging = null,
CancellationToken ct = default)
{
ArgumentNullException.ThrowIfNull(filter);
var effective = paging ?? new AuditLogPaging(DefaultPageSize);
return _repository.QueryAsync(filter, effective, ct);
}
///
public async Task GetKpiSnapshotAsync(CancellationToken ct = default)
{
// 1. Volume + error counts: aggregate over the trailing 1h window.
// BacklogTotal is left at 0 by the repository โ we fill it from the
// in-memory health aggregator below.
var repoSnapshot = await _repository.GetKpiSnapshotAsync(KpiWindow, nowUtc: null, ct);
// 2. Backlog: sum PendingCount across every site's latest report.
// Sites that have not yet reported or whose reporter is disabled
// leave SiteAuditBacklog null โ those contribute zero (a Missing
// snapshot is "unknown", not "zero", but the tile is best-effort).
long backlog = 0;
foreach (var state in _healthAggregator.GetAllSiteStates().Values)
{
var pending = state.LatestReport?.SiteAuditBacklog?.PendingCount;
if (pending is > 0)
{
backlog += pending.Value;
}
}
return repoSnapshot with { BacklogTotal = backlog };
}
}