using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ZB.MOM.WW.Secrets.DependencyInjection;
using ZB.MOM.WW.Secrets.Replicator.AkkaDotNet.DependencyInjection;

namespace ZB.MOM.WW.OtOpcUa.Host.Configuration;

/// <summary>
///     Single registration point for the secrets subsystem on every OtOpcUa node, admin- and
///     driver-role alike. Exists as a named extension rather than an inline <c>AddZbSecrets</c> call
///     in <c>Program.cs</c> so the choice of <c>ISecretStore</c> implementation is covered by a DI
///     resolution test — <c>Program.cs</c> is top-level statements and cannot be exercised directly,
///     which is exactly how a "registered but never resolvable" wiring defect ships unnoticed.
/// </summary>
public static class SecretsRegistration
{
    /// <summary>Configuration key gating cluster secret replication. Absent or false = off.</summary>
    public const string ReplicationEnabledKey = "Secrets:Replication:Enabled";

    /// <summary>Configuration section holding <c>AkkaSecretsReplicationOptions</c>.</summary>
    public const string ReplicationSectionPath = "Secrets:Replication";

    /// <summary>Configuration section holding the core secrets options.</summary>
    public const string SecretsSectionPath = "Secrets";

    /// <summary>
    ///     True when cluster secret replication is switched on in configuration. Defaults to false —
    ///     absent configuration must mean "off".
    /// </summary>
    /// <param name="configuration">The application configuration.</param>
    /// <returns><c>true</c> when replication is enabled.</returns>
    public static bool IsReplicationEnabled(IConfiguration configuration)
    {
        ArgumentNullException.ThrowIfNull(configuration);
        return configuration.GetValue(ReplicationEnabledKey, defaultValue: false);
    }

    /// <summary>
    ///     Registers the secrets subsystem. Replication is <b>opt-in</b>: unless
    ///     <see cref="ReplicationEnabledKey"/> is true this is exactly the plain local SQLite wiring
    ///     the host has always used.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         The gate is deliberately default-deny. This call decides which <c>ISecretStore</c> the
    ///         container hands to <b>every</b> node, including driver-role nodes with no auth or
    ///         AdminUI surface, where a bad store surfaces as drivers failing to open sessions rather
    ///         than as a failing test. Enabling replication by default would change secret resolution
    ///         across a production fleet with nobody having asked for it.
    ///     </para>
    ///     <para>
    ///         <c>AddZbSecretsAkkaReplication</c> calls <c>AddZbSecrets</c> internally, so the two
    ///         branches are alternatives, never additive — calling both would double-register.
    ///     </para>
    /// </remarks>
    /// <param name="services">The service collection to add to.</param>
    /// <param name="configuration">The application configuration.</param>
    /// <returns>The same <paramref name="services"/> instance, for chaining.</returns>
    public static IServiceCollection AddOtOpcUaSecrets(
        this IServiceCollection services,
        IConfiguration configuration)
    {
        ArgumentNullException.ThrowIfNull(services);
        ArgumentNullException.ThrowIfNull(configuration);

        if (!IsReplicationEnabled(configuration))
        {
            services.AddZbSecrets(configuration, SecretsSectionPath);
            return services;
        }

        services.AddZbSecretsAkkaReplication(configuration, SecretsSectionPath, ReplicationSectionPath);

        // Anti-entropy participation must not hinge on this node happening to touch a secret — see
        // SecretReplicationStarter for why the library's lazy actor creation is insufficient here.
        services.AddSingleton<SecretReplicationStarter>();
        services.AddHostedService(sp => sp.GetRequiredService<SecretReplicationStarter>());

        return services;
    }
}
