namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Protocol;

/// <summary>
/// Optional capability for an <see cref="IDataConnection"/> implementation
/// that supports browsing the server's address space. Consumed only by
/// management/UI flows (e.g. the OPC UA tag picker on the instance config
/// page) — never by Instance Actors on the hot path.
/// </summary>
public interface IBrowsableDataConnection
{
    /// <summary>
    /// Returns the immediate children of <paramref name="parentNodeId"/>, or
    /// the server's root-level nodes when null. Pageable: when a prior call
    /// returned a non-null <see cref="BrowseChildrenResult.ContinuationToken"/>,
    /// pass it back via <paramref name="continuationToken"/> to fetch the next
    /// page; a null/empty token starts a fresh browse of <paramref name="parentNodeId"/>.
    /// </summary>
    /// <param name="parentNodeId">Node id whose children to browse, or null for the server root (OPC UA ObjectsFolder).</param>
    /// <param name="continuationToken">Opaque token from a prior <see cref="BrowseChildrenResult.ContinuationToken"/> to fetch the next page; null/empty starts a fresh browse. The token is implementation-private (e.g. a Base64 OPC UA continuation point) and must not be interpreted by callers.</param>
    /// <param name="cancellationToken">Cancellation token; on cancellation the implementation should throw <see cref="OperationCanceledException"/>.</param>
    /// <returns>A task that resolves to the child nodes, a flag indicating whether more results remain, and a continuation token to fetch the next page (null when exhausted).</returns>
    Task<BrowseChildrenResult> BrowseChildrenAsync(
        string? parentNodeId,
        string? continuationToken = null,
        CancellationToken cancellationToken = default);
}

/// <param name="Children">Child nodes returned by the server in browse order.</param>
/// <param name="Truncated">True when more children remain beyond this page; fetch them by passing <paramref name="ContinuationToken"/> back to <see cref="IBrowsableDataConnection.BrowseChildrenAsync"/>.</param>
/// <param name="ContinuationToken">Opaque token to fetch the next page (echo it back via the <c>continuationToken</c> parameter), or null when the browse is exhausted. Implementation-private — callers must treat it as opaque.</param>
public record BrowseChildrenResult(
    IReadOnlyList<BrowseNode> Children,
    bool Truncated,
    string? ContinuationToken = null);

/// <param name="NodeId">Server-issued node identifier (e.g. <c>"nsu=https://server/ns;s=Devices.Pump1.Speed"</c>).</param>
/// <param name="DisplayName">Human-readable display name from the server's DisplayName attribute.</param>
/// <param name="NodeClass">Classifies the node for UI purposes (Variable rows are selectable; Object rows are navigable).</param>
/// <param name="HasChildren">Hint so the UI can render an expand chevron without a second roundtrip.</param>
/// <param name="DataType">Friendly built-in DataType name for Variable nodes (e.g. "Double", "Int32"); the NodeId string for vendor types; null when not a Variable or the type read failed (best-effort).</param>
/// <param name="ValueRank">OPC UA ValueRank for Variable nodes (-1 = scalar, 0 = one-or-more dims, >0 = fixed array rank); null when not a Variable or the type read failed.</param>
/// <param name="Writable">True when the node grants CurrentWrite to the calling user; null when not a Variable or the type read failed.</param>
public record BrowseNode(
    string NodeId,
    string DisplayName,
    BrowseNodeClass NodeClass,
    bool HasChildren,
    string? DataType = null,
    int? ValueRank = null,
    bool? Writable = null);

public enum BrowseNodeClass { Object, Variable, Method, Other }

/// <summary>
/// Thrown by <see cref="IBrowsableDataConnection.BrowseChildrenAsync"/> when
/// the underlying session is not currently connected. Translated to
/// <c>BrowseFailureKind.ConnectionNotConnected</c> by the site-side handler.
/// </summary>
public sealed class ConnectionNotConnectedException : InvalidOperationException
{
    /// <summary>Initializes a new <see cref="ConnectionNotConnectedException"/> with the specified error message.</summary>
    /// <param name="message">Message describing why the connection is not currently connected.</param>
    public ConnectionNotConnectedException(string message) : base(message) { }
}
