using Akka.Actor;
using Akka.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using ZB.MOM.WW.Health.Akka;

namespace ZB.MOM.WW.Health.Akka.Tests;

/// <summary>
/// The states a node passes through on the way up, where the active tier must not report a booting
/// node as a failed standby — now that the tier is a real 503, an Unhealthy here would pull the node
/// out of an orchestrator's pool while it starts.
/// </summary>
public sealed class ActiveNodeStartupSafetyTests
{
    [Fact]
    public async Task NoActorSystem_IsDegraded()
    {
        var check = new ActiveNodeHealthCheck(new ServiceCollection().BuildServiceProvider());

        var result = await check.CheckHealthAsync(NewContext(check));

        Assert.Equal(HealthStatus.Degraded, result.Status);
        // Description-only: an empty data dictionary is what makes the writer omit "data" entirely.
        Assert.Empty(result.Data);
    }

    [Fact]
    public async Task NoActorSystem_RoleScoped_IsDegraded()
    {
        var check = new ActiveNodeHealthCheck(new ServiceCollection().BuildServiceProvider(), "admin");

        Assert.Equal(HealthStatus.Degraded, (await check.CheckHealthAsync(NewContext(check))).Status);
    }

    [Fact]
    public void Gate_NoActorSystem_IsNotActive()
    {
        Assert.False(new AkkaActiveNodeGate(new ServiceCollection().BuildServiceProvider()).IsActiveNode);
    }

    [Fact]
    public async Task ClusterNotConfigured_IsDegraded()
    {
        // ActorSystem present but Akka.Cluster not configured → Cluster.Get throws. The check must
        // return Degraded (the spec's startup-safety rule), not let the exception escape (→ Unhealthy).
        using var system = ActorSystem.Create("active-node-no-cluster");
        try
        {
            var provider = new ServiceCollection().AddSingleton(system).BuildServiceProvider();
            var check = new ActiveNodeHealthCheck(provider);

            Assert.Equal(HealthStatus.Degraded, (await check.CheckHealthAsync(NewContext(check))).Status);
        }
        finally
        {
            await system.Terminate();
        }
    }

    [Fact]
    public async Task Gate_ClusterNotConfigured_IsNotActive()
    {
        using var system = ActorSystem.Create("active-node-gate-no-cluster");
        try
        {
            var provider = new ServiceCollection().AddSingleton(system).BuildServiceProvider();

            Assert.False(new AkkaActiveNodeGate(provider).IsActiveNode);
        }
        finally
        {
            await system.Terminate();
        }
    }

    [Fact]
    public async Task ClusteredButNeverJoined_IsUnhealthy_NobodyIsActive()
    {
        // A cold start: the cluster provider is up but no member has reached Up. Nobody is active, so
        // nobody may claim to be — this is the one case that must NOT be Degraded, because a cluster
        // that never forms would otherwise sit at 200 forever.
        var config = ConfigurationFactory.ParseString("""
            akka {
              loglevel = "WARNING"
              actor.provider = cluster
              remote.dot-netty.tcp { hostname = "127.0.0.1", port = 0 }
              cluster.seed-nodes = []
            }
            """);
        var system = ActorSystem.Create("active-node-never-joined", config);
        try
        {
            var provider = new ServiceCollection().AddSingleton(system).BuildServiceProvider();
            var check = new ActiveNodeHealthCheck(provider);

            var result = await check.CheckHealthAsync(NewContext(check));

            Assert.Equal(HealthStatus.Unhealthy, result.Status);
            Assert.Contains("No Up member", result.Description);
            Assert.False(new AkkaActiveNodeGate(provider).IsActiveNode);
        }
        finally
        {
            await system.Terminate();
        }
    }

    private static HealthCheckContext NewContext(IHealthCheck check) => new()
    {
        Registration = new HealthCheckRegistration("active-node", check, HealthStatus.Unhealthy, tags: null),
    };
}
