using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ZB.MOM.WW.Secrets.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Host.Configuration;
///
/// Forces this node's secret-replication actor into existence at startup.
///
///
///
/// The replication actor is created lazily, on the first resolution of
/// — resolving the store builds ReplicatingSecretStore,
/// which takes ISecretReplicator, whose factory touches
/// SecretReplicationActorProvider.ActorRef and thereby spawns the actor.
///
///
/// Laziness is correct for the library (the ActorSystem 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 ${secret:} 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.
///
///
/// History — this hook has met two library defects, both fixed. Against 0.2.0 it was
/// inert: the package never bound its own ISecretReplicator (TryAdd registration
/// order), so this resolve built a ReplicatingSecretStore around a no-op sink and
/// spawned no actor. Against 0.2.1 it was worse: the resolve deadlocked the host at
/// startup — 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;
/// SecretsReplicationRegistrationTests.The_startup_hook_actually_creates_the_replication_actor
/// exercises this hook against a built provider on a real single-node cluster.
///
///
/// Root provider used to resolve the (decorated) secret store exactly once.
public sealed class SecretReplicationStarter(IServiceProvider services) : IHostedService
{
/// Resolves , which spawns the replication actor.
/// Unused — resolution is synchronous and non-blocking.
/// A completed task.
public Task StartAsync(CancellationToken cancellationToken)
{
// The resolution itself is the side effect; the instance is deliberately unused.
_ = services.GetRequiredService();
return Task.CompletedTask;
}
/// No-op: the actor's lifetime is the actor system's.
/// Unused.
/// A completed task.
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}