using Microsoft.Extensions.Caching.Distributed;
using QRRapidoApp.Models;

namespace QRRapidoApp.Services
{
    /// <summary>
    /// Fixed-window rate limiter backed by IDistributedCache (works with MemoryCache or Redis).
    /// Minor race conditions at window boundaries are acceptable for rate limiting purposes.
    /// </summary>
    public class ApiRateLimitService : IApiRateLimitService
    {
        private readonly IDistributedCache _cache;
        private readonly ILogger<ApiRateLimitService> _logger;

        public ApiRateLimitService(IDistributedCache cache, ILogger<ApiRateLimitService> logger)
        {
            _cache = cache;
            _logger = logger;
        }

        public async Task<RateLimitResult> CheckAndIncrementAsync(string keyPrefix, ApiPlanTier tier)
        {
            var (perMin, perMonth) = ApiPlanLimits.GetLimits(tier);
            var now = DateTime.UtcNow;

            var minuteKey = $"rl:min:{keyPrefix}:{now:yyyyMMddHHmm}";
            var monthKey  = $"rl:mo:{keyPrefix}:{now:yyyyMM}";

            var nextMinute = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, 0, DateTimeKind.Utc)
                .AddMinutes(1);
            var resetTimestamp = ((DateTimeOffset)nextMinute).ToUnixTimeSeconds();

            var minStr = await _cache.GetStringAsync(minuteKey);
            var moStr  = await _cache.GetStringAsync(monthKey);

            int minCount = int.TryParse(minStr, out var mc) ? mc : 0;
            int moCount  = int.TryParse(moStr,  out var mo) ? mo : 0;

            bool monthlyExceeded = false;
            bool allowed = true;

            if (tier != ApiPlanTier.Enterprise)
            {
                if (moCount >= perMonth)
                {
                    monthlyExceeded = true;
                    allowed = false;
                }
                else if (minCount >= perMin)
                {
                    allowed = false;
                }
            }

            if (allowed)
            {
                var minOpts = new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(2)
                };
                var moOpts = new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(33)
                };

                await Task.WhenAll(
                    _cache.SetStringAsync(minuteKey, (minCount + 1).ToString(), minOpts),
                    _cache.SetStringAsync(monthKey,  (moCount  + 1).ToString(), moOpts)
                );
            }

            int reportedMinLimit = perMin == int.MaxValue ? -1 : perMin;
            int reportedMoLimit  = perMonth == int.MaxValue ? -1 : perMonth;
            int usedMin = minCount + (allowed ? 1 : 0);
            int usedMo  = moCount  + (allowed ? 1 : 0);

            return new RateLimitResult(
                Allowed:          allowed,
                PerMinuteLimit:   reportedMinLimit,
                PerMinuteUsed:    usedMin,
                ResetUnixTimestamp: resetTimestamp,
                MonthlyLimit:     reportedMoLimit,
                MonthlyUsed:      usedMo,
                MonthlyExceeded:  monthlyExceeded
            );
        }
    }
}
