using Microsoft.EntityFrameworkCore;
using StackExchange.Redis;

public class QsofaService : IQsofaService
{
    private readonly AppDbContext _db;
    private readonly IConnectionMultiplexer _redis;

    public QsofaService(AppDbContext db, IConnectionMultiplexer redis)
    {
        _db = db;
        _redis = redis;
    }

    public async Task<QsofaCurrentResponse> GetCurrentAsync(Guid encounterId)
    {
        await EnsureEncounterExistsAsync(encounterId);

        var values = await ReadCriterionValuesAsync(encounterId);
        return BuildResponse(values);
    }

    public async Task<int> GetActiveCriteriaCountAsync(Guid encounterId)
    {
        var values = await ReadCriterionValuesAsync(encounterId);
        return QsofaCalculator.CountActiveCriteria(values);
    }

    public async Task<CursorPage<ClinicalAlert>> GetHistoryAsync(
        Guid encounterId, int limit, string? cursor)
    {
        limit = Math.Clamp(limit, 1, 100);

        var query = _db.ClinicalAlerts
            .AsNoTracking()
            .Where(a => a.EncounterId == encounterId
#pragma warning disable CS0618 // Include legacy QSOFA_WARNING rows in history
                && (a.AlertType == AlertType.QsofaScreen || a.AlertType == AlertType.QsofaWarning));
#pragma warning restore CS0618

        var items = await query
            .OrderByDescending(a => a.TriggeredAt)
            .ThenByDescending(a => a.Id)
            .Take(limit + 1)
            .ToListAsync();

        var hasMore = items.Count > limit;
        if (hasMore) items.RemoveAt(limit);

        return new CursorPage<ClinicalAlert>(items, null, hasMore);
    }

    private async Task EnsureEncounterExistsAsync(Guid encounterId)
    {
        var exists = await _db.Encounters.AnyAsync(e => e.Id == encounterId);
        if (!exists)
            throw new NotFoundException("Encounter not found.", "ENCOUNTER_NOT_FOUND");
    }

    private async Task<RedisValue[]> ReadCriterionValuesAsync(Guid encounterId)
    {
        var cache = _redis.GetDatabase();
        return await cache.StringGetAsync(QsofaCalculator.AllCriterionKeys(encounterId));
    }

    private static QsofaCurrentResponse BuildResponse(RedisValue[] values)
    {
        return new QsofaCurrentResponse(
            QsofaCalculator.CountActiveCriteria(values),
            new QsofaCriteriaState(
                ParseOptionalDecimal(values[0]),
                ParseOptionalDecimal(values[1]),
                ParseOptionalDecimal(values[2])));
    }

    private static decimal? ParseOptionalDecimal(RedisValue value) =>
        value.HasValue ? decimal.Parse(value.ToString()) : null;
}
