using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Options;
using System.Security.Cryptography;
using System.Text.Json;
using TakeoutSaaS.Application.Identity.Abstractions;
using TakeoutSaaS.Application.Identity.Models;
using TakeoutSaaS.Infrastructure.Identity.Options;

namespace TakeoutSaaS.Infrastructure.Identity.Services;

/// <summary>
/// Redis 刷新令牌存储。
/// </summary>
public sealed class RedisRefreshTokenStore(IDistributedCache cache, IOptions<RefreshTokenStoreOptions> options) : IRefreshTokenStore
{
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);
    private readonly RefreshTokenStoreOptions _options = options.Value;

    /// <summary>
    /// 签发刷新令牌并写入缓存。
    /// </summary>
    /// <param name="userId">用户 ID。</param>
    /// <param name="expiresAt">过期时间。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>刷新令牌描述。</returns>
    public async Task<RefreshTokenDescriptor> IssueAsync(long userId, DateTime expiresAt, CancellationToken cancellationToken = default)
    {
        // 1. 生成随机令牌
        var token = Convert.ToBase64String(RandomNumberGenerator.GetBytes(48));
        var descriptor = new RefreshTokenDescriptor(token, userId, expiresAt, false);

        // 2. 写入缓存
        var key = BuildKey(token);
        var entryOptions = new DistributedCacheEntryOptions { AbsoluteExpiration = expiresAt };
        await cache.SetStringAsync(key, JsonSerializer.Serialize(descriptor, JsonOptions), entryOptions, cancellationToken);

        return descriptor;
    }

    /// <summary>
    /// 获取刷新令牌描述。
    /// </summary>
    /// <param name="refreshToken">刷新令牌值。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>刷新令牌描述或 null。</returns>
    public async Task<RefreshTokenDescriptor?> GetAsync(string refreshToken, CancellationToken cancellationToken = default)
    {
        // 1. 读取缓存
        var json = await cache.GetStringAsync(BuildKey(refreshToken), cancellationToken);
        return string.IsNullOrWhiteSpace(json)
            ? null
            : JsonSerializer.Deserialize<RefreshTokenDescriptor>(json, JsonOptions);
    }

    /// <summary>
    /// 吊销刷新令牌。
    /// </summary>
    /// <param name="refreshToken">刷新令牌值。</param>
    /// <param name="cancellationToken">取消标记。</param>
    /// <returns>异步任务。</returns>
    public async Task RevokeAsync(string refreshToken, CancellationToken cancellationToken = default)
    {
        // 1. 读取令牌
        var descriptor = await GetAsync(refreshToken, cancellationToken);
        if (descriptor == null)
        {
            return;
        }

        // 2. 标记吊销并回写缓存
        var updated = descriptor with { Revoked = true };
        var entryOptions = new DistributedCacheEntryOptions { AbsoluteExpiration = updated.ExpiresAt };
        await cache.SetStringAsync(BuildKey(refreshToken), JsonSerializer.Serialize(updated, JsonOptions), entryOptions, cancellationToken);
    }

    private string BuildKey(string token) => $"{_options.Prefix}{token}";
}
