using CliFx.Infrastructure;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.Modbus.Cli.Commands;

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

/// <summary>
///     Covers the branch validation inside <see cref="WriteCommand.ExecuteAsync"/>:
///     1. Driver.Modbus.Cli-002 — write to <see cref="ModbusRegion.Coils"/> must use
///        <c>--type Bool</c>.
///     2. Read-only regions (DiscreteInputs / InputRegisters) reject any write.
///     The actual driver call is never reached for these guard cases — they throw a
///     <see cref="CliFx.Exceptions.CommandException"/> before the driver is constructed,
///     so we can exercise <c>ExecuteAsync</c> against an unreachable host.
/// </summary>
[Trait("Category", "Unit")]
public sealed class WriteCommandRegionValidationTests
{
    /// <summary>Verifies that write to read-only regions is rejected before reaching the driver.</summary>
    /// <param name="region">The read-only Modbus region to attempt a write against.</param>
    /// <param name="type">The data type used in the write attempt.</param>
    /// <param name="value">The raw string value supplied to the write command.</param>
    [Theory]
    [InlineData(ModbusRegion.DiscreteInputs, ModbusDataType.Bool, "0")]
    [InlineData(ModbusRegion.InputRegisters, ModbusDataType.UInt16, "1")]
    public async Task ExecuteAsync_rejects_read_only_regions(
        ModbusRegion region, ModbusDataType type, string value)
    {
        var sut = new WriteCommand
        {
            // Host is required, but the guard fires before any socket use.
            Host = "127.0.0.1",
            Region = region,
            Address = 0,
            DataType = type,
            Value = value,
        };
        using var console = new FakeInMemoryConsole();

        await Should.ThrowAsync<CliFx.Exceptions.CommandException>(
            async () => await sut.ExecuteAsync(console));
    }

    /// <summary>Verifies that Coils region requires Bool data type (Driver.Modbus.Cli-002).</summary>
    /// <param name="type">The non-Bool data type that should be rejected for the Coils region.</param>
    [Theory]
    [InlineData(ModbusDataType.UInt16)]
    [InlineData(ModbusDataType.Int16)]
    [InlineData(ModbusDataType.Float32)]
    [InlineData(ModbusDataType.Int32)]
    public async Task ExecuteAsync_rejects_non_Bool_type_for_Coils_region(ModbusDataType type)
    {
        var sut = new WriteCommand
        {
            Host = "127.0.0.1",
            Region = ModbusRegion.Coils,
            Address = 5,
            DataType = type,
            Value = "42",
        };
        using var console = new FakeInMemoryConsole();

        var ex = await Should.ThrowAsync<CliFx.Exceptions.CommandException>(
            async () => await sut.ExecuteAsync(console));
        ex.Message.ShouldContain("Coils");
        ex.Message.ShouldContain("Bool");
    }
}
