using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.Auth.Abstractions.ApiKeys;
using ZB.MOM.WW.Auth.ApiKeys.Sqlite;

namespace ZB.MOM.WW.Auth.ApiKeys.DependencyInjection;

/// <summary>
/// Dependency-injection helpers that wire up the ZB.MOM.WW API-key authentication provider
/// from configuration. These compose the SQLite-backed stores and the configuration-backed
/// pepper provider so a consuming app registers the verifier with a single call.
/// </summary>
public static class ApiKeyServiceCollectionExtensions
{
    /// <summary>
    /// Registers API-key authentication: binds <see cref="ApiKeyOptions"/> from the
    /// configuration section at <paramref name="sectionPath"/>, wires up the SQLite-backed
    /// stores and the configuration-backed pepper provider, and registers
    /// <see cref="IApiKeyVerifier"/>.
    /// </summary>
    /// <param name="services">The service collection to add to.</param>
    /// <param name="config">The application configuration.</param>
    /// <param name="sectionPath">Path of the configuration section holding the API-key options.</param>
    /// <returns>The same <paramref name="services"/> instance, for chaining.</returns>
    public static IServiceCollection AddZbApiKeyAuth(
        this IServiceCollection services,
        IConfiguration config,
        string sectionPath)
    {
        ArgumentNullException.ThrowIfNull(services);
        ArgumentNullException.ThrowIfNull(config);
        ArgumentException.ThrowIfNullOrWhiteSpace(sectionPath);

        services.Configure<ApiKeyOptions>(config.GetSection(sectionPath));

        // The pepper provider reads the live IConfiguration on each call. In an ASP.NET Core
        // host IConfiguration is already registered; register the caller-supplied instance here
        // (TryAdd, so the host's own registration wins when present) so the provider resolves
        // even in a bare ServiceCollection.
        services.TryAddSingleton(config);
        services.TryAddSingleton<IApiKeyPepperProvider, ConfigurationApiKeyPepperProvider>();

        // One connection factory targets the configured SQLite path. Singleton: it is
        // stateless aside from the path and opens a fresh connection per operation.
        services.TryAddSingleton(sp =>
            new AuthSqliteConnectionFactory(
                sp.GetRequiredService<IOptions<ApiKeyOptions>>().Value.SqlitePath));

        services.TryAddSingleton<IApiKeyStore>(sp =>
            new SqliteApiKeyStore(sp.GetRequiredService<AuthSqliteConnectionFactory>()));
        services.TryAddSingleton<IApiKeyAdminStore>(sp =>
            new SqliteApiKeyAdminStore(sp.GetRequiredService<AuthSqliteConnectionFactory>()));
        services.TryAddSingleton<IApiKeyAuditStore>(sp =>
            new SqliteApiKeyAuditStore(sp.GetRequiredService<AuthSqliteConnectionFactory>()));

        services.TryAddSingleton<IApiKeyVerifier>(sp =>
            new ApiKeyVerifier(
                sp.GetRequiredService<IOptions<ApiKeyOptions>>().Value,
                sp.GetRequiredService<IApiKeyStore>(),
                sp.GetRequiredService<IApiKeyPepperProvider>()));

        // Migrator: singleton, constructed from the already-registered connection factory.
        // Needed before any store operations so the schema exists.
        services.TryAddSingleton(sp =>
            new SqliteAuthStoreMigrator(sp.GetRequiredService<AuthSqliteConnectionFactory>()));

        // Hosted service that runs migrations on startup when ApiKeyOptions.RunMigrationsOnStartup.
        services.AddHostedService<ApiKeyMigrationHostedService>();

        return services;
    }
}
