#nullable enable
using Baya.Application.Contracts.Common;
using Microsoft.Extensions.Caching.Memory;

namespace Baya.Infrastructure.CrossCutting.Seams;

/// <summary>
/// In-process <see cref="IMemoryCache"/> implementation of <see cref="ICacheService"/> — the mock seam.
/// The real implementation swaps to Redis (StackExchange.Redis) while keeping the same key/TTL scheme.
/// </summary>
public sealed class MemoryCacheService(IMemoryCache cache) : ICacheService
{
    public ValueTask<T?> GetAsync<T>(string key, CancellationToken cancellationToken = default)
    {
        return ValueTask.FromResult(cache.TryGetValue(key, out T? value) ? value : default);
    }

    public ValueTask SetAsync<T>(string key, T value, TimeSpan? ttl = null, CancellationToken cancellationToken = default)
    {
        var options = new MemoryCacheEntryOptions();
        if (ttl is { } expiry)
            options.AbsoluteExpirationRelativeToNow = expiry;

        cache.Set(key, value, options);
        return ValueTask.CompletedTask;
    }

    public ValueTask RemoveAsync(string key, CancellationToken cancellationToken = default)
    {
        cache.Remove(key);
        return ValueTask.CompletedTask;
    }

    public async ValueTask<T> GetOrCreateAsync<T>(
        string key,
        Func<CancellationToken, ValueTask<T>> factory,
        TimeSpan? ttl = null,
        CancellationToken cancellationToken = default)
    {
        if (cache.TryGetValue(key, out T? cached) && cached is not null)
            return cached;

        var value = await factory(cancellationToken);
        await SetAsync(key, value, ttl, cancellationToken);
        return value;
    }
}
