using Shouldly;
using Xunit;

namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205;

/// <summary>
///     Verifies the DL205/DL260 low-byte-first ASCII string packing quirk against the
///     <c>dl205.json</c> pymodbus profile. Standard Modbus packs the first char of each pair
///     in the high byte of the register; DirectLOGIC packs it in the low byte instead. Without
///     <see cref="ModbusStringByteOrder.LowByteFirst"/> the driver decodes "eHllo" garbage
///     even though the bytes on the wire are identical.
/// </summary>
/// <remarks>
///     <para>
///         Requires the dl205 profile (<c>docker compose -f Docker/docker-compose.yml --profile dl205 up</c>). The standard
///         profile does not seed HR[1040..1042] with string bytes, so running this against the
///         standard profile returns <c>"\0\0\0\0\0"</c> and the test fails. Skip when the env
///         var <c>MODBUS_SIM_PROFILE</c> is not set to <c>dl205</c>.
///     </para>
/// </remarks>
[Collection(ModbusSimulatorCollection.Name)]
[Trait("Category", "Integration")]
[Trait("Device", "DL205")]
public sealed class DL205StringQuirkTests(ModbusSimulatorFixture sim)
{
    /// <summary>Verifies that DL205 low-byte-first string packing correctly decodes "Hello" from HR1040.</summary>
    [Fact]
    public async Task DL205_string_low_byte_first_decodes_Hello_from_HR1040()
    {
        if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason);
        if (!string.Equals(Environment.GetEnvironmentVariable("MODBUS_SIM_PROFILE"), "dl205",
                StringComparison.OrdinalIgnoreCase))
        {
            Assert.Skip("MODBUS_SIM_PROFILE != dl205 — skipping (standard profile does not seed HR[1040..1042]).");
        }

        var options = new ModbusDriverOptions
        {
            Host = sim.Host,
            Port = sim.Port,
            UnitId = 1,
            Timeout = TimeSpan.FromSeconds(2),
            RawTags = ModbusRawTags.Entries([
                new ModbusTagDefinition(
                    Name: "DL205_Hello_Low",
                    Region: ModbusRegion.HoldingRegisters,
                    Address: 1040,
                    DataType: ModbusDataType.String,
                    Writable: false,
                    StringLength: 5,
                    StringByteOrder: ModbusStringByteOrder.LowByteFirst),
                // Control: same address, HighByteFirst, to prove the driver would have decoded
                // garbage without the quirk flag.
                new ModbusTagDefinition(
                    Name: "DL205_Hello_High",
                    Region: ModbusRegion.HoldingRegisters,
                    Address: 1040,
                    DataType: ModbusDataType.String,
                    Writable: false,
                    StringLength: 5,
                    StringByteOrder: ModbusStringByteOrder.HighByteFirst),
            ]),
            Probe = new ModbusProbeOptions { Enabled = false },
        };
        await using var driver = new ModbusDriver(options, driverInstanceId: "dl205-string");
        await driver.InitializeAsync(driverConfigJson: "{}", TestContext.Current.CancellationToken);

        var results = await driver.ReadAsync(["DL205_Hello_Low", "DL205_Hello_High"],
            TestContext.Current.CancellationToken);

        results.Count.ShouldBe(2);
        results[0].StatusCode.ShouldBe(0u);
        results[0].Value.ShouldBe("Hello", "DL205 low-byte-first ordering must produce 'Hello' from HR[1040..1042]");

        // The high-byte-first read of the same wire bytes should differ — not asserting the
        // exact garbage string (that would couple the test to the ASCII byte math) but the two
        // decodes MUST disagree, otherwise the quirk flag is a no-op.
        results[1].StatusCode.ShouldBe(0u);
        results[1].Value.ShouldNotBe("Hello");
    }
}
