using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.Browsing;
using ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser;

namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.Tests;

/// <summary>
///     Live-server tests against the opc-plc Docker fixture. Bring up with
///     `lmxopcua-fix up opcuaclient` from PowerShell before running. These tests
///     drive the internal <c>OpcUaClientBrowseSession</c> through its public factory
///     <see cref="OpcUaClientDriverBrowser"/> so no InternalsVisibleTo is needed.
///     Filter out with <c>--filter "Category!=RequiresOpcPlc"</c> when the fixture
///     is unreachable.
/// </summary>
[Trait("Category", "RequiresOpcPlc")]
public sealed class OpcUaClientBrowseSessionTests
{
    private static string Endpoint =>
        Environment.GetEnvironmentVariable("OPCUA_SIM_ENDPOINT") ?? "opc.tcp://10.100.0.35:50000";

    // SecurityPolicy.None, SecurityMode.None, OpcUaAuthType.Anonymous are all
    // index 0 and can also be written as string names now that the browser carries
    // JsonStringEnumConverter (Driver.OpcUaClient.Browser-003). Numeric ordinals are
    // kept here as they were before the fix — both forms are accepted.
    private static string ConfigJson => $$"""
        {
            "EndpointUrl":"{{Endpoint}}",
            "SecurityPolicy":0,
            "SecurityMode":0,
            "AuthType":0,
            "SessionTimeout":"00:01:00",
            "Timeout":"00:00:10",
            "PerEndpointConnectTimeout":"00:00:10"
        }
        """;

    /// <summary>RootAsync should surface at least one node under ObjectsFolder
    /// (opc-plc exposes a non-empty top level).</summary>
    [Fact]
    public async Task RootAsync_returns_at_least_one_node()
    {
        var ct = TestContext.Current.CancellationToken;
        var browser = new OpcUaClientDriverBrowser();
        await using var session = await browser.OpenAsync(ConfigJson, ct);
        var roots = await session.RootAsync(ct);
        roots.Count.ShouldBeGreaterThan(0);
    }

    /// <summary>ExpandAsync should accept a stable NodeId returned by RootAsync
    /// and round-trip through the live namespace table.</summary>
    [Fact]
    public async Task ExpandAsync_round_trips_stable_NodeId()
    {
        var ct = TestContext.Current.CancellationToken;
        var browser = new OpcUaClientDriverBrowser();
        await using var session = await browser.OpenAsync(ConfigJson, ct);
        var roots = await session.RootAsync(ct);
        var folder = roots.FirstOrDefault(n => n.Kind == BrowseNodeKind.Folder);
        folder.ShouldNotBeNull("expected at least one Folder under ObjectsFolder");
        var children = await session.ExpandAsync(folder!.NodeId, ct);
        children.ShouldNotBeNull();
    }

    /// <summary>The OPC UA picker treats variables as terminal leaves, so
    /// AttributesAsync must always be empty for this driver.</summary>
    [Fact]
    public async Task AttributesAsync_is_empty_for_opcuaclient()
    {
        var ct = TestContext.Current.CancellationToken;
        var browser = new OpcUaClientDriverBrowser();
        await using var session = await browser.OpenAsync(ConfigJson, ct);
        var attrs = await session.AttributesAsync("nsu=http://opcfoundation.org/UA/;i=85", ct);
        attrs.ShouldBeEmpty();
    }
}
