using Microsoft.Extensions.Caching.Distributed;
using System.Text.Json;
using StackExchange.Redis;

namespace TakeoutSaaS.Infrastructure.Dictionary.Caching;

/// <summary>
/// Redis 缓存访问封装。
/// </summary>
public sealed class RedisCacheService(IDistributedCache cache, IConnectionMultiplexer? multiplexer = null)
{
    private readonly JsonSerializerOptions _serializerOptions = new(JsonSerializerDefaults.Web);
    private readonly IDatabase? _database = multiplexer?.GetDatabase();
    private readonly IConnectionMultiplexer? _multiplexer = multiplexer;

    /// <summary>
    /// 读取缓存。
    /// </summary>
    public async Task<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default)
    {
        var payload = await cache.GetAsync(key, cancellationToken);
        if (payload == null || payload.Length == 0)
        {
            return default;
        }

        return JsonSerializer.Deserialize<T>(payload, _serializerOptions);
    }

    /// <summary>
    /// 写入缓存。
    /// </summary>
    public Task SetAsync<T>(string key, T value, TimeSpan ttl, CancellationToken cancellationToken = default)
    {
        var payload = JsonSerializer.SerializeToUtf8Bytes(value, _serializerOptions);
        var options = new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow = ttl
        };
        return cache.SetAsync(key, payload, options, cancellationToken);
    }

    /// <summary>
    /// 删除缓存键。
    /// </summary>
    public Task RemoveAsync(string key, CancellationToken cancellationToken = default)
        => cache.RemoveAsync(key, cancellationToken);

    /// <summary>
    /// 按前缀删除缓存键。
    /// </summary>
    public async Task RemoveByPrefixAsync(string prefix, CancellationToken cancellationToken = default)
        => await RemoveByPrefixWithCountAsync(prefix, cancellationToken).ConfigureAwait(false);

    /// <summary>
    /// 按前缀删除缓存键并返回数量。
    /// </summary>
    public async Task<long> RemoveByPrefixWithCountAsync(string prefix, CancellationToken cancellationToken = default)
    {
        if (_multiplexer == null || _database == null || string.IsNullOrWhiteSpace(prefix))
        {
            return 0;
        }

        var pattern = prefix.EndsWith('*') ? prefix : $"{prefix}*";
        long removed = 0;
        foreach (var endpoint in _multiplexer.GetEndPoints())
        {
            var server = _multiplexer.GetServer(endpoint);
            foreach (var key in server.Keys(pattern: pattern))
            {
                await _database.KeyDeleteAsync(key).ConfigureAwait(false);
                removed += 1;
            }
        }

        return removed;
    }
}
