using Shouldly;
using Xunit;

namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli.Tests;

/// <summary>
///     Driver.FOCAS.Cli-002: the FOCAS <c>subscribe</c> command — a near-verbatim copy
///     of the Modbus subscribe command — must
///     (a) serialise writes from the <c>OnDataChange</c> handler (raised from the
///         driver's <c>PollGroupEngine</c> background thread) with a lock, so the
///         "Subscribed to ..." banner write from the CliFx main thread cannot interleave
///         with the first poll-driven change line; and
///     (b) carry the explanatory comment that documents why <c>OnDataChange</c> uses
///         <c>console.Output.WriteLine</c> (synchronous, on a driver background thread)
///         instead of <c>System.Console</c> or the async <c>WriteLineAsync</c>. The
///         rationale is non-obvious to a reader and the Modbus copy carries it; the FOCAS
///         copy must too.
/// </summary>
[Trait("Category", "Unit")]
public sealed class SubscribeCommandConsoleHandlerTests
{
    private static string ReadSubscribeSource()
    {
        var dir = new DirectoryInfo(AppContext.BaseDirectory);
        while (dir is not null && !File.Exists(Path.Combine(dir.FullName, "ZB.MOM.WW.OtOpcUa.slnx")))
            dir = dir.Parent;
        dir.ShouldNotBeNull();
        return File.ReadAllText(Path.Combine(
            dir!.FullName, "src", "Drivers", "Cli", "ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Cli",
            "Commands", "SubscribeCommand.cs"));
    }

    /// <summary>Verifies that SubscribeCommand documents why OnDataChange uses synchronous console output.</summary>
    [Fact]
    public void SubscribeCommand_explains_why_OnDataChange_uses_console_Output_synchronously()
    {
        var source = ReadSubscribeSource();

        // The comment must reference the CliFx console abstraction so future copy-pastes
        // do not lose the rationale.
        source.ShouldContain("CliFx console");
        source.ShouldContain("IConsole");
    }

    /// <summary>Verifies that SubscribeCommand serializes console writes with a lock.</summary>
    [Fact]
    public void SubscribeCommand_serialises_console_writes_with_a_lock()
    {
        var source = ReadSubscribeSource();

        // Both the banner write and the OnDataChange handler must share a writeLock so the
        // banner from the CliFx invocation thread cannot interleave with the first
        // poll-driven change line from the driver tick thread.
        source.ShouldContain("writeLock");
        source.ShouldContain("lock (writeLock)");
    }
}
