using OpenIddict.Abstractions;
using StellaOps.Authority.Plugins.Abstractions;

namespace StellaOps.Authority.OpenIddict;

internal static class AuthorityIdentityProviderSelector
{
    public static ProviderSelectionResult ResolvePasswordProvider(OpenIddictRequest request, IAuthorityIdentityProviderRegistry registry)
    {
        ArgumentNullException.ThrowIfNull(request);
        ArgumentNullException.ThrowIfNull(registry);

        if (registry.PasswordProviders.Count == 0)
        {
            return ProviderSelectionResult.Failure(
                OpenIddictConstants.Errors.UnsupportedGrantType,
                "Password grants are not enabled because no identity providers support password authentication.");
        }

        var providerName = request.GetParameter(AuthorityOpenIddictConstants.ProviderParameterName)?.Value?.ToString();
        if (string.IsNullOrWhiteSpace(providerName))
        {
            if (registry.PasswordProviders.Count == 1)
            {
                var provider = registry.PasswordProviders.First();
                return ProviderSelectionResult.Success(provider);
            }

            return ProviderSelectionResult.Failure(
                OpenIddictConstants.Errors.InvalidRequest,
                "identity_provider parameter is required when multiple password-capable providers are registered.");
        }

        if (!registry.TryGet(providerName!, out var selected))
        {
            return ProviderSelectionResult.Failure(
                OpenIddictConstants.Errors.InvalidRequest,
                $"Unknown identity provider '{providerName}'.");
        }

        if (!selected.Capabilities.SupportsPassword)
        {
            return ProviderSelectionResult.Failure(
                OpenIddictConstants.Errors.InvalidRequest,
                $"Identity provider '{providerName}' does not support password authentication.");
        }

        return ProviderSelectionResult.Success(selected);
    }

    internal sealed record ProviderSelectionResult(
        bool Succeeded,
        IIdentityProviderPlugin? Provider,
        string? Error,
        string? Description)
    {
        public static ProviderSelectionResult Success(IIdentityProviderPlugin provider)
            => new(true, provider, null, null);

        public static ProviderSelectionResult Failure(string error, string description)
            => new(false, null, error, description);
    }
}
