using ZB.MOM.WW.Audit;

namespace ZB.MOM.WW.MxGateway.Server.Security.Audit;

/// <summary>
/// Best-effort <see cref="IAuditWriter"/> over the MxGateway-owned
/// <see cref="SqliteCanonicalAuditStore"/>. It honours the canonical
/// <see cref="IAuditWriter"/> contract: a failed audit write is swallowed and logged
/// rather than propagated, so it can never abort the user-facing action that produced it.
/// </summary>
/// <remarks>
/// This is the single sink through which ALL MxGateway audit flows — the library admin
/// verbs (via <see cref="CanonicalForwardingApiKeyAuditStore"/>) and the gateway's own
/// dashboard / constraint-denial producers, which write canonical events directly. The
/// best-effort wrapping here also closes the gap that the library's
/// <c>SqliteApiKeyAuditStore.AppendAsync</c> propagated exceptions.
/// </remarks>
public sealed class CanonicalAuditWriter(
    SqliteCanonicalAuditStore store,
    ILogger<CanonicalAuditWriter> logger) : IAuditWriter
{
    /// <inheritdoc />
    public async Task WriteAsync(AuditEvent auditEvent, CancellationToken cancellationToken = default)
    {
        ArgumentNullException.ThrowIfNull(auditEvent);

        try
        {
            await store.InsertAsync(auditEvent, cancellationToken).ConfigureAwait(false);
        }
        catch (Exception exception)
        {
            // Best-effort: a failed audit write must never abort the action that produced it.
            // Swallow everything (including OperationCanceledException) and log for diagnosis.
            logger.LogWarning(
                exception,
                "Failed to persist audit event {EventId} (action {Action}); audit write is best-effort and was suppressed.",
                auditEvent.EventId,
                auditEvent.Action);
        }
    }
}
