using Akka.Cluster;

namespace ZB.MOM.WW.OtOpcUa.Cluster.Redundancy;

public readonly record struct NodeHealthInputs(
    MemberStatus MemberState,
    bool DbReachable,
    bool OpcUaProbeOk,
    bool Stale,
    bool IsDriverPrimary);

/// <summary>
/// Pure ServiceLevel computation per design §6. Output range 0–255, where higher = "more
/// authoritative." The OPC UA SDK exposes this as the node's <c>ServiceLevel</c> Variable and
/// redundant clients use it to pick which server to subscribe to.
///
/// Tiering:
/// - Member not Up/Joining: 0 (cluster cannot trust this node).
/// - DB reachable + OPC UA probe ok + not stale: 240 (full service).
/// - Stale config (DB reachable or not, OPC UA probe state ignored): 100 or 200 depending on DB.
/// - +10 bonus when this node is the driver Primary (the oldest Up member with the "driver" role —
///   where the cluster singletons live). Previously this keyed off Akka's role *leader*, the
///   lowest-addressed member, which names a different node once any node has restarted.
/// </summary>
public static class ServiceLevelCalculator
{
    /// <summary>Computes the service level (0-255) based on node health inputs.</summary>
    /// <param name="h">The node health inputs including cluster state, database reachability, and probe status.</param>
    /// <returns>A service level byte value (0-255) indicating node authority, where higher values indicate more authoritative nodes.</returns>
    public static byte Compute(NodeHealthInputs h)
    {
        if (h.MemberState is not (MemberStatus.Up or MemberStatus.Joining))
            return 0;

        var basis = (h.DbReachable, h.OpcUaProbeOk, h.Stale) switch
        {
            (true,  true,  false) => 240,
            (true,  _,     true)  => 200,
            (false, _,     true)  => 100,
            _ => 0,
        };

        return (byte)Math.Clamp(basis + (h.IsDriverPrimary ? 10 : 0), 0, 255);
    }
}
