using System.Collections.Concurrent;
using adas_core.Application.Services.Interfaces;

namespace adas_core.Application.Services.Caching
{
    /// <summary>
    /// Implementación local del sistema de locking por clave.
    /// Se basa en SemaphoreSlim y solo controla concurrencia DENTRO del proceso.
    /// Para CacheService (in-memory).
    /// </summary>
    public class InMemoryLockProvider : ILockProvider
    {
        private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new(StringComparer.Ordinal);

        /// <summary>
        /// Crea u obtiene un semáforo asociado a la clave.
        /// </summary>
        private SemaphoreSlim GetOrCreate(string key)
        {
            return _locks.GetOrAdd(key, _ => new SemaphoreSlim(1, 1));
        }

        /// <summary>
        /// Intenta adquirir el lock por clave.
        /// </summary>
        public async Task<bool> AcquireAsync(string key, TimeSpan timeout)
        {
            var sem = GetOrCreate(key);

            try
            {
                return await sem.WaitAsync(timeout).ConfigureAwait(false);
            }
            catch (ObjectDisposedException)
            {
                // Si el semáforo se eliminó entre medio, creamos uno nuevo.
                _locks.TryRemove(key, out _);
                return await GetOrCreate(key).WaitAsync(timeout).ConfigureAwait(false);
            }
        }

        /// <summary>
        /// Libera el lock (si existe y no está ya liberado).
        /// </summary>
        public Task ReleaseAsync(string key)
        {
            if (!_locks.TryGetValue(key, out var sem)) 
                return Task.CompletedTask;
            
            try
            {
                sem.Release();
            }
            catch (SemaphoreFullException)
            {
                // Idempotencia: ignoramos exceso de releases
            }

            return Task.CompletedTask;
        }
    }
}