using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;

namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;

public sealed class AbLegacyTagConfigModelTests
{
    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("   ")]
    [InlineData("{}")]
    public void FromJson_returns_defaults_for_empty_input(string? json)
    {
        var m = AbLegacyTagConfigModel.FromJson(json);

        m.DeviceHostAddress.ShouldBe("");
        m.Address.ShouldBe("");
        m.DataType.ShouldBe(AbLegacyDataType.Int);
    }

    [Fact]
    public void Round_trip_preserves_all_fields()
    {
        var m = new AbLegacyTagConfigModel
        {
            DeviceHostAddress = "ab://10.0.0.5",
            Address = "N7:0",
            DataType = AbLegacyDataType.Float,
        };

        var json = m.ToJson();
        var m2 = AbLegacyTagConfigModel.FromJson(json);

        m2.DeviceHostAddress.ShouldBe("ab://10.0.0.5");
        m2.Address.ShouldBe("N7:0");
        m2.DataType.ShouldBe(AbLegacyDataType.Float);
    }

    [Fact]
    public void ToJson_emits_camelCase_keys_with_enum_names()
    {
        var m = new AbLegacyTagConfigModel
        {
            DeviceHostAddress = "ab://10.0.0.5",
            Address = "N7:0",
            DataType = AbLegacyDataType.Int,
        };

        var json = m.ToJson();

        json.ShouldContain("\"deviceHostAddress\":\"ab://10.0.0.5\"");
        json.ShouldContain("\"address\":\"N7:0\"");
        json.ShouldContain("\"dataType\":\"Int\"");
    }

    [Fact]
    public void FromJson_then_ToJson_preserves_unknown_keys()
    {
        var json = AbLegacyTagConfigModel
            .FromJson("""{"address":"N7:0","extraKey":"keepme"}""")
            .ToJson();

        json.ShouldContain("extraKey");
        json.ShouldContain("keepme");
        json.ShouldContain("\"address\":\"N7:0\"");
    }

    [Fact]
    public void Validate_returns_error_when_address_blank()
    {
        new AbLegacyTagConfigModel { Address = "" }.Validate().ShouldNotBeNull();
    }

    [Fact]
    public void Validate_returns_null_when_address_filled()
    {
        new AbLegacyTagConfigModel { Address = "N7:0" }.Validate().ShouldBeNull();
    }

    [Fact]
    public void ToJson_omits_blank_deviceHostAddress()
    {
        var m = new AbLegacyTagConfigModel { Address = "N7:0", DeviceHostAddress = "" };

        var json = m.ToJson();

        json.ShouldNotContain("deviceHostAddress");
    }

    [Fact]
    public void ToJson_includes_deviceHostAddress_when_set()
    {
        var m = new AbLegacyTagConfigModel { Address = "N7:0", DeviceHostAddress = "ab://host" };

        var json = m.ToJson();

        json.ShouldContain("\"deviceHostAddress\":\"ab://host\"");
    }

    // Array-length cap (review I-3): authored arrayLength > 256 on an AbLegacy array tag must be
    // rejected at author-time to prevent a Part 6 §5.3.2 ArrayDimensions mismatch at runtime.

    [Fact]
    public void Validate_returns_error_when_array_length_exceeds_256()
    {
        var m = AbLegacyTagConfigModel.FromJson("""{"address":"N7:0","isArray":true,"arrayLength":300}""");

        var error = m.Validate();

        error.ShouldNotBeNull();
        error.ShouldContain("256");
    }

    [Fact]
    public void Validate_returns_null_when_array_length_exactly_256()
    {
        var m = AbLegacyTagConfigModel.FromJson("""{"address":"N7:0","isArray":true,"arrayLength":256}""");

        m.Validate().ShouldBeNull();
    }

    [Fact]
    public void Validate_returns_null_for_scalar_tag_regardless_of_no_array_length()
    {
        var m = AbLegacyTagConfigModel.FromJson("""{"address":"N7:0"}""");

        m.Validate().ShouldBeNull();
    }
}
