using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using StellaOps.Cryptography;

namespace StellaOps.Cryptography.DependencyInjection;

/// <summary>
/// Dependency injection helpers for registering StellaOps cryptography services.
/// </summary>
public static class CryptoServiceCollectionExtensions
{
    /// <summary>
    /// Registers the default crypto provider and registry.
    /// </summary>
    /// <param name="services">Service collection.</param>
    /// <param name="configureRegistry">Optional registry ordering configuration.</param>
    /// <param name="configureProvider">Optional provider-level configuration (e.g. key registration).</param>
    /// <returns>The service collection.</returns>
    public static IServiceCollection AddStellaOpsCrypto(
        this IServiceCollection services,
        Action<CryptoProviderRegistryOptions>? configureRegistry = null,
        Action<DefaultCryptoProvider>? configureProvider = null)
    {
        ArgumentNullException.ThrowIfNull(services);

        if (configureRegistry is not null)
        {
            services.Configure(configureRegistry);
        }

        services.TryAddSingleton<DefaultCryptoProvider>(sp =>
        {
            var provider = new DefaultCryptoProvider();
            configureProvider?.Invoke(provider);
            return provider;
        });

        services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, DefaultCryptoProvider>());

#if STELLAOPS_CRYPTO_SODIUM
        services.TryAddSingleton<LibsodiumCryptoProvider>();
        services.TryAddEnumerable(ServiceDescriptor.Singleton<ICryptoProvider, LibsodiumCryptoProvider>());
#endif

        services.TryAddSingleton<ICryptoProviderRegistry>(sp =>
        {
            var providers = sp.GetServices<ICryptoProvider>();
            var options = sp.GetService<IOptions<CryptoProviderRegistryOptions>>();
            IEnumerable<string>? preferred = options?.Value?.PreferredProviders;
            return new CryptoProviderRegistry(providers, preferred);
        });

        return services;
    }
}
