using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;

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

[Trait("Category", "Unit")]
public sealed class TwinCATAmsAddressTests
{
    /// <summary>Verifies that TryParse accepts valid AMS addresses in various formats.</summary>
    /// <param name="input">The raw AMS address string to parse.</param>
    /// <param name="netId">The expected AMS Net ID after parsing.</param>
    /// <param name="port">The expected AMS port after parsing.</param>
    [Theory]
    [InlineData("ads://5.23.91.23.1.1:851", "5.23.91.23.1.1", 851)]
    [InlineData("ads://5.23.91.23.1.1:852", "5.23.91.23.1.1", 852)]
    [InlineData("ads://5.23.91.23.1.1", "5.23.91.23.1.1", 851)]                  // default port
    [InlineData("ads://127.0.0.1.1.1:851", "127.0.0.1.1.1", 851)]
    [InlineData("ADS://5.23.91.23.1.1:851", "5.23.91.23.1.1", 851)]              // case-insensitive scheme
    [InlineData("ads://10.0.0.1.1.1:10000", "10.0.0.1.1.1", 10000)]              // system service port
    public void TryParse_accepts_valid_ams_addresses(string input, string netId, int port)
    {
        var parsed = TwinCATAmsAddress.TryParse(input);
        parsed.ShouldNotBeNull();
        parsed.NetId.ShouldBe(netId);
        parsed.Port.ShouldBe(port);
    }

    /// <summary>Verifies that TryParse rejects invalid AMS address forms.</summary>
    /// <param name="input">The invalid AMS address string that should be rejected.</param>
    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("   ")]
    [InlineData("tcp://5.23.91.23.1.1:851")]        // wrong scheme
    [InlineData("ads:5.23.91.23.1.1:851")]          // missing //
    [InlineData("ads://")]                           // empty body
    [InlineData("ads://5.23.91.23.1:851")]           // only 5 octets
    [InlineData("ads://5.23.91.23.1.1.1:851")]       // 7 octets
    [InlineData("ads://5.23.91.256.1.1:851")]        // octet > 255
    [InlineData("ads://5.23.91.23.1.1:0")]           // port 0
    [InlineData("ads://5.23.91.23.1.1:65536")]       // port out of range
    [InlineData("ads://5.23.91.23.1.1:abc")]         // non-numeric port
    [InlineData("ads://a.b.c.d.e.f:851")]            // non-numeric octets
    public void TryParse_rejects_invalid_forms(string? input)
    {
        TwinCATAmsAddress.TryParse(input).ShouldBeNull();
    }

    /// <summary>Verifies that ToString produces canonical AMS address strings.</summary>
    /// <param name="netId">The AMS Net ID to use.</param>
    /// <param name="port">The AMS port to use.</param>
    /// <param name="expected">The expected canonical string representation.</param>
    [Theory]
    [InlineData("5.23.91.23.1.1", 851, "ads://5.23.91.23.1.1")]        // default port stripped
    [InlineData("5.23.91.23.1.1", 852, "ads://5.23.91.23.1.1:852")]
    public void ToString_canonicalises(string netId, int port, string expected)
    {
        new TwinCATAmsAddress(netId, port).ToString().ShouldBe(expected);
    }

    /// <summary>Verifies that parsing and stringifying an AMS address is stable.</summary>
    [Fact]
    public void RoundTrip_is_stable()
    {
        const string input = "ads://5.23.91.23.1.1:852";
        var parsed = TwinCATAmsAddress.TryParse(input)!;
        TwinCATAmsAddress.TryParse(parsed.ToString()).ShouldBe(parsed);
    }
}
