using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;

namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests;

public sealed class DeferredServiceLevelPublisherTests
{
    /// <summary>Verifies that publish before SetInner is a safe no-op.</summary>
    [Fact]
    public void Publish_before_SetInner_is_a_safe_noop()
    {
        var deferred = new DeferredServiceLevelPublisher();

        Should.NotThrow(() => deferred.Publish(123));
    }

    /// <summary>Verifies that publish after SetInner routes to the inner publisher.</summary>
    [Fact]
    public void Publish_after_SetInner_routes_to_the_inner()
    {
        var recording = new RecordingPublisher();
        var deferred = new DeferredServiceLevelPublisher();
        deferred.SetInner(recording);

        deferred.Publish(200);

        recording.LastValue.ShouldBe((byte)200);
    }

    /// <summary>Verifies that SetInner with null reverts to the null publisher.</summary>
    [Fact]
    public void SetInner_null_reverts_to_Null_publisher()
    {
        var recording = new RecordingPublisher();
        var deferred = new DeferredServiceLevelPublisher();
        deferred.SetInner(recording);
        deferred.Publish(50);

        deferred.SetInner(null);
        deferred.Publish(99);

        recording.LastValue.ShouldBe((byte)50, "writes after SetInner(null) must not reach the previous inner");
    }

    private sealed class RecordingPublisher : IServiceLevelPublisher
    {
        /// <summary>Gets the last published service level value.</summary>
        public byte? LastValue { get; private set; }
        /// <summary>Publishes a service level value.</summary>
        /// <param name="serviceLevel">The service level to publish.</param>
        public void Publish(byte serviceLevel) => LastValue = serviceLevel;
    }
}
