using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Abstractions;
using StellaOps.Authority.Plugins.Abstractions;
using Xunit;
using System.Linq;

namespace StellaOps.Authority.Tests.Identity;

public class AuthorityIdentityProviderRegistryTests
{
    [Fact]
    public void RegistryIndexesProvidersAndAggregatesCapabilities()
    {
        var providers = new[]
        {
            CreateProvider("standard", type: "standard", supportsPassword: true, supportsMfa: false, supportsClientProvisioning: false),
            CreateProvider("sso", type: "saml", supportsPassword: false, supportsMfa: true, supportsClientProvisioning: true)
        };

        var registry = new AuthorityIdentityProviderRegistry(providers, NullLogger<AuthorityIdentityProviderRegistry>.Instance);

        Assert.Equal(2, registry.Providers.Count);
        Assert.True(registry.TryGet("standard", out var standard));
        Assert.Same(providers[0], standard);
        Assert.Single(registry.PasswordProviders);
        Assert.Single(registry.MfaProviders);
        Assert.Single(registry.ClientProvisioningProviders);
        Assert.True(registry.AggregateCapabilities.SupportsPassword);
        Assert.True(registry.AggregateCapabilities.SupportsMfa);
        Assert.True(registry.AggregateCapabilities.SupportsClientProvisioning);
    }

    [Fact]
    public void RegistryIgnoresDuplicateNames()
    {
        var duplicate = CreateProvider("standard", "ldap", supportsPassword: true, supportsMfa: false, supportsClientProvisioning: false);
        var providers = new[]
        {
            CreateProvider("standard", type: "standard", supportsPassword: true, supportsMfa: false, supportsClientProvisioning: false),
            duplicate
        };

        var registry = new AuthorityIdentityProviderRegistry(providers, NullLogger<AuthorityIdentityProviderRegistry>.Instance);

        Assert.Single(registry.Providers);
        Assert.Same(providers[0], registry.Providers.First());
        Assert.True(registry.TryGet("standard", out var provider));
        Assert.Same(providers[0], provider);
    }

    private static IIdentityProviderPlugin CreateProvider(
        string name,
        string type,
        bool supportsPassword,
        bool supportsMfa,
        bool supportsClientProvisioning)
    {
        var manifest = new AuthorityPluginManifest(
            name,
            type,
            true,
            AssemblyName: null,
            AssemblyPath: null,
            Capabilities: BuildCapabilities(supportsPassword, supportsMfa, supportsClientProvisioning),
            Metadata: new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase),
            ConfigPath: string.Empty);

        var context = new AuthorityPluginContext(manifest, new ConfigurationBuilder().Build());
        return new TestIdentityProviderPlugin(context, supportsPassword, supportsMfa, supportsClientProvisioning);
    }

    private static IReadOnlyList<string> BuildCapabilities(bool password, bool mfa, bool clientProvisioning)
    {
        var capabilities = new List<string>();
        if (password)
        {
            capabilities.Add(AuthorityPluginCapabilities.Password);
        }

        if (mfa)
        {
            capabilities.Add(AuthorityPluginCapabilities.Mfa);
        }

        if (clientProvisioning)
        {
            capabilities.Add(AuthorityPluginCapabilities.ClientProvisioning);
        }

        return capabilities;
    }

    private sealed class TestIdentityProviderPlugin : IIdentityProviderPlugin
    {
        public TestIdentityProviderPlugin(
            AuthorityPluginContext context,
            bool supportsPassword,
            bool supportsMfa,
            bool supportsClientProvisioning)
        {
            Context = context;
            Capabilities = new AuthorityIdentityProviderCapabilities(
                SupportsPassword: supportsPassword,
                SupportsMfa: supportsMfa,
                SupportsClientProvisioning: supportsClientProvisioning);
        }

        public string Name => Context.Manifest.Name;

        public string Type => Context.Manifest.Type;

        public AuthorityPluginContext Context { get; }

        public IUserCredentialStore Credentials => throw new NotImplementedException();

        public IClaimsEnricher ClaimsEnricher => throw new NotImplementedException();

        public IClientProvisioningStore? ClientProvisioning => null;

        public AuthorityIdentityProviderCapabilities Capabilities { get; }

        public ValueTask<AuthorityPluginHealthResult> CheckHealthAsync(CancellationToken cancellationToken)
            => ValueTask.FromResult(AuthorityPluginHealthResult.Healthy());
    }
}
