using Akka.Actor;
using Akka.Cluster;
using ScadaLink.Commons.Messages.Health;
using ScadaLink.HealthMonitoring;
using ScadaLink.Host.Actors;

namespace ScadaLink.Host.Health;

/// <summary>
/// Provides cluster node statuses from Akka.NET cluster membership for health reporting.
/// </summary>
public class AkkaClusterNodeProvider : IClusterNodeProvider
{
    private readonly AkkaHostedService _akkaService;
    private readonly string _siteRole;

    public AkkaClusterNodeProvider(AkkaHostedService akkaService, string siteRole)
    {
        _akkaService = akkaService;
        _siteRole = siteRole;
    }

    public bool SelfIsPrimary
    {
        get
        {
            var system = _akkaService.ActorSystem;
            if (system == null) return false;
            var cluster = Cluster.Get(system);
            if (cluster.SelfMember.Status != MemberStatus.Up) return false;
            var leader = cluster.State.Leader;
            return leader != null && leader.Equals(cluster.SelfAddress);
        }
    }

    public IReadOnlyList<NodeStatus> GetClusterNodes()
    {
        var system = _akkaService.ActorSystem;
        if (system == null) return [];

        var cluster = Cluster.Get(system);
        var selfAddress = cluster.SelfAddress;
        var leader = cluster.State.Leader;

        var nodes = new List<NodeStatus>();
        foreach (var member in cluster.State.Members)
        {
            if (!member.HasRole(_siteRole))
                continue;

            var hostname = member.Address.Host ?? member.Address.ToString();
            var isOnline = member.Status == MemberStatus.Up;
            var isLeader = member.Address.Equals(leader);
            var role = isLeader ? "Primary" : "Standby";

            nodes.Add(new NodeStatus(hostname, isOnline, role));
        }

        // If we have unreachable members, add them as offline
        foreach (var unreachable in cluster.State.Unreachable)
        {
            if (!unreachable.HasRole(_siteRole))
                continue;

            // Don't duplicate if already in members list
            if (nodes.Any(n => n.Hostname == (unreachable.Address.Host ?? unreachable.Address.ToString())))
                continue;

            var hostname = unreachable.Address.Host ?? unreachable.Address.ToString();
            nodes.Add(new NodeStatus(hostname, false, "Standby"));
        }

        return nodes;
    }
}
