using ZB.MOM.WW.ScadaBridge.Commons.Entities.SecuredWrites;

namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Repositories;

/// <summary>
/// Operational-state data access for the central <c>PendingSecuredWrites</c> table.
/// One row per pending two-person secured
/// write; rows are inserted at submission and mutated as the request is decided and
/// executed. Mirrors the <c>SiteCalls</c> repository shape.
/// </summary>
public interface ISecuredWriteRepository
{
    /// <summary>
    /// Inserts <paramref name="securedWrite"/> and returns the store-generated
    /// <see cref="PendingSecuredWrite.Id"/>.
    /// </summary>
    /// <param name="securedWrite">The pending secured write to persist.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task that resolves to the generated identity of the inserted row.</returns>
    Task<long> AddAsync(PendingSecuredWrite securedWrite, CancellationToken ct = default);

    /// <summary>
    /// Returns the row for the given id, or <c>null</c> if none exists.
    /// </summary>
    /// <param name="id">The identity to look up.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task that resolves to the matching <see cref="PendingSecuredWrite"/>, or <c>null</c> if no row exists.</returns>
    Task<PendingSecuredWrite?> GetAsync(long id, CancellationToken ct = default);

    /// <summary>
    /// Persists the current state of <paramref name="securedWrite"/> (matched by
    /// <see cref="PendingSecuredWrite.Id"/>).
    /// </summary>
    /// <param name="securedWrite">The tracked entity whose changes to persist.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task that represents the asynchronous operation.</returns>
    Task UpdateAsync(PendingSecuredWrite securedWrite, CancellationToken ct = default);

    /// <summary>
    /// Returns up to <paramref name="take"/> rows (skipping <paramref name="skip"/>)
    /// optionally filtered by <paramref name="status"/> and <paramref name="siteId"/>,
    /// ordered by <c>SubmittedAtUtc DESC, Id DESC</c>. A <c>null</c> filter argument
    /// matches every row.
    /// </summary>
    /// <param name="status">Status filter; <c>null</c> matches every status.</param>
    /// <param name="siteId">Site id filter; <c>null</c> matches every site.</param>
    /// <param name="skip">Number of rows to skip (offset paging).</param>
    /// <param name="take">Maximum number of rows to return.</param>
    /// <param name="permittedSiteIds">When non-null, only rows whose <c>SiteId</c> (site
    /// identifier) is in the set match; <c>null</c> matches every site. Applied IN ADDITION
    /// to <paramref name="siteId"/>. Used by the ManagementActor to constrain an unfiltered
    /// list to a site-scoped caller's permitted sites at the query level (arch-review R2 N3),
    /// so paging and totals stay correct.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task that resolves to a page of matching rows, newest submission first.</returns>
    Task<IReadOnlyList<PendingSecuredWrite>> QueryAsync(
        string? status,
        string? siteId,
        int skip,
        int take,
        IReadOnlyCollection<string>? permittedSiteIds = null,
        CancellationToken ct = default);

    /// <summary>
    /// Atomically flips a row from <c>Pending</c> to <c>Approved</c>, stamping the
    /// verifier identity, comment, and decision time, but ONLY if the row is still
    /// <c>Pending</c>. This is the compare-and-swap guard for the two-verifier race:
    /// two verifiers may approve the same write concurrently, but the
    /// conditional <c>WHERE Status='Pending'</c> guarantees exactly one wins. The
    /// loser observes <c>false</c> and must not relay the write.
    /// </summary>
    /// <param name="id">Identity of the pending secured write.</param>
    /// <param name="verifierUser">The approving verifier's username.</param>
    /// <param name="verifierComment">Optional free-text comment from the verifier.</param>
    /// <param name="decidedAtUtc">UTC instant the approval decision was made.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>
    /// A task resolving to <c>true</c> if this caller won the approval (exactly one
    /// row transitioned), or <c>false</c> if the row was no longer <c>Pending</c>
    /// (already decided by another verifier).
    /// </returns>
    Task<bool> TryMarkApprovedAsync(
        long id,
        string verifierUser,
        string? verifierComment,
        DateTime decidedAtUtc,
        CancellationToken ct = default);

    /// <summary>
    /// Atomically flips a row from <c>Pending</c> to <c>Expired</c>, stamping the
    /// expiry as the decision time, but ONLY if the row is still <c>Pending</c>.
    /// This is the multi-node-safe expiry primitive: the conditional
    /// <c>WHERE Status='Pending'</c> makes the Pending-&gt;Expired transition atomic at
    /// the row level, so two nodes sweeping the same overdue write concurrently
    /// produce exactly one winner. The expiry is a system decision — no verifier is
    /// recorded and <see cref="PendingSecuredWrite.VerifierUser"/> stays <c>null</c>.
    /// The loser observes <c>false</c> (the row was already decided or expired).
    /// </summary>
    /// <param name="id">Identity of the pending secured write.</param>
    /// <param name="expiredAtUtc">UTC instant the write was deemed expired.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>
    /// A task resolving to <c>true</c> if this caller won the expiry (exactly one
    /// row transitioned), or <c>false</c> if the row was no longer <c>Pending</c>.
    /// </returns>
    Task<bool> TryMarkExpiredAsync(long id, DateTime expiredAtUtc, CancellationToken ct = default);

    /// <summary>
    /// Returns the number of rows matching the optional <paramref name="status"/> and
    /// <paramref name="siteId"/> filters. A <c>null</c> filter argument matches every
    /// row. Mirrors <see cref="QueryAsync"/>'s filter composition without paging.
    /// </summary>
    /// <param name="status">Status filter; <c>null</c> matches every status.</param>
    /// <param name="siteId">Site id filter; <c>null</c> matches every site.</param>
    /// <param name="permittedSiteIds">When non-null, only rows whose <c>SiteId</c> (site
    /// identifier) is in the set match; <c>null</c> matches every site. Applied IN ADDITION
    /// to <paramref name="siteId"/>. Used by the ManagementActor to constrain an unfiltered
    /// list to a site-scoped caller's permitted sites at the query level (arch-review R2 N3),
    /// so paging and totals stay correct.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A task that resolves to the count of matching rows.</returns>
    Task<int> CountAsync(
        string? status,
        string? siteId,
        IReadOnlyCollection<string>? permittedSiteIds = null,
        CancellationToken ct = default);
}
