using Shouldly;
using Xunit;

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

/// <summary>
///     Coverage for the new ByteSwap (BADC) and FullReverse (DCBA) byte orders added by #137.
///     The existing BigEndian (ABCD) and WordSwap (CDAB) cases live in <see cref="ModbusDataTypeTests"/>.
/// </summary>
[Trait("Category", "Unit")]
public sealed class ModbusByteOrderTests
{
    /// <summary>Verifies that Int32 values decode correctly with ByteSwap (BADC) byte order.</summary>
    [Fact]
    public void Int32_ByteSwap_decodes_BADC_layout()
    {
        // Value 0x12345678. PLC stores bytes within each register swapped:
        // register[0] = 0x3412, register[1] = 0x7856 → wire [0x34, 0x12, 0x78, 0x56].
        var tag = new ModbusTagDefinition("T", ModbusRegion.HoldingRegisters, 0, ModbusDataType.Int32,
            ByteOrder: ModbusByteOrder.ByteSwap);
        var bytes = new byte[] { 0x34, 0x12, 0x78, 0x56 };
        ModbusDriver.DecodeRegister(bytes, tag).ShouldBe(0x12345678);
    }

    /// <summary>Verifies that Int32 values decode correctly with FullReverse (DCBA) byte order.</summary>
    [Fact]
    public void Int32_FullReverse_decodes_DCBA_layout()
    {
        // Value 0x12345678 stored fully little-endian:
        // wire [0x78, 0x56, 0x34, 0x12].
        var tag = new ModbusTagDefinition("T", ModbusRegion.HoldingRegisters, 0, ModbusDataType.Int32,
            ByteOrder: ModbusByteOrder.FullReverse);
        var bytes = new byte[] { 0x78, 0x56, 0x34, 0x12 };
        ModbusDriver.DecodeRegister(bytes, tag).ShouldBe(0x12345678);
    }

    /// <summary>Verifies that Float32 values roundtrip correctly with all byte orders.</summary>
    /// <param name="order">The byte order to test.</param>
    [Theory]
    [InlineData(ModbusByteOrder.BigEndian)]
    [InlineData(ModbusByteOrder.WordSwap)]
    [InlineData(ModbusByteOrder.ByteSwap)]
    [InlineData(ModbusByteOrder.FullReverse)]
    public void Float32_All_ByteOrders_Roundtrip(ModbusByteOrder order)
    {
        var tag = new ModbusTagDefinition("T", ModbusRegion.HoldingRegisters, 0, ModbusDataType.Float32, ByteOrder: order);
        var wire = ModbusDriver.EncodeRegister(3.14159f, tag);
        wire.Length.ShouldBe(4);
        ModbusDriver.DecodeRegister(wire, tag).ShouldBe(3.14159f);
    }

    /// <summary>Verifies that Float64 values roundtrip correctly with all byte orders.</summary>
    /// <param name="order">The byte order to test.</param>
    [Theory]
    [InlineData(ModbusByteOrder.BigEndian)]
    [InlineData(ModbusByteOrder.WordSwap)]
    [InlineData(ModbusByteOrder.ByteSwap)]
    [InlineData(ModbusByteOrder.FullReverse)]
    public void Float64_All_ByteOrders_Roundtrip(ModbusByteOrder order)
    {
        var tag = new ModbusTagDefinition("T", ModbusRegion.HoldingRegisters, 0, ModbusDataType.Float64, ByteOrder: order);
        var wire = ModbusDriver.EncodeRegister(2.718281828459045d, tag);
        wire.Length.ShouldBe(8);
        ModbusDriver.DecodeRegister(wire, tag).ShouldBe(2.718281828459045d);
    }

    /// <summary>Verifies that Int32 values roundtrip correctly with all byte orders.</summary>
    /// <param name="order">The byte order to test.</param>
    [Theory]
    [InlineData(ModbusByteOrder.BigEndian)]
    [InlineData(ModbusByteOrder.WordSwap)]
    [InlineData(ModbusByteOrder.ByteSwap)]
    [InlineData(ModbusByteOrder.FullReverse)]
    public void Int32_All_ByteOrders_Roundtrip(ModbusByteOrder order)
    {
        var tag = new ModbusTagDefinition("T", ModbusRegion.HoldingRegisters, 0, ModbusDataType.Int32, ByteOrder: order);
        var wire = ModbusDriver.EncodeRegister(unchecked((int)0xDEADBEEF), tag);
        wire.Length.ShouldBe(4);
        ModbusDriver.DecodeRegister(wire, tag).ShouldBe(unchecked((int)0xDEADBEEF));
    }
}
