using System.Reflection;
using System.Text.Json;

namespace ScadaLink.Host.Tests;

/// <summary>
/// Host-003 regression: secrets must not be committed in plaintext in the
/// shipped <c>appsettings.Central.json</c>. Connection-string passwords, the LDAP
/// service-account password and the JWT signing key must be supplied via
/// environment variables (or another secret store) at deployment time — the
/// committed file may only carry non-sensitive structural defaults or
/// placeholder values.
/// </summary>
public class ConfigSecretsTests
{
    private static string FindHostProjectDirectory()
    {
        var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!;
        var dir = new DirectoryInfo(assemblyDir);
        while (dir != null)
        {
            var hostPath = Path.Combine(dir.FullName, "src", "ScadaLink.Host");
            if (Directory.Exists(hostPath))
                return hostPath;
            dir = dir.Parent;
        }
        throw new DirectoryNotFoundException("Could not locate src/ScadaLink.Host");
    }

    private static JsonElement ScadaLinkSection()
    {
        var path = Path.Combine(FindHostProjectDirectory(), "appsettings.Central.json");
        var json = File.ReadAllText(path);
        using var doc = JsonDocument.Parse(json);
        return doc.RootElement.GetProperty("ScadaLink").Clone();
    }

    [Fact]
    public void CentralConfig_ConnectionStrings_ContainNoPlaintextPassword()
    {
        var db = ScadaLinkSection().GetProperty("Database");
        foreach (var prop in db.EnumerateObject())
        {
            var value = prop.Value.GetString() ?? string.Empty;
            // A committed connection string must not carry a literal Password= value.
            // Either the password is delivered via an environment variable or the
            // whole connection string is. A placeholder reference is acceptable.
            var idx = value.IndexOf("Password=", StringComparison.OrdinalIgnoreCase);
            if (idx >= 0)
            {
                var after = value[(idx + "Password=".Length)..];
                var literal = after.Split(';')[0];
                Assert.True(
                    literal.Length == 0 || literal.Contains('{') || literal.Contains('$'),
                    $"appsettings.Central.json '{prop.Name}' contains a plaintext Password value '{literal}'. " +
                    "Move the secret to an environment variable.");
            }
        }
    }

    [Fact]
    public void CentralConfig_LdapServiceAccountPassword_IsNotCommitted()
    {
        var security = ScadaLinkSection().GetProperty("Security");
        if (security.TryGetProperty("LdapServiceAccountPassword", out var pw))
        {
            var value = pw.GetString() ?? string.Empty;
            Assert.True(
                value.Length == 0 || value.Contains('{') || value.Contains('$'),
                $"appsettings.Central.json carries a plaintext LdapServiceAccountPassword '{value}'. " +
                "Move it to an environment variable.");
        }
    }

    [Fact]
    public void CentralConfig_JwtSigningKey_IsNotCommitted()
    {
        var security = ScadaLinkSection().GetProperty("Security");
        if (security.TryGetProperty("JwtSigningKey", out var key))
        {
            var value = key.GetString() ?? string.Empty;
            Assert.True(
                value.Length == 0 || value.Contains('{') || value.Contains('$'),
                $"appsettings.Central.json carries a committed JwtSigningKey '{value}'. " +
                "A committed signing key lets anyone with repo access forge session tokens. " +
                "Move it to an environment variable.");
        }
    }
}
