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

public class SofaVasopressorResolver
{
    private static readonly HashSet<string> VasopressorDrugs = new(StringComparer.OrdinalIgnoreCase)
    {
        "DOPAMINE", "DOBUTAMINE", "EPINEPHRINE", "NOREPINEPHRINE",
        "VASOPRESSIN", "PHENYLEPHRINE"
    };

    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        PropertyNameCaseInsensitive = true
    };

    private readonly AppDbContext _db;
    private readonly IConnectionMultiplexer _redis;
    private readonly SofaOptions _options;

    public SofaVasopressorResolver(
        AppDbContext db,
        IConnectionMultiplexer redis,
        IOptions<SofaOptions> options)
    {
        _db = db;
        _redis = redis;
        _options = options.Value;
    }

    public static string VasopressorCacheKey(Guid encounterId) =>
        $"sofa:{encounterId}:vasopressor";

    public async Task CacheFromAdministrationAsync(MedicationAdministration med, CancellationToken ct)
    {
        if (!VasopressorDrugs.Contains(med.DrugName)) return;

        var info = new VasopressorInfo(med.DrugName, NormalizeDose(med.DrugName, med.Dose, med.DoseUnit));
        var json = JsonSerializer.Serialize(new
        {
            info.DrugName,
            info.DoseUgKgMin,
            med.AdministeredAt
        }, JsonOptions);

        await _redis.GetDatabase().StringSetAsync(
            VasopressorCacheKey(med.EncounterId),
            json,
            TimeSpan.FromHours(_options.VasopressorWindowHours));
    }

    public async Task<VasopressorInfo?> GetActiveVasopressorAsync(
        Guid encounterId, CancellationToken ct)
    {
        var cached = await _redis.GetDatabase()
            .StringGetAsync(VasopressorCacheKey(encounterId));
        if (cached.HasValue)
        {
            using var doc = JsonDocument.Parse((string)cached!);
            var root = doc.RootElement;
            return new VasopressorInfo(
                root.GetProperty("drugName").GetString()!,
                root.GetProperty("doseUgKgMin").GetDecimal());
        }

        var since = DateTimeOffset.UtcNow.AddHours(-_options.VasopressorWindowHours);
        var med = await _db.MedicationAdministrations
            .AsNoTracking()
            .Where(m => m.EncounterId == encounterId
                     && VasopressorDrugs.Contains(m.DrugName)
                     && m.AdministeredAt >= since)
            .OrderByDescending(m => m.AdministeredAt)
            .FirstOrDefaultAsync(ct);

        if (med is null) return null;
        return new VasopressorInfo(med.DrugName, NormalizeDose(med.DrugName, med.Dose, med.DoseUnit));
    }

    private static decimal NormalizeDose(string drug, decimal dose, string unit) =>
        unit.ToLowerInvariant() switch
        {
            "mcg/kg/min" or "µg/kg/min" => dose,
            "mcg/min" or "µg/min"       => dose / 70m,
            "mg/hr"                      => dose * 1000m / 60m / 70m,
            _                            => dose
        };
}