using System.Collections.Concurrent;
using Akka.Actor;
using Akka.Cluster.Tools.PublishSubscribe;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Hubs;
using ZB.MOM.WW.OtOpcUa.Commons.Messages.Logging;
namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
///
/// E2E integration coverage for the ScriptLogSignalRBridge actor → in-process
/// broadcaster → Blazor /script-log page pipeline (Layer 0 of the script-log /
/// scripted-alarm runtime work).
///
/// Scope note: mirrors . The Blazor circuit
/// itself can't be exercised from an integration test (it needs an HTTP listener, JWT auth, and
/// a real WebSocket upgrade — covered by the manual runbook). This suite instead exercises the
/// exact server-side delivery the page depends on: it spawns a
/// in the harness actor system, publishes a
/// to the script-logs DPS topic, and asserts that the entry
/// reaches the singleton resolved from the SAME DI
/// container the /script-log page injects from (plus the SignalR hub push). This is the
/// link the existing (publisher → topic) and
/// (broadcaster → subscriber) tests do not cover
/// together: bridge subscription → broadcaster fan-out off the live topic.
///
[Trait("Category", "Integration")]
public sealed class ScriptLogHubE2eTests
{
private static CancellationToken Ct => TestContext.Current.CancellationToken;
///
/// Verifies that a published to the script-logs DPS
/// topic is forwarded by to (a) the
/// singleton the Blazor page reads and (b) the mock
/// via a SendAsync on the expected method.
///
[Fact]
public async Task ScriptLogBridge_ForwardsEntry_ToInProcessBroadcaster_FromDpsTopic()
{
await using var harness = await TwoNodeClusterHarness.StartAsync();
// The SAME singleton the Blazor /script-log page injects: AddAdminUI() →
// AddOtOpcUaDriverStatusServices() registers IInProcessBroadcaster<> as an open-generic
// singleton, so resolving the closed type here yields the page's instance.
var broadcaster = harness.NodeA.Services.GetRequiredService>();
var received = new ConcurrentQueue();
void Handler(ScriptLogEntry e) => received.Enqueue(e);
broadcaster.Received += Handler;
// Mock IHubContext: the bridge pushes to Clients.All in addition to the
// broadcaster (for any out-of-process SignalR client).
var hubCalls = new List<(string Method, object? Arg)>();
var mockClients = new Mock();
var mockClientProxy = new Mock();
mockClients.Setup(c => c.All).Returns(mockClientProxy.Object);
mockClientProxy
.Setup(p => p.SendCoreAsync(It.IsAny(), It.IsAny