using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.Extensions.Configuration;
using ScadaLink.Host.Health;

namespace ScadaLink.Host.Tests;

/// <summary>
/// WP-12: Tests for /health/ready and /health/active endpoints.
/// </summary>
public class HealthCheckTests : IDisposable
{
    private readonly List<IDisposable> _disposables = new();

    public HealthCheckTests()
    {
        // Host-003: connection strings are externalised; supply them via env vars.
        _disposables.Add(new CentralDbTestEnvironment());
    }

    public void Dispose()
    {
        foreach (var d in _disposables)
        {
            try { d.Dispose(); } catch { /* best effort */ }
        }
    }

    [Fact]
    public async Task HealthReady_Endpoint_ReturnsResponse()
    {
        var previousEnv = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
        try
        {
            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Central");

            var factory = new WebApplicationFactory<Program>()
                .WithWebHostBuilder(builder =>
                {
                    builder.ConfigureAppConfiguration((context, config) =>
                    {
                        config.AddInMemoryCollection(new Dictionary<string, string?>
                        {
                            ["ScadaLink:Node:NodeHostname"] = "localhost",
                            ["ScadaLink:Node:RemotingPort"] = "0",
                            ["ScadaLink:Cluster:SeedNodes:0"] = "akka.tcp://scadalink@localhost:2551",
                            ["ScadaLink:Cluster:SeedNodes:1"] = "akka.tcp://scadalink@localhost:2552",
                            ["ScadaLink:Database:SkipMigrations"] = "true",
                        });
                    });
                    builder.UseSetting("ScadaLink:Node:Role", "Central");
                    builder.UseSetting("ScadaLink:Database:SkipMigrations", "true");
                });
            _disposables.Add(factory);

            var client = factory.CreateClient();
            _disposables.Add(client);

            var response = await client.GetAsync("/health/ready");

            // The endpoint exists and returns a status code.
            // With test infrastructure (no real DB), the database check may fail,
            // so we accept either 200 (Healthy) or 503 (Unhealthy).
            Assert.True(
                response.StatusCode == System.Net.HttpStatusCode.OK ||
                response.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable,
                $"Expected 200 or 503, got {(int)response.StatusCode}");
        }
        finally
        {
            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", previousEnv);
        }
    }

    [Fact]
    public async Task HealthActive_Endpoint_ReturnsResponse()
    {
        var previousEnv = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
        try
        {
            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Central");

            var factory = new WebApplicationFactory<Program>()
                .WithWebHostBuilder(builder =>
                {
                    builder.ConfigureAppConfiguration((context, config) =>
                    {
                        config.AddInMemoryCollection(new Dictionary<string, string?>
                        {
                            ["ScadaLink:Node:NodeHostname"] = "localhost",
                            ["ScadaLink:Node:RemotingPort"] = "0",
                            ["ScadaLink:Cluster:SeedNodes:0"] = "akka.tcp://scadalink@localhost:2551",
                            ["ScadaLink:Cluster:SeedNodes:1"] = "akka.tcp://scadalink@localhost:2552",
                            ["ScadaLink:Database:SkipMigrations"] = "true",
                        });
                    });
                    builder.UseSetting("ScadaLink:Node:Role", "Central");
                    builder.UseSetting("ScadaLink:Database:SkipMigrations", "true");
                });
            _disposables.Add(factory);

            var client = factory.CreateClient();
            _disposables.Add(client);

            var response = await client.GetAsync("/health/active");

            // In test mode, the ActorSystem may not be fully available,
            // so the active-node check returns 503 (Unhealthy).
            Assert.True(
                response.StatusCode == System.Net.HttpStatusCode.OK ||
                response.StatusCode == System.Net.HttpStatusCode.ServiceUnavailable,
                $"Expected 200 or 503, got {(int)response.StatusCode}");
        }
        finally
        {
            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", previousEnv);
        }
    }

    [Fact]
    public async Task HealthReady_Endpoint_ExcludesActiveNodeCheck()
    {
        // Host-001 regression: /health/ready must reflect cluster membership + DB
        // connectivity only (REQ-HOST-4a), NOT cluster leadership. The leader-only
        // "active-node" check belongs solely to /health/active. If /health/ready
        // included "active-node", a fully operational standby central node would
        // permanently report 503, breaking load-balancer failover readiness.
        var previousEnv = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
        try
        {
            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Central");

            var factory = new WebApplicationFactory<Program>()
                .WithWebHostBuilder(builder =>
                {
                    builder.ConfigureAppConfiguration((context, config) =>
                    {
                        config.AddInMemoryCollection(new Dictionary<string, string?>
                        {
                            ["ScadaLink:Node:NodeHostname"] = "localhost",
                            ["ScadaLink:Node:RemotingPort"] = "0",
                            ["ScadaLink:Cluster:SeedNodes:0"] = "akka.tcp://scadalink@localhost:2551",
                            ["ScadaLink:Cluster:SeedNodes:1"] = "akka.tcp://scadalink@localhost:2552",
                            ["ScadaLink:Database:SkipMigrations"] = "true",
                        });
                    });
                    builder.UseSetting("ScadaLink:Node:Role", "Central");
                    builder.UseSetting("ScadaLink:Database:SkipMigrations", "true");
                });
            _disposables.Add(factory);

            var client = factory.CreateClient();
            _disposables.Add(client);

            var response = await client.GetAsync("/health/ready");
            var body = await response.Content.ReadAsStringAsync();

            // The readiness body lists each executed check by name in its entries map.
            // The leader-only "active-node" check must not be among them.
            Assert.DoesNotContain("active-node", body);
        }
        finally
        {
            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", previousEnv);
        }
    }

    [Fact]
    public async Task ActiveNodeHealthCheck_SystemNotStarted_ReturnsUnhealthy()
    {
        // AkkaHostedService before StartAsync has ActorSystem == null.
        // The integration test (HealthActive_Endpoint_ReturnsResponse) validates the full
        // endpoint wiring. This test validates the null-system path via WebApplicationFactory
        // where the ActorSystem may not be available.
        var previousEnv = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
        try
        {
            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", "Central");
            var factory = new WebApplicationFactory<Program>()
                .WithWebHostBuilder(builder =>
                {
                    builder.ConfigureAppConfiguration((context, config) =>
                    {
                        config.AddInMemoryCollection(new Dictionary<string, string?>
                        {
                            ["ScadaLink:Node:NodeHostname"] = "localhost",
                            ["ScadaLink:Node:RemotingPort"] = "0",
                            ["ScadaLink:Cluster:SeedNodes:0"] = "akka.tcp://scadalink@localhost:2551",
                            ["ScadaLink:Database:SkipMigrations"] = "true",
                        });
                    });
                    builder.UseSetting("ScadaLink:Node:Role", "Central");
                    builder.UseSetting("ScadaLink:Database:SkipMigrations", "true");
                });
            _disposables.Add(factory);

            var client = factory.CreateClient();
            _disposables.Add(client);

            var response = await client.GetAsync("/health/active");
            var body = await response.Content.ReadAsStringAsync();

            // Active-node check returns 503 when ActorSystem is not yet available or not leader
            Assert.Equal(System.Net.HttpStatusCode.ServiceUnavailable, response.StatusCode);
            Assert.Contains("active-node", body);
        }
        finally
        {
            Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", previousEnv);
        }
    }
}
