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

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

/// <summary>
///     R2-11 (05/CONV-2): the Modbus probe parses the SAME factory DTO shape the driver factory parses, so
///     <c>timeoutMs</c> binds identically instead of being silently dropped (it never bound to the runtime
///     <c>ModbusDriverOptions.Timeout</c> TimeSpan). The OpcUaClient probe/factory parse-parity rule.
/// </summary>
public sealed class ModbusDriverProbeParityTests
{
    [Fact]
    public void Config_timeoutMs_binds_as_the_effective_probe_timeout()
    {
        var (target, error) = ModbusDriverProbe.TryParseProbeTarget(
            "{\"host\":\"10.0.0.5\",\"port\":502,\"unitId\":3,\"timeoutMs\":250}",
            fallbackTimeout: TimeSpan.FromSeconds(5));

        error.ShouldBeNull();
        target.ShouldNotBeNull();
        target!.Value.Host.ShouldBe("10.0.0.5");
        target.Value.Port.ShouldBe(502);
        target.Value.UnitId.ShouldBe((byte)3);
        target.Value.Timeout.ShouldBe(TimeSpan.FromMilliseconds(250));
    }

    [Fact]
    public void Absent_timeoutMs_falls_back_to_the_caller_timeout()
    {
        var (target, _) = ModbusDriverProbe.TryParseProbeTarget(
            "{\"host\":\"10.0.0.5\",\"port\":502}", fallbackTimeout: TimeSpan.FromSeconds(5));

        target!.Value.Timeout.ShouldBe(TimeSpan.FromSeconds(5));
        target.Value.UnitId.ShouldBe((byte)1);   // DTO default
    }

    [Fact]
    public void Missing_host_is_an_error()
    {
        var (target, error) = ModbusDriverProbe.TryParseProbeTarget(
            "{\"port\":0}", fallbackTimeout: TimeSpan.FromSeconds(5));
        target.ShouldBeNull();
        error.ShouldNotBeNull();
    }

    [Fact]
    public void Invalid_json_is_an_error()
    {
        var (target, error) = ModbusDriverProbe.TryParseProbeTarget(
            "not valid json {{", fallbackTimeout: TimeSpan.FromSeconds(5));
        target.ShouldBeNull();
        error.ShouldContain("invalid");
    }
}
