using ZB.MOM.WW.Secrets.Abstractions;

namespace ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.Tests.Fakes;

/// <summary>
/// Wraps a store so every operation genuinely yields the thread before completing.
/// </summary>
/// <remarks>
/// <para>
/// This exists to close a blind spot that let a real bug through. A local SQLite store is fast
/// enough that <c>await</c> on it frequently completes <b>synchronously</b>, which leaves the
/// caller on the actor's own mailbox thread — and therefore leaves the actor context (a
/// <c>[ThreadStatic]</c>) intact. Code that illegally touches actor context after an <c>await</c>
/// then works by accident, and the whole suite passes.
/// </para>
/// <para>
/// Under a slower store, a contended one, or simply a differently-timed thread pool, the same
/// <c>await</c> goes properly asynchronous, the continuation lands on a pool thread with no actor
/// context, and the code throws. That is the worst shape of bug: green in tests, broken in
/// production, timing-dependent either way. Forcing the async path makes it deterministic.
/// </para>
/// </remarks>
/// <param name="inner">The store to wrap.</param>
public sealed class GenuinelyAsyncSecretStore(ISecretStore inner) : ISecretStore
{
    private static async Task YieldRealAsync()
    {
        // Task.Yield alone can still be scheduled inline by some schedulers; a real (if tiny) delay
        // guarantees the continuation is posted to the thread pool.
        await Task.Delay(5).ConfigureAwait(false);
        await Task.Yield();
    }

    /// <inheritdoc />
    public async Task<StoredSecret?> GetAsync(SecretName name, CancellationToken ct)
    {
        await YieldRealAsync().ConfigureAwait(false);
        return await inner.GetAsync(name, ct).ConfigureAwait(false);
    }

    /// <inheritdoc />
    public async Task UpsertAsync(StoredSecret row, CancellationToken ct)
    {
        await YieldRealAsync().ConfigureAwait(false);
        await inner.UpsertAsync(row, ct).ConfigureAwait(false);
    }

    /// <inheritdoc />
    public async Task<bool> DeleteAsync(SecretName name, string? actor, CancellationToken ct)
    {
        await YieldRealAsync().ConfigureAwait(false);
        return await inner.DeleteAsync(name, actor, ct).ConfigureAwait(false);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<SecretMetadata>> ListAsync(bool includeDeleted, CancellationToken ct)
    {
        await YieldRealAsync().ConfigureAwait(false);
        return await inner.ListAsync(includeDeleted, ct).ConfigureAwait(false);
    }

    /// <inheritdoc />
    public async Task<IReadOnlyList<SecretManifestEntry>> GetManifestAsync(CancellationToken ct)
    {
        await YieldRealAsync().ConfigureAwait(false);
        return await inner.GetManifestAsync(ct).ConfigureAwait(false);
    }

    /// <inheritdoc />
    public async Task ApplyReplicatedAsync(StoredSecret row, CancellationToken ct)
    {
        await YieldRealAsync().ConfigureAwait(false);
        await inner.ApplyReplicatedAsync(row, ct).ConfigureAwait(false);
    }

    /// <inheritdoc />
    public async Task<bool> ApplyRewrapAsync(
        StoredSecret rewrappedRow, byte[] expectedCurrentWrappedDek, CancellationToken ct)
    {
        await YieldRealAsync().ConfigureAwait(false);
        return await inner.ApplyRewrapAsync(rewrappedRow, expectedCurrentWrappedDek, ct)
            .ConfigureAwait(false);
    }
}
