using adas_core.Application.Services.Interfaces;
using Microsoft.Extensions.Logging;

namespace adas_core.Application.Services.Caching
{
    /// <summary>
    /// Servicio que ejecuta acciones dentro de una sección crítica controlada por lock.
    /// CacheService lo usará para evitar condiciones de carrera en GetOrSet.
    /// Funciona igual para locks locales o distribuidos.
    /// </summary>
    /// <!-- aidoc-review:v1 severity=medium kind=missing_param
    ///      "Constructor parameter 'logger' (ILogger<LockManagerService>) is not documented." -->
    /// <!-- aidoc-review:v1 severity=medium kind=missing_param
    ///      "Constructor parameter 'provider' (ILockProvider) is not documented." -->
    public class LockManagerService(
        ILogger<LockManagerService> logger,
        ILockProvider provider)
    {
        /// <summary>
        /// Ejecuta una función que devuelve un valor bajo un lock por clave.
        /// </summary>
        /// <!-- aidoc-review:v1 severity=medium kind=missing_param
        ///      "Parameter 'key' is not documented" -->
        /// <!-- aidoc-review:v1 severity=medium kind=missing_param
        ///      "Parameter 'timeout' is not documented" -->
        /// <!-- aidoc-review:v1 severity=medium kind=missing_param
        ///      "Parameter 'action' is not documented" -->
        /// <!-- aidoc-review:v1 severity=medium kind=missing_param
        ///      "Parameter 'cancellationToken' is not documented" -->
        /// <!-- aidoc-review:v1 severity=medium kind=missing_returns
        ///      "Method returns Task<T> via action delegate, but no <returns> tag is present" -->
        /// <!-- aidoc-review:v1 severity=medium kind=missing_exception
        ///      "TimeoutException is thrown when the lock cannot be acquired within the timeout, but no <exception> tag is present" -->
        public async Task<T> WithLockAsync<T>(
            string key,
            TimeSpan timeout,
            Func<Task<T>> action,
            CancellationToken cancellationToken = default)
        {
            logger.LogDebug(
                "LockManager — intentando adquirir lock para key={Key} timeout={TimeoutMs}ms",
                key, timeout.TotalMilliseconds);

            var acquired = false;

            try
            {
                acquired = await provider.AcquireAsync(key, timeout).ConfigureAwait(false);

                if (!acquired)
                {
                    logger.LogWarning(
                        "LockManager — timeout al adquirir lock para key={Key} tras {TimeoutMs}ms",
                        key, timeout.TotalMilliseconds);
                    throw new TimeoutException($"No se pudo adquirir el lock para '{key}'");
                }

                logger.LogDebug(
                    "LockManager — lock adquirido correctamente para key={Key}",
                    key);

                return await action().ConfigureAwait(false);
            }
            finally
            {
                if (acquired)
                {
                    await SafeReleaseAsync(key);
                }
            }
        }

        /// <summary>
        /// Version Task (sin valor).
        /// </summary>
        /// <!-- aidoc:v1 sig=3facc4e body=59bc0c3 -->
        public Task WithLockAsync(
            string key,
            TimeSpan timeout,
            Func<Task> action,
            CancellationToken cancellationToken = default)
        {
            return WithLockAsync<object?>(
                key,
                timeout,
                async () =>
                {
                    await action();
                    return null;
                },
                cancellationToken);
        }

        /// <summary>
        /// Libera el lock y registra posibles errores sin interrumpir el flujo.
        /// </summary>
        /// <!-- aidoc:v1 sig=88f2f04 body=608f118 -->
        private async Task SafeReleaseAsync(string key)
        {
            try
            {
                await provider.ReleaseAsync(key).ConfigureAwait(false);
                logger.LogDebug("LockManager — key={Key} lock deleted ", key);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "LockManager —  key={Key} error deleting lock", key);
            }
        }
    }
}