namespace ZB.MOM.WW.Secrets.Abstractions;

/// <summary>
/// The single definition of the last-writer-wins (LWW) ordering used to reconcile a secret row
/// against a peer's copy: newer <see cref="StoredSecret.UpdatedUtc"/> wins, and
/// <see cref="StoredSecret.Revision"/> breaks a timestamp tie.
/// </summary>
/// <remarks>
/// <para>
/// Every store's <see cref="ISecretStore.ApplyReplicatedAsync"/> and every replication transport
/// reconciler routes its "is the incoming row newer?" decision through here. Keeping one definition
/// is a correctness requirement, not a tidiness one: a cluster can mix stores (a node backed by the
/// shared SQL-Server store alongside nodes on local SQLite), and two implementations that disagree
/// about a tie would converge to <em>different</em> rows on different nodes — a silent split-brain
/// that no single-node test can catch.
/// </para>
/// <para>
/// The comparison is deliberately <b>strict</b>: a row that merely ties the local one is not newer
/// and is ignored. That makes replication idempotent — re-delivering a row a node already has is a
/// no-op rather than a write — so at-least-once transports (pub/sub redelivery, an anti-entropy
/// sweep racing a live broadcast) cost nothing and cannot flap a row back and forth.
/// </para>
/// </remarks>
public static class SecretLastWriterWins
{
    /// <summary>
    /// Returns <see langword="true"/> if a row at (<paramref name="incomingUpdatedUtc"/>,
    /// <paramref name="incomingRevision"/>) should overwrite a local row at
    /// (<paramref name="localUpdatedUtc"/>, <paramref name="localRevision"/>).
    /// </summary>
    /// <param name="incomingUpdatedUtc">The incoming row's last-updated timestamp.</param>
    /// <param name="incomingRevision">The incoming row's revision.</param>
    /// <param name="localUpdatedUtc">The local row's last-updated timestamp.</param>
    /// <param name="localRevision">The local row's revision.</param>
    /// <returns><see langword="true"/> if the incoming row is strictly newer.</returns>
    public static bool IsNewer(
        DateTimeOffset incomingUpdatedUtc,
        long incomingRevision,
        DateTimeOffset localUpdatedUtc,
        long localRevision) =>
        incomingUpdatedUtc > localUpdatedUtc ||
        (incomingUpdatedUtc == localUpdatedUtc && incomingRevision > localRevision);

    /// <summary>
    /// Returns <see langword="true"/> if <paramref name="incoming"/> should overwrite the local row
    /// described by <paramref name="local"/>.
    /// </summary>
    /// <param name="incoming">The manifest entry received from a peer.</param>
    /// <param name="local">The corresponding local manifest entry.</param>
    /// <returns><see langword="true"/> if the incoming entry is strictly newer.</returns>
    public static bool IsNewer(SecretManifestEntry incoming, SecretManifestEntry local)
    {
        ArgumentNullException.ThrowIfNull(incoming);
        ArgumentNullException.ThrowIfNull(local);

        return IsNewer(incoming.UpdatedUtc, incoming.Revision, local.UpdatedUtc, local.Revision);
    }
}
