using System;
using System.IO;
using System.Linq;
using StellaOps.Authority.Plugins.Abstractions;
using StellaOps.Configuration;
using Xunit;

namespace StellaOps.Configuration.Tests;

public class AuthorityPluginConfigurationLoaderTests : IDisposable
{
    private readonly string tempRoot;

    public AuthorityPluginConfigurationLoaderTests()
    {
        tempRoot = Path.Combine(Path.GetTempPath(), "authority-plugin-tests", Guid.NewGuid().ToString("N"));
        Directory.CreateDirectory(tempRoot);
    }

    [Fact]
    public void Load_ReturnsConfiguration_ForEnabledPlugin()
    {
        var pluginDir = Path.Combine(tempRoot, "etc", "authority.plugins");
        Directory.CreateDirectory(pluginDir);

        var standardConfigPath = Path.Combine(pluginDir, "standard.yaml");
        File.WriteAllText(standardConfigPath, "secretKey: value");

        var options = CreateOptions();
        options.Plugins.ConfigurationDirectory = "etc/authority.plugins";
        options.Plugins.Descriptors["standard"] = new AuthorityPluginDescriptorOptions
        {
            AssemblyName = "StellaOps.Authority.Plugin.Standard",
            Enabled = true
        };

        options.Validate();

        var contexts = AuthorityPluginConfigurationLoader.Load(options, tempRoot);
        var context = Assert.Single(contexts);
        Assert.Equal("standard", context.Manifest.Name);
        Assert.Equal("value", context.Configuration["secretKey"]);
        Assert.True(context.Manifest.Enabled);
    }

    [Fact]
    public void Load_Throws_WhenEnabledConfigMissing()
    {
        var options = CreateOptions();
        options.Plugins.ConfigurationDirectory = "etc/authority.plugins";
        options.Plugins.Descriptors["standard"] = new AuthorityPluginDescriptorOptions
        {
            AssemblyName = "StellaOps.Authority.Plugin.Standard",
            Enabled = true
        };

        options.Validate();

        var ex = Assert.Throws<FileNotFoundException>(() =>
            AuthorityPluginConfigurationLoader.Load(options, tempRoot));

        Assert.Contains("standard.yaml", ex.FileName, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void Load_SkipsMissingFile_ForDisabledPlugin()
    {
        var options = CreateOptions();
        options.Plugins.ConfigurationDirectory = "etc/authority.plugins";
        options.Plugins.Descriptors["ldap"] = new AuthorityPluginDescriptorOptions
        {
            AssemblyName = "StellaOps.Authority.Plugin.Ldap",
            Enabled = false,
            ConfigFile = "ldap.yaml"
        };

        options.Validate();

        var contexts = AuthorityPluginConfigurationLoader.Load(options, tempRoot);
        var context = Assert.Single(contexts);
        Assert.False(context.Manifest.Enabled);
        Assert.Equal("ldap", context.Manifest.Name);
        Assert.Null(context.Configuration["connection:host"]);
    }

    [Fact]
    public void Validate_ThrowsForUnknownCapability()
    {
        var options = CreateOptions();
        options.Plugins.Descriptors["standard"] = new AuthorityPluginDescriptorOptions
        {
            AssemblyName = "StellaOps.Authority.Plugin.Standard",
            Enabled = true
        };
        options.Plugins.Descriptors["standard"].Capabilities.Add("custom-flow");

        var ex = Assert.Throws<InvalidOperationException>(() => options.Validate());
        Assert.Contains("unknown capability", ex.Message, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void Analyze_ReturnsWarning_WhenStandardPasswordPolicyWeaker()
    {
        var pluginDir = Path.Combine(tempRoot, "etc", "authority.plugins");
        Directory.CreateDirectory(pluginDir);

        var standardConfigPath = Path.Combine(pluginDir, "standard.yaml");
        File.WriteAllText(standardConfigPath, "passwordPolicy:\n  minimumLength: 8\n  requireSymbol: false\n");

        var options = CreateOptions();
        options.Plugins.ConfigurationDirectory = "etc/authority.plugins";
        options.Plugins.Descriptors["standard"] = new AuthorityPluginDescriptorOptions
        {
            AssemblyName = "StellaOps.Authority.Plugin.Standard",
            Enabled = true
        };

        options.Validate();

        var contexts = AuthorityPluginConfigurationLoader.Load(options, tempRoot);
        var diagnostics = AuthorityPluginConfigurationAnalyzer.Analyze(contexts);

        var diagnostic = Assert.Single(diagnostics);
        Assert.Equal(AuthorityConfigurationDiagnosticSeverity.Warning, diagnostic.Severity);
        Assert.Equal("standard", diagnostic.PluginName);
        Assert.Contains("minimum length 8", diagnostic.Message, StringComparison.OrdinalIgnoreCase);
        Assert.Contains("symbol requirement disabled", diagnostic.Message, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void Analyze_ReturnsNoDiagnostics_WhenPasswordPolicyMatchesBaseline()
    {
        var pluginDir = Path.Combine(tempRoot, "etc", "authority.plugins");
        Directory.CreateDirectory(pluginDir);

        var standardConfigPath = Path.Combine(pluginDir, "standard.yaml");
        // Baseline configuration (no overrides)
        File.WriteAllText(standardConfigPath, "bootstrapUser:\n  username: bootstrap\n  password: Bootstrap1!\n");

        var options = CreateOptions();
        options.Plugins.ConfigurationDirectory = "etc/authority.plugins";
        options.Plugins.Descriptors["standard"] = new AuthorityPluginDescriptorOptions
        {
            AssemblyName = "StellaOps.Authority.Plugin.Standard",
            Enabled = true
        };

        options.Validate();

        var contexts = AuthorityPluginConfigurationLoader.Load(options, tempRoot);
        var diagnostics = AuthorityPluginConfigurationAnalyzer.Analyze(contexts);

        Assert.Empty(diagnostics);
    }

    public void Dispose()
    {
        try
        {
            if (Directory.Exists(tempRoot))
            {
                Directory.Delete(tempRoot, recursive: true);
            }
        }
        catch
        {
            // ignore cleanup failures in test environment
        }
    }

    private static StellaOpsAuthorityOptions CreateOptions()
    {
        var options = new StellaOpsAuthorityOptions
        {
            Issuer = new Uri("https://authority.stella-ops.test"),
            SchemaVersion = 1
        };

        options.Storage.ConnectionString = "mongodb://localhost:27017/authority_test";
        options.Signing.ActiveKeyId = "test-key";
        options.Signing.KeyPath = "/tmp/authority-test-key.pem";
        return options;
    }
}
