using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ZB.MOM.WW.Secrets.Abstractions;

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

/// <summary>
///     Forces this node's secret-replication actor into existence at startup.
/// </summary>
/// <remarks>
///     <para>
///         The replication actor is created <b>lazily</b>, on the first resolution of
///         <see cref="ISecretStore"/> — resolving the store builds <c>ReplicatingSecretStore</c>,
///         which takes <c>ISecretReplicator</c>, whose factory touches
///         <c>SecretReplicationActorProvider.ActorRef</c> and thereby spawns the actor.
///     </para>
///     <para>
///         Laziness is correct for the library (the <c>ActorSystem</c> is usually registered after
///         the replication call), but it makes participation in anti-entropy accidental: a node that
///         happens never to read or write a secret — a plausible steady state for a driver-role node
///         whose driver configs carry no <c>${secret:}</c> references — would never create the actor,
///         never announce its manifest, and never converge. Nothing would fail; it would just
///         silently not replicate. Resolving the store once at startup makes participation
///         unconditional.
///     </para>
///     <para>
///         <b>History — this hook has met two library defects, both fixed.</b> Against 0.2.0 it was
///         inert: the package never bound its own <c>ISecretReplicator</c> (TryAdd registration
///         order), so this resolve built a <c>ReplicatingSecretStore</c> around a no-op sink and
///         spawned no actor. Against 0.2.1 it was worse: the resolve <b>deadlocked the host at
///         startup</b> — the package's DI graph had a circular singleton dependency, invisible to
///         the container through factory lambdas (scadaproj#1). Fixed in 0.2.2 by deferring the
///         cycle-closing invalidator edge;
///         <c>SecretsReplicationRegistrationTests.The_startup_hook_actually_creates_the_replication_actor</c>
///         exercises this hook against a built provider on a real single-node cluster.
///     </para>
/// </remarks>
/// <param name="services">Root provider used to resolve the (decorated) secret store exactly once.</param>
public sealed class SecretReplicationStarter(IServiceProvider services) : IHostedService
{
    /// <summary>Resolves <see cref="ISecretStore"/>, which spawns the replication actor.</summary>
    /// <param name="cancellationToken">Unused — resolution is synchronous and non-blocking.</param>
    /// <returns>A completed task.</returns>
    public Task StartAsync(CancellationToken cancellationToken)
    {
        // The resolution itself is the side effect; the instance is deliberately unused.
        _ = services.GetRequiredService<ISecretStore>();
        return Task.CompletedTask;
    }

    /// <summary>No-op: the actor's lifetime is the actor system's.</summary>
    /// <param name="cancellationToken">Unused.</param>
    /// <returns>A completed task.</returns>
    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
