using Akka.Actor;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Mesh;

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

/// <summary>
///     Pins the two mesh comm-actor paths against a <b>really booted</b> host.
/// </summary>
/// <remarks>
///     <para>
///         <c>/user/central-communication</c> and <c>/user/node-communication</c> are the wire
///         contract of the central↔node boundary: central sends to a path string, and the node
///         registers under one. Nothing else in the codebase asserts they agree. Renaming either
///         actor would compile, pass every unit test, deploy — and deliver nothing, with no error
///         on either side, because a ClusterClient send to an unregistered path is simply dropped.
///     </para>
///     <para>
///         Booting the real host is the point. A test that spawned the actors itself would pin the
///         constant against itself and prove nothing about the wiring in
///         <c>WithOtOpcUaControlPlaneSingletons</c> / <c>WithOtOpcUaRuntimeActors</c>.
///     </para>
/// </remarks>
public class MeshCommActorPathTests
{
    private static async Task AssertActorExistsAsync(ActorSystem system, string path)
    {
        var identity = await system.ActorSelection(path)
            .Ask<ActorIdentity>(new Identify(path), TimeSpan.FromSeconds(10));

        identity.Subject.ShouldNotBeNull(
            $"{path} is the ClusterClient wire contract for the central↔node boundary; nothing else "
            + "pins it, and a mismatch delivers nothing without erroring on either side");
    }

    [Fact]
    public async Task Both_mesh_comm_actors_exist_at_their_contract_paths()
    {
        await using var harness = await TwoNodeClusterHarness.StartAsync();

        // Harness nodes carry admin+driver, so BOTH comm actors live on each of them.
        await AssertActorExistsAsync(harness.NodeASystem, MeshPaths.CentralCommunication);
        await AssertActorExistsAsync(harness.NodeASystem, MeshPaths.NodeCommunication);
        await AssertActorExistsAsync(harness.NodeBSystem, MeshPaths.CentralCommunication);
        await AssertActorExistsAsync(harness.NodeBSystem, MeshPaths.NodeCommunication);
    }

    // NOT TESTED HERE: that the comm actors are registered per node rather than as cluster
    // singletons. Two candidate discriminators were tried and BOTH were invalid:
    //
    //   * "the path resolves on both nodes" — a ClusterSingletonManager is also created on every
    //     node in the role, so this passes under either shape.
    //   * "no /singleton child exists" — measured against the KNOWN singleton /user/config-publish,
    //     that child is null there too, so Akka.Hosting does not lay singletons out this way and
    //     the assertion proves nothing.
    //
    // A sabotage that re-registered CentralCommunicationActor via WithSingleton left both green,
    // which is what exposed them. Rather than ship an assertion I could not make fail, the property
    // is left to live-gate step 8 (stop the OLDEST central; the survivor's comm actor must still
    // receive a node ack, which a singleton hosted on the stopped node cannot).
    //
    // NOT MeshClusterClientBoundaryTests: that class proves a send only arrives if the target is
    // registered with the receptionist, which is necessary but not discriminating — with one node
    // per side, per-node and singleton registration look identical.
}
