using StackExchange.Redis;

public static class News2Calculator
{
    // The 7 NEWS2 parameter codes. Order is stable for MGET.
    public static readonly IReadOnlyList<string> ParameterCodes = new[]
    {
        "RESP_RATE", "SPO2", "SYSTOLIC_BP", "HEART_RATE", "AVPU", "TEMP_C", "SUPPLEMENTAL_O2"
    };

    public static RedisKey[] AllParameterKeys(Guid encounterId) =>
        ParameterCodes
            .Select(code => (RedisKey)$"news2:{encounterId}:{code}")
            .ToArray();

    public static string ParameterKey(Guid encounterId, string code) =>
        $"news2:{encounterId}:{code}";

    public static bool IsNews2Code(string observationCode) =>
        ParameterCodes.Contains(observationCode) || GcsCalculator.IsGcsCode(observationCode);

    public static int ScoreRespRate(decimal value) => value switch
    {
        <= 8  => 3,
        <= 11 => 1,
        <= 20 => 0,
        <= 24 => 2,
        _     => 3
    };

    public static int ScoreSpo2(decimal value) => value switch
    {
        <= 91 => 3,
        <= 93 => 2,
        <= 95 => 1,
        _     => 0
    };

    public static int ScoreSystolicBp(decimal value) => value switch
    {
        <= 90  => 3,
        <= 100 => 2,
        <= 110 => 1,
        <= 219 => 0,
        _      => 3
    };

    public static int ScoreHeartRate(decimal value) => value switch
    {
        <= 40  => 3,
        <= 50  => 1,
        <= 90  => 0,
        <= 110 => 1,
        <= 130 => 2,
        _      => 3
    };

    public static int ScoreConsciousness(decimal value) => value switch
    {
        0 => 0,
        _ => 3
    };

    public static int ScoreConsciousnessFromGcs(int gcsTotal) =>
        GcsCalculator.ToNews2ConsciousnessScore(gcsTotal);

    public static int ScoreTemperature(decimal value) => value switch
    {
        <= 35.0m => 3,
        <= 36.0m => 1,
        <= 38.0m => 0,
        <= 39.0m => 1,
        _        => 2
    };

    public static int ScoreSupplementalO2(decimal value) =>
        value >= 1 ? 2 : 0;

    public static int ScoreParameter(string observationCode, decimal value) =>
        observationCode switch
        {
            "RESP_RATE"       => ScoreRespRate(value),
            "SPO2"            => ScoreSpo2(value),
            "SYSTOLIC_BP"     => ScoreSystolicBp(value),
            "HEART_RATE"      => ScoreHeartRate(value),
            "AVPU"            => ScoreConsciousness(value),
            "TEMP_C"          => ScoreTemperature(value),
            "SUPPLEMENTAL_O2" => ScoreSupplementalO2(value),
            _                 => throw new ArgumentOutOfRangeException(nameof(observationCode))
        };

    public static string DetermineRiskLevel(int totalScore, bool hasSingleParamThree) =>
        totalScore switch
        {
            >= 7                       => "HIGH",
            >= 5                       => "MEDIUM",
            _ when hasSingleParamThree => "LOW_MEDIUM",
            _                          => "LOW"
        };
}