using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Health;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests.Health;

/// <summary>
///     Tests for <see cref="HostConnectivityForwarder"/>'s push path. The forwarder is a
///     thin shim over <see cref="HostStatusAggregator"/>; the only invariants worth pinning
///     are that SetTransport routes correctly under the configured client name and that
///     repeated identical pushes don't produce duplicate change events (the aggregator's
///     dedup carries that — this test asserts the forwarder doesn't re-introduce them).
/// </summary>
public sealed class HostConnectivityForwarderTests
{
    /// <summary>Verifies that SetTransport pushes the state change under the client name.</summary>
    [Fact]
    public void SetTransport_Running_PushesUnderClientName()
    {
        var agg = new HostStatusAggregator();
        var captured = new List<HostStatusChangedEventArgs>();
        agg.OnHostStatusChanged += (_, e) => captured.Add(e);

        var fwd = new HostConnectivityForwarder("OtOpcUa-A", agg);
        fwd.SetTransport(HostState.Running);

        captured.Count.ShouldBe(1);
        captured[0].HostName.ShouldBe("OtOpcUa-A");
        captured[0].NewState.ShouldBe(HostState.Running);
        agg.Snapshot()[0].HostName.ShouldBe("OtOpcUa-A");
    }

    /// <summary>Verifies that SetTransport fires a change event on state transitions.</summary>
    [Fact]
    public void SetTransport_StateTransition_FiresChange()
    {
        var agg = new HostStatusAggregator();
        var fwd = new HostConnectivityForwarder("OtOpcUa-A", agg);

        fwd.SetTransport(HostState.Running);
        var captured = new List<HostStatusChangedEventArgs>();
        agg.OnHostStatusChanged += (_, e) => captured.Add(e);

        fwd.SetTransport(HostState.Stopped);

        captured.Count.ShouldBe(1);
        captured[0].OldState.ShouldBe(HostState.Running);
        captured[0].NewState.ShouldBe(HostState.Stopped);
    }

    /// <summary>Verifies that repeated identical state changes do not fire events.</summary>
    [Fact]
    public void SetTransport_RepeatedSameState_DoesNotFire()
    {
        var agg = new HostStatusAggregator();
        var fwd = new HostConnectivityForwarder("OtOpcUa-A", agg);

        fwd.SetTransport(HostState.Running);
        var captured = new List<HostStatusChangedEventArgs>();
        agg.OnHostStatusChanged += (_, e) => captured.Add(e);

        fwd.SetTransport(HostState.Running);
        fwd.SetTransport(HostState.Running);
        fwd.SetTransport(HostState.Running);

        captured.ShouldBeEmpty();
    }

    /// <summary>Verifies that the constructor rejects empty client names.</summary>
    [Fact]
    public void Constructor_RejectsEmptyClientName()
    {
        var agg = new HostStatusAggregator();
        Should.Throw<ArgumentException>(() => new HostConnectivityForwarder("", agg));
        Should.Throw<ArgumentException>(() => new HostConnectivityForwarder("   ", agg));
    }

    /// <summary>Verifies that SetTransport throws after the forwarder is disposed.</summary>
    [Fact]
    public void SetTransport_AfterDispose_Throws()
    {
        var agg = new HostStatusAggregator();
        var fwd = new HostConnectivityForwarder("OtOpcUa-A", agg);
        fwd.Dispose();
        Should.Throw<ObjectDisposedException>(() => fwd.SetTransport(HostState.Running));
    }
}
