using Knot.Contracts.Conversations.Application.Abstractions;
using Microsoft.Extensions.Caching.Memory;

namespace Knot.Modules.Conversations.Infrastructure.Idempotency;

/// <summary>
/// Реализация хранилища идемпотентности на основе IMemoryCache.
/// Использует семафор для предотвращения race condition при одновременных запросах с одинаковым ключом.
/// </summary>
public sealed class MemoryCacheIdempotencyStore : IIdempotencyStore
{
    private readonly IMemoryCache _cache;
    private readonly SemaphoreSlim _semaphore = new(1, 1);

    public MemoryCacheIdempotencyStore(IMemoryCache cache)
    {
        _cache = cache;
    }

    public async Task<T> GetOrCreateAsync<T>(
        string key,
        Func<CancellationToken, Task<T>> factory,
        TimeSpan? expiration = null,
        CancellationToken cancellationToken = default)
    {
        if (_cache.TryGetValue(key, out T? cachedValue) && cachedValue is not null)
        {
            return cachedValue;
        }

        await _semaphore.WaitAsync(cancellationToken);
        try
        {
            // Double-check после получения блокировки
            if (_cache.TryGetValue(key, out cachedValue) && cachedValue is not null)
            {
                return cachedValue;
            }

            var value = await factory(cancellationToken);

            var options = new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(expiration ?? TimeSpan.FromHours(24))
                .SetPriority(CacheItemPriority.Normal);

            _cache.Set(key, value, options);
            return value;
        }
        finally
        {
            _semaphore.Release();
        }
    }
}
