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

/// <summary>
/// Enumeration surface consumed by <see cref="SiteAuditReconciliationActor"/> to
/// discover which sites to poll on each reconciliation tick. Extracted so the
/// actor can be unit-tested against a static list without depending on the
/// production <c>ISiteRepository</c> + EF Core DbContext.
/// </summary>
/// <remarks>
/// The production implementation wraps <c>ISiteRepository.GetAllSitesAsync</c>
/// and projects each <c>Site</c> to a <see cref="SiteEntry"/>. The primary dial
/// target is <c>GrpcNodeAAddress</c> when set, otherwise <c>GrpcNodeBAddress</c>;
/// when both a distinct NodeA and NodeB address exist, NodeB rides along as
/// <see cref="SiteEntry.FallbackGrpcEndpoint"/> so the reconciliation pull can
/// fail over per call. A site is skipped only when BOTH addresses are
/// blank — the reconciliation pull cannot reach it, but absence of any address
/// is a configuration decision, not a runtime error.
/// </remarks>
public interface ISiteEnumerator
{
    /// <summary>
    /// Returns the current set of sites the reconciliation puller should visit
    /// on the next tick. Implementations should reflect adds/removes promptly
    /// — the actor calls this once per tick.
    /// </summary>
    /// <param name="ct">Cancellation token for the async enumeration.</param>
    /// <returns>A task that resolves to the current set of site entries to poll on the next reconciliation tick.</returns>
    Task<IReadOnlyList<SiteEntry>> EnumerateAsync(CancellationToken ct = default);
}

/// <summary>
/// One reconciliation target: the site identifier the actor uses as the
/// cursor key and the gRPC endpoint the pull clients dial to issue the pull.
/// Endpoints are bare authorities (e.g. <c>http://siteA:8083</c>); transport
/// selection (TLS, keepalive, etc.) is the client's concern.
/// </summary>
/// <param name="SiteId">The site identifier used as the reconciliation cursor key.</param>
/// <param name="GrpcEndpoint">The primary gRPC authority to dial (NodeA where configured, else NodeB).</param>
/// <param name="FallbackGrpcEndpoint">
/// Optional secondary gRPC authority (NodeB) dialed once when the primary raises
/// a transport fault during a pull. Null when no distinct second
/// address is configured. Additive — absent value preserves the prior
/// single-endpoint behaviour.
/// </param>
public sealed record SiteEntry(
    string SiteId,
    string GrpcEndpoint,
    string? FallbackGrpcEndpoint = null);
