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

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

/// <summary>
/// M7-B2 (T15): <see cref="StubOpcUaClient"/> serves a small canned address-space
/// tree so DCL browse/paging flows can be exercised without a live OPC UA server.
/// The stub also fakes BrowseNext continuation paging on <c>Folder1</c> so the
/// continuation-token round-trip is testable end-to-end against the contract.
/// </summary>
public class StubOpcUaClientBrowseTests
{
    // StubOpcUaClient is internal; reached here via InternalsVisibleTo on the
    // DataConnectionLayer assembly (the test project is already a friend, since
    // other tests in this project new up internal adapter types).
    private static async Task<IOpcUaClient> ConnectedStubAsync()
    {
        var client = new StubOpcUaClient();
        await client.ConnectAsync("opc.tcp://stub", null, CancellationToken.None);
        return client;
    }

    [Fact]
    public async Task BrowseRoot_ReturnsTwoFolders_NoContinuation()
    {
        await using var client = (StubOpcUaClient)await ConnectedStubAsync();

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

        Assert.False(result.Truncated);
        Assert.Null(result.ContinuationToken);
        Assert.Collection(
            result.Children,
            n =>
            {
                Assert.Equal("Folder1", n.DisplayName);
                Assert.Equal(BrowseNodeClass.Object, n.NodeClass);
                Assert.True(n.HasChildren);
            },
            n =>
            {
                Assert.Equal("Folder2", n.DisplayName);
                Assert.Equal(BrowseNodeClass.Object, n.NodeClass);
                Assert.False(n.HasChildren);
            });
    }

    [Fact]
    public async Task BrowseFolder1_NoToken_ReturnsTag1_AndContinuationToken()
    {
        await using var client = (StubOpcUaClient)await ConnectedStubAsync();

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

        Assert.True(result.Truncated);
        Assert.Equal("STUB_PAGE2", result.ContinuationToken);
        var only = Assert.Single(result.Children);
        Assert.Equal("Tag1", only.DisplayName);
        Assert.Equal(BrowseNodeClass.Variable, only.NodeClass);
        Assert.False(only.HasChildren);
    }

    [Fact]
    public async Task BrowseFolder1_WithContinuationToken_ReturnsTag2_AndNoFurtherContinuation()
    {
        await using var client = (StubOpcUaClient)await ConnectedStubAsync();

        var result = await client.BrowseChildrenAsync(
            parentNodeId: "Folder1",
            continuationToken: "STUB_PAGE2");

        Assert.False(result.Truncated);
        Assert.Null(result.ContinuationToken);
        var only = Assert.Single(result.Children);
        Assert.Equal("Tag2", only.DisplayName);
        Assert.Equal(BrowseNodeClass.Variable, only.NodeClass);
        Assert.False(only.HasChildren);
    }

    [Fact]
    public async Task BrowseLeaf_ReturnsEmpty_NoContinuation()
    {
        await using var client = (StubOpcUaClient)await ConnectedStubAsync();

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

        Assert.False(result.Truncated);
        Assert.Null(result.ContinuationToken);
        Assert.Empty(result.Children);
    }
}
