using ZB.MOM.WW.ScadaBridge.Commons.Types;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;

namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;

/// <summary>
/// CentralUI facade over <see cref="ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories.IAuditLogRepository"/>.
/// The Audit Log page's results grid talks to this service rather than
/// the repository directly so tests can substitute a fake without spinning up EF
/// Core, and so a future caching / shaping layer (e.g. server-side CSV streaming)
/// can hang off the same seam.
/// </summary>
public interface IAuditLogQueryService
{
    /// <summary>
    /// Returns a keyset-paged result page for <paramref name="filter"/>. When
    /// <paramref name="paging"/> is <c>null</c>, defaults to <see cref="DefaultPageSize"/>
    /// rows with no cursor (first page). The repository orders by
    /// <c>(OccurredAtUtc DESC, EventId DESC)</c>; pass the last row's
    /// <see cref="AuditEventView.OccurredAtUtc"/> + <see cref="AuditEventView.EventId"/>
    /// back as the cursor for the next page.
    /// </summary>
    /// <remarks>
    /// The repository seam returns the canonical
    /// <c>ZB.MOM.WW.Audit.AuditEvent</c>; this facade decomposes each row into a flat
    /// <see cref="AuditEventView"/> so the audit pages keep binding to typed properties.
    /// </remarks>
    /// <param name="filter">Filter criteria applied to the audit log query.</param>
    /// <param name="paging">Optional paging cursor; defaults to first page when null.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task that resolves to a page of audit event views matching the filter.</returns>
    Task<IReadOnlyList<AuditEventView>> QueryAsync(
        AuditLogQueryFilter filter,
        AuditLogPaging? paging = null,
        CancellationToken ct = default);

    /// <summary>Default page size when callers don't specify one.</summary>
    int DefaultPageSize { get; }

    /// <summary>
    /// Returns the point-in-time KPI snapshot
    /// the Health dashboard's Audit tiles render. Composes:
    /// <list type="bullet">
    ///   <item><c>TotalEventsLastHour</c> + <c>ErrorEventsLastHour</c> from
    ///   <see cref="ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories.IAuditLogRepository.GetKpiSnapshotAsync"/>
    ///   (1-hour trailing window).</item>
    ///   <item><c>BacklogTotal</c> from the sum of every site's
    ///   <c>SiteHealthReport.SiteAuditBacklog.PendingCount</c> via
    ///   <see cref="ZB.MOM.WW.ScadaBridge.HealthMonitoring.ICentralHealthAggregator"/>.</item>
    /// </list>
    /// </summary>
    /// <remarks>
    /// Repository + aggregator are read independently; if either source has no
    /// data the corresponding field is zero (a real signal — "no events" vs
    /// "no backlog" — rather than an error). The service does NOT swallow
    /// exceptions; the page wraps the call in a try/catch so a transient DB
    /// outage degrades the tile group to "unavailable" rather than killing the
    /// dashboard.
    /// </remarks>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task that resolves to the current audit KPI snapshot.</returns>
    Task<AuditLogKpiSnapshot> GetKpiSnapshotAsync(CancellationToken ct = default);

    /// <summary>
    /// Audit Log ParentExecutionId feature — returns the full
    /// execution chain containing <paramref name="executionId"/> as a flat list
    /// of <see cref="ExecutionTreeNode"/>, delegating to
    /// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories.IAuditLogRepository.GetExecutionTreeAsync"/>.
    /// The execution-chain tree view (<c>/audit/execution-tree</c>) assembles the
    /// returned flat list into a tree by joining
    /// <see cref="ExecutionTreeNode.ParentExecutionId"/> to a parent node's
    /// <see cref="ExecutionTreeNode.ExecutionId"/>.
    /// </summary>
    /// <remarks>
    /// A pure pass-through, mirroring <see cref="QueryAsync"/> — the production
    /// implementation opens its own DI scope per call so the tree page's
    /// auto-load never contends with the circuit-scoped <c>ScadaBridgeDbContext</c>.
    /// </remarks>
    /// <param name="executionId">Any execution id in the chain to look up.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task that resolves to the flat list of execution tree nodes in the chain.</returns>
    Task<IReadOnlyList<ExecutionTreeNode>> GetExecutionTreeAsync(
        Guid executionId,
        CancellationToken ct = default);

    /// <summary>
    /// Returns the distinct, non-null <c>SourceNode</c> values present in the
    /// central <c>AuditLog</c> table, backing the Audit Log page's Node
    /// multi-select filter. The implementation caches the result for ~60s so
    /// rendering the filter bar never produces more than one DB hit per minute
    /// per circuit. The cache is process-wide — node membership changes
    /// (failover, scaling) surface within a minute, which is acceptable for a
    /// filter affordance.
    /// </summary>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task that resolves to the distinct non-null source node names present in the audit log.</returns>
    Task<IReadOnlyList<string>> GetDistinctSourceNodesAsync(CancellationToken ct = default);
}
