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

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

/// <summary>
///     CONV-4 — <see cref="AbCipDriver.ResolveHost"/> keys per-host resilience (bulkhead / circuit
///     breaker). It must resolve an authored tag (by its RawPath) to its OWN device host, not fall back
///     to the first device — otherwise a broken device B trips device A's breaker. Unknown refs keep the
///     current first-device fallback.
/// </summary>
[Trait("Category", "Unit")]
public sealed class AbCipResolveHostTests
{
    private const string DeviceA = "ab://10.0.0.5/1,0";
    private const string DeviceB = "ab://10.0.0.6/1,0";

    private static AbCipDriver NewDriver(params RawTagEntry[] rawTags) => new(
        new AbCipDriverOptions
        {
            Devices = [new AbCipDeviceOptions(DeviceA), new AbCipDeviceOptions(DeviceB)],
            RawTags = rawTags,
            Probe = new AbCipProbeOptions { Enabled = false },
        },
        "abcip-1", new FakeAbCipTagFactory());

    /// <summary>An authored tag routed to device B resolves to device B's host, not the first device.</summary>
    [Fact]
    public async Task ResolveHost_TagRoutedToDeviceB_ReturnsItsOwnDeviceHost()
    {
        var drv = NewDriver(new RawTagEntry(
            RawPath: "line1/motorB/speed",
            TagConfig: """{"tagPath":"Motor1.Speed","dataType":"DInt"}""",
            WriteIdempotent: false,
            DeviceName: DeviceB));
        await drv.InitializeAsync("{}", CancellationToken.None);

        drv.ResolveHost("line1/motorB/speed").ShouldBe(DeviceB);
    }

    /// <summary>An unresolvable ref keeps the current first-device fallback.</summary>
    [Fact]
    public void ResolveHost_UnknownRef_KeepsCurrentFallback()
    {
        var drv = NewDriver();
        drv.ResolveHost("totally-unknown").ShouldBe(DeviceA);
    }
}
