using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Driver.Modbus;

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

[Trait("Category", "Unit")]
public class ModbusTagDefinitionFactoryTests
{
    private const string RawPath = "cell/modbus/dev1/Reg";

    [Fact]
    public void Maps_tagconfig_into_a_definition_keyed_by_rawpath()
    {
        var json = """{"region":"HoldingRegisters","address":40001,"dataType":"UInt16","byteOrder":"BigEndian","bitIndex":0,"stringLength":0}""";
        ModbusTagDefinitionFactory.FromTagConfig(json, RawPath, out var def).ShouldBeTrue();
        def!.Name.ShouldBe(RawPath); // v3: identity is the RawPath, not the blob
        def.Region.ShouldBe(ModbusRegion.HoldingRegisters);
        def.Address.ShouldBe((ushort)40001);
        def.DataType.ShouldBe(ModbusDataType.UInt16);
    }

    [Fact]
    public void Rejects_a_non_address_blob()
        => ModbusTagDefinitionFactory.FromTagConfig("""{"FullName":"x"}""", RawPath, out _).ShouldBeFalse();

    [Fact]
    public void Rejects_garbage()
        => ModbusTagDefinitionFactory.FromTagConfig("not json", RawPath, out _).ShouldBeFalse();

    [Fact]
    public void Rejects_address_as_a_json_string()
        => ModbusTagDefinitionFactory.FromTagConfig(
            """{"region":"HoldingRegisters","address":"40001","dataType":"UInt16"}""", RawPath, out _).ShouldBeFalse();

    [Fact]
    public void Rejects_address_out_of_ushort_range()
        => ModbusTagDefinitionFactory.FromTagConfig(
            """{"region":"HoldingRegisters","address":70000,"dataType":"UInt16"}""", RawPath, out _).ShouldBeFalse();

    /// <summary>
    ///     End-to-end driver-level proof: a Modbus driver handed an authored raw tag (RawPath + TagConfig
    ///     blob) maps it into its RawPath → definition table and reads by RawPath — the read goes to the
    ///     wire instead of returning BadNodeIdUnknown.
    /// </summary>
    [Fact]
    public async Task Driver_resolves_a_rawpath_and_reads_instead_of_BadNodeIdUnknown()
    {
        // Address 10 fits the fake transport's 256-register bank (40001 would overflow it).
        const string rawPath = "cell/modbus/dev1/U16";
        var json = """{"region":"HoldingRegisters","address":10,"dataType":"UInt16","byteOrder":"BigEndian","bitIndex":0,"stringLength":0}""";
        var fake = new ModbusDriverTests.FakeTransport();
        var opts = new ModbusDriverOptions { Host = "fake", RawTags = [new RawTagEntry(rawPath, json, false)] };
        var drv = new ModbusDriver(opts, "modbus-eq", _ => fake);
        await drv.InitializeAsync("{}", CancellationToken.None);
        fake.HoldingRegisters[10] = 4242;

        var r = await drv.ReadAsync([rawPath], CancellationToken.None);

        r[0].StatusCode.ShouldBe(0u);
        r[0].StatusCode.ShouldNotBe(0x80340000u); // not BadNodeIdUnknown
        r[0].Value.ShouldBe((ushort)4242);
    }
}
