using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;

namespace ScadaLink.IntegrationTests;

/// <summary>
/// WP-22: Startup validation — missing required config fails with clear error.
/// Tests the StartupValidator that runs on boot.
///
/// Note: These tests temporarily set environment variables because Program.cs reads
/// configuration from env vars in the initial ConfigurationBuilder (before WebApplicationFactory
/// can inject settings). Each test saves/restores env vars to avoid interference.
/// </summary>
public class StartupValidationTests
{
    [Fact]
    public void MissingRole_ThrowsInvalidOperationException()
    {
        // Set all required config EXCEPT Role
        using var env = new TempEnvironment(new Dictionary<string, string>
        {
            ["DOTNET_ENVIRONMENT"] = "Development",
            ["ScadaLink__Node__NodeHostname"] = "localhost",
            ["ScadaLink__Node__RemotingPort"] = "8081",
            ["ScadaLink__Cluster__SeedNodes__0"] = "akka.tcp://scadalink@localhost:8081",
            ["ScadaLink__Cluster__SeedNodes__1"] = "akka.tcp://scadalink@localhost:8082",
        });

        var factory = new WebApplicationFactory<Program>();

        var ex = Assert.Throws<InvalidOperationException>(() => factory.CreateClient());
        Assert.Contains("Role", ex.Message, StringComparison.OrdinalIgnoreCase);

        factory.Dispose();
    }

    [Fact]
    public void MissingJwtSigningKey_ForCentral_ThrowsInvalidOperationException()
    {
        using var env = new TempEnvironment(new Dictionary<string, string>
        {
            ["DOTNET_ENVIRONMENT"] = "Development",
            ["ScadaLink__Node__Role"] = "Central",
            ["ScadaLink__Node__NodeHostname"] = "localhost",
            ["ScadaLink__Node__RemotingPort"] = "8081",
            ["ScadaLink__Cluster__SeedNodes__0"] = "akka.tcp://scadalink@localhost:8081",
            ["ScadaLink__Cluster__SeedNodes__1"] = "akka.tcp://scadalink@localhost:8082",
            ["ScadaLink__Database__ConfigurationDb"] = "Server=x;Database=x",
            ["ScadaLink__Database__MachineDataDb"] = "Server=x;Database=x",
            ["ScadaLink__Security__LdapServer"] = "localhost",
            // Deliberately missing JwtSigningKey
        });

        var factory = new WebApplicationFactory<Program>();

        var ex = Assert.Throws<InvalidOperationException>(() => factory.CreateClient());
        Assert.Contains("JwtSigningKey", ex.Message, StringComparison.OrdinalIgnoreCase);

        factory.Dispose();
    }

    [Fact]
    public void CentralRole_StartsSuccessfully_WithValidConfig()
    {
        using var factory = new ScadaLinkWebApplicationFactory();
        using var client = factory.CreateClient();

        Assert.NotNull(client);
    }

    /// <summary>
    /// Helper to temporarily set environment variables and restore them on dispose.
    /// Clears all ScadaLink__ vars first to ensure a clean slate.
    /// </summary>
    private sealed class TempEnvironment : IDisposable
    {
        private readonly Dictionary<string, string?> _previousValues = new();

        /// <summary>
        /// All ScadaLink env vars that might be set by other tests/factories.
        /// </summary>
        private static readonly string[] KnownKeys =
        {
            "DOTNET_ENVIRONMENT",
            "ScadaLink__Node__Role",
            "ScadaLink__Node__NodeHostname",
            "ScadaLink__Node__RemotingPort",
            "ScadaLink__Node__SiteId",
            "ScadaLink__Cluster__SeedNodes__0",
            "ScadaLink__Cluster__SeedNodes__1",
            "ScadaLink__Database__ConfigurationDb",
            "ScadaLink__Database__MachineDataDb",
            "ScadaLink__Database__SkipMigrations",
            "ScadaLink__Security__JwtSigningKey",
            "ScadaLink__Security__LdapServer",
            "ScadaLink__Security__LdapPort",
            "ScadaLink__Security__LdapUseTls",
            "ScadaLink__Security__AllowInsecureLdap",
            "ScadaLink__Security__LdapSearchBase",
        };

        public TempEnvironment(Dictionary<string, string> varsToSet)
        {
            // Save and clear all known keys
            foreach (var key in KnownKeys)
            {
                _previousValues[key] = Environment.GetEnvironmentVariable(key);
                Environment.SetEnvironmentVariable(key, null);
            }

            // Set the requested vars
            foreach (var (key, value) in varsToSet)
            {
                if (!_previousValues.ContainsKey(key))
                    _previousValues[key] = Environment.GetEnvironmentVariable(key);
                Environment.SetEnvironmentVariable(key, value);
            }
        }

        public void Dispose()
        {
            foreach (var (key, previousValue) in _previousValues)
            {
                Environment.SetEnvironmentVariable(key, previousValue);
            }
        }
    }
}
