using adas_core.Application.Services.Caching;
using adas_core.Application.Services.Interfaces;
using adas_core.Domain.Models.AppSettings;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace adas_core.Infrastructure.Utils
{
    /// <summary>
    /// Provides extension methods for configuring or building cache host instances.
    /// </summary>
    /// <!-- aidoc:v1 sig=f280f01 -->
    public static class CacheHostBuilderExtension
    {
        /// <summary>
        /// Registers the cache infrastructure on the host, exposing <see cref="ICacheService"/> through a <see cref="CacheDispatcher"/> that can route to either an in-memory or Redis-backed implementation based on <see cref="CacheSettings"/>. <see cref="RedisService"/> is wired lazily so that its <see cref="RedisService.Database"/> is only resolved once the underlying connection has been established asynchronously, while <see cref="CacheService"/> is provided with an <see cref="InMemoryLockProvider"/> and <see cref="NoCacheService"/> is registered as a no-op fallback.
        /// </summary>
        /// <param name="hostBuilder">The <see cref="IHostBuilder"/> to extend with the cache service registrations.</param>
        /// <returns>The same <paramref name="hostBuilder"/> instance, configured with the cache services for fluent chaining.</returns>
        /// <!-- aidoc:v1 sig=06693b5 body=f80dd06 -->
        public static IHostBuilder UseCache(this IHostBuilder hostBuilder)
        {
            return hostBuilder.ConfigureServices((context, services) =>
            {
                services.Configure<CacheSettings>(context.Configuration.GetSection("CacheSettings"));
                services.AddSingleton(sp => sp.GetRequiredService<IOptions<CacheSettings>>().Value);

                services.AddSingleton<InMemoryLockProvider>();
                services.AddSingleton<NoCacheService>();

                // RedisLockProvider obtiene IDatabase de forma lazy a través de una clausura,
                // ya que la conexión se establece de forma asíncrona dentro de RedisService.
                services.AddSingleton<RedisService>(sp => {
                    RedisService? svcRef = null;
                    var lockProvider = new RedisLockProvider(() => svcRef?.Database);
                    var lockMgr = new LockManagerService(
                        sp.GetRequiredService<ILogger<LockManagerService>>(),
                        lockProvider);
                    svcRef = new RedisService(
                        sp.GetRequiredService<IOptions<CacheSettings>>(),
                        sp.GetRequiredService<ILogger<RedisService>>(),
                        lockMgr);
                    return svcRef;
                });

                // Inyectamos LockManager con InMemoryLockProvider
                services.AddSingleton<CacheService>(sp => {
                    var lockMgr = new LockManagerService(
                        sp.GetRequiredService<ILogger<LockManagerService>>(),
                        sp.GetRequiredService<InMemoryLockProvider>());
                    return new CacheService(lockMgr);
                });

                services.AddSingleton<ICacheService, CacheDispatcher>();
            });
        }
    }
}