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

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

[Trait("Category", "Unit")]
public sealed class AbLegacyEquipmentTagTests
{
    private const string RawPath = "cell/ablegacy/dev1/Counter";

    [Fact]
    public void Maps_tagconfig_into_a_definition_keyed_by_rawpath()
    {
        // v3: the deviceHostAddress key is dropped by the mapper (device routing travels on the
        // RawTagEntry's DeviceName). The mapped definition is keyed by the RawPath, not the blob.
        var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int"}""";
        AbLegacyTagDefinitionFactory.FromTagConfig(json, RawPath, out var def).ShouldBeTrue();
        def!.Name.ShouldBe(RawPath);
        def.Address.ShouldBe("N7:0");
        def.DataType.ShouldBe(AbLegacyDataType.Int);
        def.DeviceHostAddress.ShouldBe("");  // dropped — resolved from RawTagEntry.DeviceName at table-build time
    }

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

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

    [Fact]
    public void Rejects_address_as_a_json_number()
        => AbLegacyTagDefinitionFactory.FromTagConfig(
            """{"address":7,"dataType":"Int"}""", RawPath, out _).ShouldBeFalse();

    [Fact]
    public void Rejects_empty_address()
        => AbLegacyTagDefinitionFactory.FromTagConfig(
            """{"address":"","dataType":"Int"}""", RawPath, out _).ShouldBeFalse();

    // -002 regression: isArray:true without a valid positive arrayLength → scalar (null ArrayLength).

    [Fact]
    public void IsArray_true_with_arrayLength_zero_produces_scalar()
    {
        var json = """{"address":"N7:0","dataType":"Int","isArray":true,"arrayLength":0}""";
        AbLegacyTagDefinitionFactory.FromTagConfig(json, RawPath, out var def).ShouldBeTrue();
        def!.ArrayLength.ShouldBeNull();
    }

    [Fact]
    public void IsArray_true_with_no_arrayLength_produces_scalar()
    {
        var json = """{"address":"N7:0","dataType":"Int","isArray":true}""";
        AbLegacyTagDefinitionFactory.FromTagConfig(json, RawPath, out var def).ShouldBeTrue();
        def!.ArrayLength.ShouldBeNull();
    }

    [Fact]
    public void IsArray_true_with_negative_arrayLength_produces_scalar()
    {
        var json = """{"address":"N7:0","dataType":"Int","isArray":true,"arrayLength":-5}""";
        AbLegacyTagDefinitionFactory.FromTagConfig(json, RawPath, out var def).ShouldBeTrue();
        def!.ArrayLength.ShouldBeNull();
    }

    /// <summary>
    ///     End-to-end driver-level proof: a raw tag authored as a <see cref="RawTagEntry"/> (RawPath
    ///     identity + TagConfig blob) is resolved by the driver's RawPath → def table and read against the
    ///     fake runtime, returning Good instead of BadNodeIdUnknown. Under v3 the read reference is always
    ///     a RawPath — the driver no longer parses a raw TagConfig blob handed as the reference.
    /// </summary>
    [Fact]
    public async Task Driver_resolves_a_rawpath_ref_and_reads_instead_of_BadNodeIdUnknown()
    {
        var tagConfig = """{"address":"N7:0","dataType":"Int"}""";
        var factory = new FakeAbLegacyTagFactory { Customise = p => new FakeAbLegacyTag(p) { Value = 4242 } };
        var drv = new AbLegacyDriver(new AbLegacyDriverOptions
        {
            Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
            RawTags = [new RawTagEntry(RawPath, tagConfig, WriteIdempotent: false, DeviceName: "ab://10.0.0.5/1,0")],
            Probe = new AbLegacyProbeOptions { Enabled = false },
        }, "ablegacy-eq", factory);
        await drv.InitializeAsync("{}", CancellationToken.None);

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

        r[0].StatusCode.ShouldBe(AbLegacyStatusMapper.Good);
        r[0].StatusCode.ShouldNotBe(AbLegacyStatusMapper.BadNodeIdUnknown);
        r[0].Value.ShouldBe(4242);
    }
}
