using ZB.MOM.WW.ScadaBridge.Commons.Messages.Integration;

namespace ZB.MOM.WW.ScadaBridge.AuditLog.Central;

/// <summary>
/// Mockable abstraction over the central-side <c>PullAuditEvents</c> gRPC
/// client surface that <see cref="SiteAuditReconciliationActor"/> uses to
/// fetch the next reconciliation batch from a specific site. Extracted so the
/// actor can be unit-tested against an in-memory stub without standing up a
/// real <c>GrpcChannel</c> per site.
/// </summary>
/// <remarks>
/// <para>
/// The production implementation (host wiring task) wraps the auto-generated
/// <c>SiteStreamService.SiteStreamServiceClient</c>, multiplexing one
/// <c>GrpcChannel</c> per site keyed on
/// <see cref="SiteEntry.GrpcEndpoint"/>. Until that wiring lands the DI
/// composition root binds a NoOp default that returns an empty response — the
/// reconciliation tick is still scheduled and the cursor logic still runs, so
/// regressions in the actor itself are caught even before the real client
/// arrives.
/// </para>
/// <para>
/// Implementations MUST NOT throw on transport faults that the actor can
/// tolerate (connection refused, deadline exceeded). The actor's contract is
/// "one site's failure doesn't sink the rest of the tick"; an exception still
/// won't crash the actor (the per-site try/catch catches it), but returning
/// an empty response on a known-recoverable error keeps the logs cleaner.
/// </para>
/// </remarks>
public interface IPullAuditEventsClient
{
    /// <summary>
    /// Issues a <c>PullAuditEvents</c> RPC against the site whose endpoint
    /// is registered against <paramref name="siteId"/>. Returns the next
    /// batch of <see cref="ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.AuditEvent"/>
    /// rows ordered oldest-first AND a <c>MoreAvailable</c> flag the actor
    /// uses to decide whether to fire another pull immediately.
    /// </summary>
    /// <param name="siteId">The identifier of the site to pull audit events from.</param>
    /// <param name="sinceUtc">Only events with an <c>OccurredAtUtc</c> at or after this cursor time are returned.</param>
    /// <param name="afterId">
    /// The composite-keyset tiebreak cursor, mirroring
    /// <see cref="IPullSiteCallsClient.PullAsync"/>. When non-null it is the
    /// <c>EventId</c> ("D" GUID form) of the last row already consumed at
    /// <paramref name="sinceUtc"/>; the site returns only rows strictly greater than the
    /// composite <c>(OccurredAtUtc, EventId)</c> pair, so a burst sharing one exact instant
    /// drains via the id tiebreak instead of pinning the inclusive-timestamp cursor — and the
    /// site's <c>MarkReconciledUpToAsync</c> can retire the rows AT that instant, which a bare
    /// timestamp can never prove received. Null on the first pull (or against a legacy site)
    /// preserves the inclusive <c>&gt;=</c> contract.
    /// </param>
    /// <param name="batchSize">Maximum number of events to return per call.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task that resolves to the next reconciliation batch with a <c>MoreAvailable</c> flag.</returns>
    Task<PullAuditEventsResponse> PullAsync(
        string siteId,
        DateTime sinceUtc,
        string? afterId,
        int batchSize,
        CancellationToken ct);
}
