using Akka.Cluster;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Cluster.Redundancy;

namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Tests;

public sealed class ServiceLevelCalculatorTests
{
    /// <summary>Verifies that non-Up member statuses return a service level of zero.</summary>
    /// <param name="status">The Akka cluster member status to test.</param>
    [Theory]
    [InlineData(MemberStatus.Down)]
    [InlineData(MemberStatus.Removed)]
    [InlineData(MemberStatus.Exiting)]
    [InlineData(MemberStatus.Leaving)]
    public void NotUp_returns_zero(MemberStatus status)
    {
        var sl = ServiceLevelCalculator.Compute(new(status,
            DbReachable: true, OpcUaProbeOk: true, Stale: false, IsDriverPrimary: true));
        sl.ShouldBe((byte)0);
    }

    /// <summary>Verifies that a fully healthy non-leader member returns a service level of 240.</summary>
    [Fact]
    public void Fully_healthy_non_leader_returns_240()
    {
        var sl = ServiceLevelCalculator.Compute(new(MemberStatus.Up,
            DbReachable: true, OpcUaProbeOk: true, Stale: false, IsDriverPrimary: false));
        sl.ShouldBe((byte)240);
    }

    /// <summary>Verifies that a fully healthy role leader returns a service level of 250.</summary>
    [Fact]
    public void Fully_healthy_role_leader_returns_250()
    {
        var sl = ServiceLevelCalculator.Compute(new(MemberStatus.Up,
            DbReachable: true, OpcUaProbeOk: true, Stale: false, IsDriverPrimary: true));
        sl.ShouldBe((byte)250);
    }

    /// <summary>Verifies that a database reachable but stale returns a service level of 200.</summary>
    [Fact]
    public void Db_reachable_but_stale_returns_200()
    {
        var sl = ServiceLevelCalculator.Compute(new(MemberStatus.Up,
            DbReachable: true, OpcUaProbeOk: true, Stale: true, IsDriverPrimary: false));
        sl.ShouldBe((byte)200);
    }

    /// <summary>Verifies that an unreachable and stale database returns a service level of 100.</summary>
    [Fact]
    public void Db_unreachable_and_stale_returns_100()
    {
        var sl = ServiceLevelCalculator.Compute(new(MemberStatus.Up,
            DbReachable: false, OpcUaProbeOk: false, Stale: true, IsDriverPrimary: false));
        sl.ShouldBe((byte)100);
    }

    /// <summary>Verifies that an OPC UA probe failure on a non-stale database returns zero.</summary>
    [Fact]
    public void Opcua_probe_fail_when_not_stale_returns_zero()
    {
        // (DbReachable=true, OpcUaProbeOk=false, Stale=false) falls through to the catch-all 0.
        var sl = ServiceLevelCalculator.Compute(new(MemberStatus.Up,
            DbReachable: true, OpcUaProbeOk: false, Stale: false, IsDriverPrimary: false));
        sl.ShouldBe((byte)0);
    }

    /// <summary>Verifies that a Joining member status is treated like Up for service level grading.</summary>
    [Fact]
    public void Joining_member_is_treated_like_Up_for_grading()
    {
        var sl = ServiceLevelCalculator.Compute(new(MemberStatus.Joining,
            DbReachable: true, OpcUaProbeOk: true, Stale: false, IsDriverPrimary: false));
        sl.ShouldBe((byte)240);
    }
}
