using GestionIntegral.Api.Data.Repositories.Contables;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;

namespace GestionIntegral.Api.Services.Contables
{
    public class PeriodoCerradoValidator : IPeriodoCerradoValidator
    {
        private readonly IServiceScopeFactory _scopeFactory;
        private readonly IMemoryCache _cache;
        private readonly ILogger<PeriodoCerradoValidator> _logger;

        // TTL del cache: ventana stale aceptada por contrato (los cierres son operación rara).
        private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(10);

        // Singleton consumiendo Scoped (ICierreCuentaCorrienteRepository) → resolver vía IServiceScopeFactory cada lookup.
        public PeriodoCerradoValidator(
            IServiceScopeFactory scopeFactory,
            IMemoryCache cache,
            ILogger<PeriodoCerradoValidator> logger)
        {
            _scopeFactory = scopeFactory;
            _cache = cache;
            _logger = logger;
        }

        public async Task<PeriodoCerradoResult> EstaCerradoAsync(string destino, int idDestino, int idEmpresa, DateTime fechaOperacion)
        {
            // Sólo aplica a Distribuidores. Canillas no tiene cierre por (Canilla + Empresa).
            if (!string.Equals(destino, "Distribuidores", StringComparison.OrdinalIgnoreCase))
                return new PeriodoCerradoResult(false, null, null);

            var key = CacheKey(idDestino, idEmpresa);

            var ultimo = await _cache.GetOrCreateAsync(key, async entry =>
            {
                entry.AbsoluteExpirationRelativeToNow = CacheTtl;

                using var scope = _scopeFactory.CreateScope();
                var repo = scope.ServiceProvider.GetRequiredService<ICierreCuentaCorrienteRepository>();
                var cierre = await repo.GetUltimoCierreVigenteAsync(idDestino, idEmpresa);

                // Cacheamos sólo lo necesario para que la decisión sea rápida.
                return cierre == null ? null : new CachedUltimoCierre(cierre.IdCierre, cierre.FechaCorte);
            });

            if (ultimo == null)
                return new PeriodoCerradoResult(false, null, null);

            // FechaCorte es DATE; comparar en .Date para ignorar componente hora.
            if (ultimo.FechaCorte.Date >= fechaOperacion.Date)
                return new PeriodoCerradoResult(true, ultimo.IdCierre, ultimo.FechaCorte);

            return new PeriodoCerradoResult(false, null, null);
        }

        public void InvalidarCache(int idDistribuidor, int idEmpresa)
        {
            _cache.Remove(CacheKey(idDistribuidor, idEmpresa));
        }

        private static string CacheKey(int idDestino, int idEmpresa) => $"cierre-ultimo:{idDestino}:{idEmpresa}";

        private sealed record CachedUltimoCierre(int IdCierre, DateTime FechaCorte);
    }
}
