using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using TakeoutSaaS.Application.Identity.Abstractions;
using TakeoutSaaS.Infrastructure.Identity.Options;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;

namespace TakeoutSaaS.Infrastructure.Identity.Services;

/// <summary>
/// Redis 登录限流实现。
/// </summary>
public sealed class RedisLoginRateLimiter(IDistributedCache cache, IOptions<LoginRateLimitOptions> options) : ILoginRateLimiter
{
    private readonly LoginRateLimitOptions _options = options.Value;

    /// <summary>
    /// 校验指定键的登录尝试次数，超限将抛出业务异常。
    /// </summary>
    /// <param name="key">限流键（如账号或 IP）。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>异步任务。</returns>
    public async Task EnsureAllowedAsync(string key, CancellationToken cancellationToken = default)
    {
        // 1. 读取当前计数
        var cacheKey = BuildKey(key);
        var current = await cache.GetStringAsync(cacheKey, cancellationToken);
        var count = string.IsNullOrWhiteSpace(current) ? 0 : int.Parse(current);
        if (count >= _options.MaxAttempts)
        {
            throw new BusinessException(ErrorCodes.Forbidden, "尝试次数过多，请稍后再试");
        }

        // 2. 累加计数并回写缓存
        count++;
        await cache.SetStringAsync(
            cacheKey,
            count.ToString(),
            new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(_options.WindowSeconds)
            },
            cancellationToken);
    }

    /// <summary>
    /// 重置指定键的登录计数。
    /// </summary>
    /// <param name="key">限流键（如账号或 IP）。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>异步任务。</returns>
    public Task ResetAsync(string key, CancellationToken cancellationToken = default)
        => cache.RemoveAsync(BuildKey(key), cancellationToken);

    private static string BuildKey(string key) => $"identity:login:{key}";
}
