using Microsoft.Extensions.Configuration;
using OpenIddict.Abstractions;
using StellaOps.Authority.OpenIddict;
using StellaOps.Authority.Plugins.Abstractions;
using Xunit;

namespace StellaOps.Authority.Tests.Identity;

public class AuthorityIdentityProviderSelectorTests
{
    [Fact]
    public void ResolvePasswordProvider_UsesSingleProviderWhenNoParameter()
    {
        var registry = CreateRegistry(passwordProviders: new[] { CreateProvider("standard", supportsPassword: true) });
        var request = new OpenIddictRequest();

        var result = AuthorityIdentityProviderSelector.ResolvePasswordProvider(request, registry);

        Assert.True(result.Succeeded);
        Assert.Equal("standard", result.Provider!.Name);
    }

    [Fact]
    public void ResolvePasswordProvider_FailsWhenNoProviders()
    {
        var registry = CreateRegistry(passwordProviders: Array.Empty<IIdentityProviderPlugin>());
        var request = new OpenIddictRequest();

        var result = AuthorityIdentityProviderSelector.ResolvePasswordProvider(request, registry);

        Assert.False(result.Succeeded);
        Assert.Equal(OpenIddictConstants.Errors.UnsupportedGrantType, result.Error);
    }

    [Fact]
    public void ResolvePasswordProvider_RequiresParameterWhenMultipleProviders()
    {
        var registry = CreateRegistry(passwordProviders: new[]
        {
            CreateProvider("standard", supportsPassword: true),
            CreateProvider("ldap", supportsPassword: true)
        });
        var request = new OpenIddictRequest();

        var result = AuthorityIdentityProviderSelector.ResolvePasswordProvider(request, registry);

        Assert.False(result.Succeeded);
        Assert.Equal(OpenIddictConstants.Errors.InvalidRequest, result.Error);
    }

    [Fact]
    public void ResolvePasswordProvider_HonoursProviderParameter()
    {
        var registry = CreateRegistry(passwordProviders: new[]
        {
            CreateProvider("standard", supportsPassword: true),
            CreateProvider("ldap", supportsPassword: true)
        });
        var request = new OpenIddictRequest();
        request.SetParameter(AuthorityOpenIddictConstants.ProviderParameterName, "ldap");

        var result = AuthorityIdentityProviderSelector.ResolvePasswordProvider(request, registry);

        Assert.True(result.Succeeded);
        Assert.Equal("ldap", result.Provider!.Name);
    }

    private static AuthorityIdentityProviderRegistry CreateRegistry(IEnumerable<IIdentityProviderPlugin> passwordProviders)
    {
        var providers = passwordProviders.ToList<IIdentityProviderPlugin>();
        return new AuthorityIdentityProviderRegistry(providers, Microsoft.Extensions.Logging.Abstractions.NullLogger<AuthorityIdentityProviderRegistry>.Instance);
    }

    private static IIdentityProviderPlugin CreateProvider(string name, bool supportsPassword)
    {
        var manifest = new AuthorityPluginManifest(
            name,
            "standard",
            true,
            AssemblyName: null,
            AssemblyPath: null,
            Capabilities: supportsPassword ? new[] { AuthorityPluginCapabilities.Password } : Array.Empty<string>(),
            Metadata: new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase),
            ConfigPath: string.Empty);

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

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

        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());
    }
}
