#nullable enable
using System.Collections.Concurrent;
using Baya.Application.Contracts.Payments;

namespace Baya.Infrastructure.CrossCutting.Seams;

/// <summary>
/// In-process mock <see cref="IDistributedLock"/> — a per-key <see cref="SemaphoreSlim"/> so the money-path
/// code runs the same acquire/release shape it will with real Redis, within a single process. It is
/// deliberately <b>not</b> a correctness guarantee across instances: the DB uniques/state-machine are the
/// authoritative backstop. A real StackExchange.Redis lock (lease/expiry, key <c>booking:{id}:payment</c>)
/// replaces this registration only.
/// </summary>
public sealed class InProcessDistributedLock : IDistributedLock
{
    private static readonly ConcurrentDictionary<string, SemaphoreSlim> Gates = new();

    public async ValueTask<IAsyncDisposable> AcquireAsync(string key, CancellationToken cancellationToken = default)
    {
        var gate = Gates.GetOrAdd(key, _ => new SemaphoreSlim(1, 1));
        await gate.WaitAsync(cancellationToken);
        return new Release(gate);
    }

    private sealed class Release(SemaphoreSlim gate) : IAsyncDisposable
    {
        public ValueTask DisposeAsync()
        {
            gate.Release();
            return ValueTask.CompletedTask;
        }
    }
}
