using CliFx.Attributes;
using CliFx.Exceptions;
using CliFx.Infrastructure;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.S7;
using ZB.MOM.WW.OtOpcUa.Driver.S7.Cli;

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

/// <summary>
///     Driver.S7.Cli-008: S7CommandBase must reject out-of-range port and non-positive
///     timeout values before the command body opens a driver — matching the validation pattern
///     established by ModbusCommandBase.ValidateEndpoint.
/// </summary>
[Trait("Category", "Unit")]
public sealed class S7EndpointValidationTests
{
    // Test-only subclass that exposes ValidateEndpoint and delegates the abstract ExecuteAsync.
    [Command("noop-validate", Description = "Test-only stub for S7CommandBase.ValidateEndpoint.")]
    private sealed class ValidateStub : S7CommandBase
    {
        /// <inheritdoc />
        public override ValueTask ExecuteAsync(IConsole console) => default;

        /// <summary>Exposes the protected ValidateEndpoint method for tests.</summary>
        public void InvokeValidate() => ValidateEndpoint();
    }

    /// <summary>Verifies that a valid port and timeout passes validation without throwing.</summary>
    [Fact]
    public void ValidateEndpoint_valid_inputs_pass()
    {
        // Driver.S7.Cli-008: verify the happy path does not throw.
        var sut = new ValidateStub { Host = "plc.local", Port = 102, TimeoutMs = 5000 };
        Should.NotThrow(() => sut.InvokeValidate());
    }

    /// <summary>Verifies that port 0 is rejected as out of range.</summary>
    [Theory]
    [InlineData(0)]
    [InlineData(-1)]
    [InlineData(65536)]
    public void ValidateEndpoint_invalid_port_throws_CommandException(int port)
    {
        // Driver.S7.Cli-008: port must be 1..65535.
        var sut = new ValidateStub { Host = "h", Port = port, TimeoutMs = 5000 };
        Should.Throw<CommandException>(() => sut.InvokeValidate())
            .Message.ShouldContain("--port");
    }

    /// <summary>Verifies that a non-positive timeout is rejected.</summary>
    [Theory]
    [InlineData(0)]
    [InlineData(-1)]
    public void ValidateEndpoint_non_positive_timeout_throws_CommandException(int timeoutMs)
    {
        // Driver.S7.Cli-008: timeout must be strictly positive.
        var sut = new ValidateStub { Host = "h", Port = 102, TimeoutMs = timeoutMs };
        Should.Throw<CommandException>(() => sut.InvokeValidate())
            .Message.ShouldContain("--timeout-ms");
    }

    /// <summary>Verifies that the boundary port values 1 and 65535 pass validation.</summary>
    [Theory]
    [InlineData(1)]
    [InlineData(65535)]
    public void ValidateEndpoint_boundary_ports_pass(int port)
    {
        var sut = new ValidateStub { Host = "h", Port = port, TimeoutMs = 1000 };
        Should.NotThrow(() => sut.InvokeValidate());
    }
}
