using ScadaLink.Commons.Entities.Audit;
using ScadaLink.Commons.Types.Audit;

namespace ScadaLink.Commons.Interfaces.Repositories;

/// <summary>
/// Append-only data access for the central <c>AuditLog</c> table (Audit Log #23).
/// </summary>
/// <remarks>
/// <para>
/// The append-only invariant is enforced both at the SQL level (the
/// <c>scadalink_audit_writer</c> role has only INSERT + SELECT — UPDATE and DELETE
/// are not granted) and at the API level: this interface deliberately exposes no
/// Update and no single-row Delete. Bulk purge is performed exclusively via
/// monthly partition switch-out (<see cref="SwitchOutPartitionAsync"/>).
/// </para>
/// <para>
/// Ingest is idempotent on <c>EventId</c>: <see cref="InsertIfNotExistsAsync"/> is
/// first-write-wins, so retrying telemetry and reconciliation pulls can both feed
/// the same writer without producing duplicates.
/// </para>
/// </remarks>
public interface IAuditLogRepository
{
    /// <summary>
    /// Inserts <paramref name="evt"/> if no row with the same
    /// <see cref="AuditEvent.EventId"/> exists; otherwise silently leaves the
    /// stored row untouched (first-write-wins). Bypasses the EF change tracker
    /// so the row never enters a tracked state.
    /// </summary>
    Task InsertIfNotExistsAsync(AuditEvent evt, CancellationToken ct = default);

    /// <summary>
    /// Returns up to <see cref="AuditLogPaging.PageSize"/> rows matching
    /// <paramref name="filter"/>, ordered by <c>(OccurredAtUtc DESC, EventId DESC)</c>.
    /// Use keyset paging by passing the last returned row's
    /// <c>OccurredAtUtc</c> + <c>EventId</c> back via
    /// <see cref="AuditLogPaging.AfterOccurredAtUtc"/> +
    /// <see cref="AuditLogPaging.AfterEventId"/> to fetch the next page.
    /// </summary>
    Task<IReadOnlyList<AuditEvent>> QueryAsync(
        AuditLogQueryFilter filter,
        AuditLogPaging paging,
        CancellationToken ct = default);

    /// <summary>
    /// Switches out (purges) the monthly partition whose lower bound is
    /// <paramref name="monthBoundary"/>. The honest M1 implementation throws
    /// <see cref="NotSupportedException"/>: the <c>UX_AuditLog_EventId</c> unique
    /// index is non-partition-aligned (lives on <c>[PRIMARY]</c>, not on
    /// <c>ps_AuditLog_Month</c>), so SQL Server rejects
    /// <c>ALTER TABLE … SWITCH PARTITION</c> until the drop-and-rebuild dance
    /// shipped by the M6 purge actor is in place.
    /// </summary>
    Task SwitchOutPartitionAsync(DateTime monthBoundary, CancellationToken ct = default);
}
