using Akka.Actor;
using Akka.Cluster;
using Microsoft.Extensions.DependencyInjection;

namespace ZB.MOM.WW.Health.Akka;

/// <summary>
/// <see cref="IActiveNodeGate"/> implementation that computes <see cref="IsActiveNode"/> directly
/// from the Akka cluster state (self member <c>Up</c> and the local node is the cluster leader).
/// Register as a singleton.
/// </summary>
/// <remarks>
/// The <see cref="ActorSystem"/> is resolved lazily from the service provider; if it is not yet
/// available — e.g. during startup before Akka is initialised — <see cref="IsActiveNode"/> returns
/// <c>false</c> (the safe default during startup). This gate reads the cluster state directly and
/// does not resolve <see cref="ActiveNodeHealthCheck"/> from DI.
/// </remarks>
public sealed class AkkaActiveNodeGate : IActiveNodeGate
{
    private readonly IServiceProvider _serviceProvider;

    /// <summary>Initializes a new <see cref="AkkaActiveNodeGate"/>.</summary>
    /// <param name="serviceProvider">
    /// The application service provider. The <see cref="ActorSystem"/> is resolved lazily; if it is
    /// not yet available <see cref="IsActiveNode"/> returns <c>false</c>.
    /// </param>
    public AkkaActiveNodeGate(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
    }

    /// <inheritdoc />
    public bool IsActiveNode
    {
        get
        {
            var system = _serviceProvider.GetService<ActorSystem>();
            if (system is null)
                return false;

            var cluster = Cluster.Get(system);
            var self = cluster.SelfMember;
            if (self.Status != MemberStatus.Up)
                return false;

            var leader = cluster.State.Leader;
            return leader is not null && leader == self.Address;
        }
    }
}
