using Akka.Cluster;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace ZB.MOM.WW.Health.Akka;

/// <summary>
/// Pure mapping from an Akka <see cref="MemberStatus"/> to a <see cref="HealthStatus"/>.
/// </summary>
/// <remarks>
/// <para>
/// Wraps a <see cref="Func{MemberStatus, HealthStatus}"/> so the decision logic is a deterministic,
/// table-testable function — <see cref="AkkaClusterHealthCheck"/> only supplies the live cluster
/// status. Two named presets reconcile the divergence between the existing ScadaBridge and OtOpcUa
/// implementations; construct a custom instance for project-specific overrides.
/// </para>
/// </remarks>
public sealed class AkkaClusterStatusPolicy
{
    private readonly Func<MemberStatus, HealthStatus> _evaluate;

    /// <summary>Initializes a new <see cref="AkkaClusterStatusPolicy"/>.</summary>
    /// <param name="evaluate">The pure status-to-health mapping function.</param>
    public AkkaClusterStatusPolicy(Func<MemberStatus, HealthStatus> evaluate)
    {
        _evaluate = evaluate ?? throw new ArgumentNullException(nameof(evaluate));
    }

    /// <summary>Applies the policy to the given member status.</summary>
    /// <param name="status">The local node's Akka cluster member status.</param>
    /// <returns>The mapped <see cref="HealthStatus"/>.</returns>
    public HealthStatus Evaluate(MemberStatus status) => _evaluate(status);

    /// <summary>
    /// ScadaBridge origin: <c>Up</c>/<c>Joining</c> → Healthy, <c>Leaving</c>/<c>Exiting</c> →
    /// Degraded, everything else → Unhealthy. The convergence target for all projects.
    /// </summary>
    public static AkkaClusterStatusPolicy Default { get; } = new(static status => status switch
    {
        MemberStatus.Up or MemberStatus.Joining => HealthStatus.Healthy,
        MemberStatus.Leaving or MemberStatus.Exiting => HealthStatus.Degraded,
        _ => HealthStatus.Unhealthy,
    });

    /// <summary>
    /// OtOpcUa origin: self-<c>Up</c>-among-reachable-members → Healthy, any non-<c>Up</c> state
    /// (including <c>Leaving</c>/<c>Exiting</c>/<c>Down</c>) → Degraded. Provided for backward
    /// compatibility during OtOpcUa's migration.
    /// </summary>
    /// <remarks>
    /// The original OtOpcUa check scanned the reachable member set for self with
    /// <c>Status == Up</c>; any other state caused the scan to miss self and collapse to Degraded.
    /// This preset reproduces that behavior: only <see cref="MemberStatus.Up"/> is Healthy.
    /// </remarks>
    public static AkkaClusterStatusPolicy OtOpcUaCompat { get; } = new(static status =>
        status == MemberStatus.Up ? HealthStatus.Healthy : HealthStatus.Degraded);
}
