using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Protocol;
using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;

namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests.Adapters;

/// <summary>
/// Tests for <see cref="RealOpcUaClient.BrowseChildrenAsync"/>.
///
/// Two shapes here:
///
/// 1. A live round-trip against the infra OPC UA server
///    (<c>opc.tcp://localhost:50000</c> — see <c>infra/docker-compose.yml</c>,
///    started via <c>cd infra &amp;&amp; docker compose up -d opcua</c>).
///    Marked <c>[SkippableFact]</c> so it reports Skipped — not failed — on
///    machines that don't have the infra stack running. The live test asserts
///    that the server root browse returns the standard "Server" node, which
///    proves we targeted ObjectsFolder (<c>ns=0;i=85</c>) and that the
///    response mapping survived the round trip.
///
/// 2. A pure unit test that exercises the not-connected guard — no infra
///    needed, runs in every build.
/// </summary>
[Trait("Category", "RequiresOpcUa")]
public class RealOpcUaClientBrowseTests
{
    // The infra/docker-compose.yml opcua container maps the OPC PLC simulator
    // on host port 50000 (not the OPC UA default 4840). Matches what the
    // existing docker/ and docker-env2/ topologies dial into.
    private const string EndpointUrl = "opc.tcp://localhost:50000";

    [SkippableFact]
    public async Task BrowseChildren_at_root_returns_ObjectsFolder_with_Server_node()
    {
        await using var client = new RealOpcUaClient();

        // Probe the endpoint before asserting anything. If the infra OPC UA
        // server isn't up, ConnectAsync surfaces a socket/timeout error from
        // deep inside the OPC Foundation SDK — we treat that as "infra not
        // available" and skip rather than fail, mirroring the SkippableFact
        // pattern already used in ConfigurationDatabase/AuditLog tests.
        try
        {
            await client.ConnectAsync(
                EndpointUrl,
                new OpcUaConnectionOptions(AutoAcceptUntrustedCerts: true));
        }
        catch (Exception ex)
        {
            Skip.If(true, $"OPC UA test server not reachable on {EndpointUrl}: {ex.Message}");
            return; // Skip.If throws; this is unreachable but keeps the compiler happy.
        }

        var result = await client.BrowseChildrenAsync(parentNodeId: null);

        // Under ObjectsFolder (ns=0;i=85) every OPC UA-compliant server
        // exposes a 'Server' object at ns=0;i=2253 — its presence confirms
        // we hit the right root and that DisplayName mapping survives the
        // round trip.
        Assert.NotEmpty(result.Children);
        Assert.Contains(result.Children, n => n.DisplayName == "Server");
    }

    [Fact]
    public async Task BrowseChildren_throws_ConnectionNotConnected_when_session_is_null()
    {
        // No ConnectAsync — _session is still null, so the typed guard at the
        // top of BrowseChildrenAsync should fire before any SDK call.
        await using var client = new RealOpcUaClient();

        await Assert.ThrowsAsync<ConnectionNotConnectedException>(
            () => client.BrowseChildrenAsync(parentNodeId: null));
    }
}
