using Shouldly;
using Xunit;

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

/// <summary>
///     Driver.S7.Cli-009: every S7 CLI command that catches
///     <see cref="System.OperationCanceledException"/> must use the
///     <c>when (ct.IsCancellationRequested)</c> exception filter so that a
///     driver-internal timeout (thrown with a different cancellation token) does not get
///     mis-handled as a user Ctrl+C. Source-level check mirrors
///     <see cref="CommandDisposalConventionsTests"/>.
/// </summary>
[Trait("Category", "Unit")]
public sealed class CancellationHandlingTests
{
    private static readonly string CommandsDir = LocateCommandsDir();

    /// <summary>
    ///     Verifies that ProbeCommand uses the ct.IsCancellationRequested guard on its
    ///     OperationCanceledException catch block so a driver-internal timeout is not
    ///     swallowed as a user-cancelled operation.
    /// </summary>
    [Fact]
    public void ProbeCommand_uses_ct_IsCancellationRequested_guard()
    {
        // Driver.S7.Cli-009: the bare `catch (OperationCanceledException)` without a
        // `when` filter would mis-classify a driver-internal timeout as a Ctrl+C and
        // either re-throw from the wrong catch block or swallow the error.
        var source = File.ReadAllText(Path.Combine(CommandsDir, "ProbeCommand.cs"));
        source.ShouldContain("when (ct.IsCancellationRequested)");
    }

    /// <summary>
    ///     Verifies that ReadCommand provides quiet cancellation handling for Ctrl+C.
    /// </summary>
    [Fact]
    public void ReadCommand_handles_OperationCanceledException_quietly()
    {
        // Driver.S7.Cli-009: ReadCommand must catch OperationCanceledException and
        // print "Cancelled." (matching the Modbus CLI pattern) rather than letting
        // CliFx render an unhandled exception on Ctrl+C.
        var source = File.ReadAllText(Path.Combine(CommandsDir, "ReadCommand.cs"));
        source.ShouldContain("OperationCanceledException");
        source.ShouldContain("Cancelled.");
    }

    /// <summary>
    ///     Verifies that WriteCommand provides quiet cancellation handling for Ctrl+C.
    /// </summary>
    [Fact]
    public void WriteCommand_handles_OperationCanceledException_quietly()
    {
        // Driver.S7.Cli-009: WriteCommand must catch OperationCanceledException and
        // print "Cancelled." (matching the Modbus CLI pattern) rather than letting
        // CliFx render an unhandled exception on Ctrl+C.
        var source = File.ReadAllText(Path.Combine(CommandsDir, "WriteCommand.cs"));
        source.ShouldContain("OperationCanceledException");
        source.ShouldContain("Cancelled.");
    }

    private static string LocateCommandsDir()
    {
        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("Could not find solution root (ZB.MOM.WW.OtOpcUa.slnx).");
        return Path.Combine(
            dir!.FullName, "src", "Drivers", "Cli", "ZB.MOM.WW.OtOpcUa.Driver.S7.Cli", "Commands");
    }
}
