using System.Text.Json;
using Microsoft.Extensions.Options;
using StackExchange.Redis;

public class SofaLabCache
{
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNameCaseInsensitive = true
    };

    private readonly IConnectionMultiplexer _redis;
    private readonly SofaOptions _options;

    public SofaLabCache(IConnectionMultiplexer redis, IOptions<SofaOptions> options)
    {
        _redis = redis;
        _options = options.Value;
    }

    public async Task StoreAsync(
        Guid encounterId, string code, decimal value, DateTimeOffset recordedAt)
    {
        var json = JsonSerializer.Serialize(new SofaCachedValue(value, recordedAt), JsonOptions);
        var key = SofaCalculator.CacheKey(encounterId, code);
        await _redis.GetDatabase().StringSetAsync(
            key, json, TimeSpan.FromHours(_options.LabStalenessHours));
    }

    public async Task<SofaCachedValue?> GetAsync(Guid encounterId, string code)
    {
        var cached = await _redis.GetDatabase()
            .StringGetAsync(SofaCalculator.CacheKey(encounterId, code));
        if (!cached.HasValue) return null;
        return JsonSerializer.Deserialize<SofaCachedValue>(cached!, JsonOptions);
    }

    public SofaValueStatus Classify(SofaCachedValue? value)
    {
        if (value is null) return SofaValueStatus.Expired;
        var age = DateTimeOffset.UtcNow - value.RecordedAt;
        if (age.TotalHours > _options.LabStalenessHours) return SofaValueStatus.Expired;
        if (age.TotalHours > _options.LabWarningHours) return SofaValueStatus.Stale;
        return SofaValueStatus.Current;
    }

    public async Task<Dictionary<string, SofaCachedValue>> GetAllAsync(Guid encounterId)
    {
        var cache = _redis.GetDatabase();
        var keys = SofaCalculator.SofaObservationCodes
            .Select(c => (RedisKey)SofaCalculator.CacheKey(encounterId, c))
            .ToArray();
        var values = await cache.StringGetAsync(keys);

        var result = new Dictionary<string, SofaCachedValue>();
        for (var i = 0; i < SofaCalculator.SofaObservationCodes.Count; i++)
        {
            if (!values[i].HasValue) continue;
            var parsed = JsonSerializer.Deserialize<SofaCachedValue>(values[i]!, JsonOptions);
            if (parsed is not null)
                result[SofaCalculator.SofaObservationCodes[i]] = parsed;
        }
        return result;
    }
}