namespace ZB.MOM.WW.OtOpcUa.Commons.Browsing;

/// <summary>
///     A live, one-level-at-a-time browse over a remote address space. Owned by the
///     AdminUI <c>BrowseSessionRegistry</c>; disposed by the registry's TTL reaper or
///     the picker body on close.
/// </summary>
public interface IBrowseSession : IAsyncDisposable
{
    /// <summary>Opaque token identifying this session in the registry.</summary>
    Guid Token { get; }

    /// <summary>Wall-clock time of the most recent successful call. Refreshed on
    /// <see cref="RootAsync"/>, <see cref="ExpandAsync"/>, and
    /// <see cref="AttributesAsync"/>; used by the reaper for idle eviction.</summary>
    DateTime LastUsedUtc { get; }

    /// <summary>Returns the top-level browse nodes.</summary>
    /// <param name="cancellationToken">Cancellation token for the operation.</param>
    /// <returns>The top-level browse nodes.</returns>
    Task<IReadOnlyList<BrowseNode>> RootAsync(CancellationToken cancellationToken);

    /// <summary>Returns the direct children of the node identified by
    /// <paramref name="nodeId"/>.</summary>
    /// <param name="nodeId">The node whose direct children are returned.</param>
    /// <param name="cancellationToken">Cancellation token for the operation.</param>
    /// <returns>The direct children of the node.</returns>
    Task<IReadOnlyList<BrowseNode>> ExpandAsync(string nodeId, CancellationToken cancellationToken);

    /// <summary>Returns the attributes of the node identified by <paramref name="nodeId"/>.
    /// Empty for drivers whose tree is uniform (OPC UA Client). Galaxy uses this to populate
    /// the attribute side-panel after the user selects an object.</summary>
    /// <param name="nodeId">The node whose attributes are returned.</param>
    /// <param name="cancellationToken">Cancellation token for the operation.</param>
    /// <returns>The attributes of the node.</returns>
    Task<IReadOnlyList<AttributeInfo>> AttributesAsync(string nodeId, CancellationToken cancellationToken);
}

/// <summary>
///     An <see cref="IBrowseSession"/> whose protocol offers an explicit, operator-triggered
///     <b>re-announce</b> action: a request that the remote peer republish its self-description so the
///     observation window can fill without waiting for the next natural announcement. Implemented
///     today only by the MQTT/Sparkplug browser, whose tree is built from observed NBIRTH/DBIRTH
///     certificates.
/// </summary>
/// <remarks>
///     <para>
///         <b>This is the only interface in the browse contract that causes an outbound message.</b>
///         Every other browse member is strictly read-only, and for the MQTT browser that read-only
///         property is load-bearing: a picker opened by an operator runs against a live production
///         broker. It is a separate interface, rather than an optional member on
///         <see cref="IBrowseSession"/>, precisely so "does this session write?" stays a type
///         question a caller cannot forget to ask.
///     </para>
///     <para>
///         <b>Callers must authorize before invoking it.</b> The AdminUI routes it through
///         <c>BrowserSessionService.RequestRebirthAsync</c>, which enforces the same
///         <c>DriverOperator</c> policy that gates the picker's Browse affordance — an implementation
///         cannot check that itself, since it holds no user identity.
///     </para>
/// </remarks>
public interface IRebirthCapableBrowseSession : IBrowseSession
{
    /// <summary>
    ///     Whether this <i>particular</i> session can actually re-announce — the runtime half of the
    ///     type question, and the one a UI must ask before offering the affordance.
    /// </summary>
    /// <remarks>
    ///     One session class may serve several protocol shapes: the MQTT browser opens the same session
    ///     type for Plain and for Sparkplug B, and a Plain MQTT window publishes <b>nothing, ever</b> —
    ///     there is no plain-MQTT re-announce to offer. Implementing the interface therefore means "this
    ///     session type may re-announce"; this property means "this instance will". A UI gating on the
    ///     type alone would draw a button that can only ever throw.
    ///     <para>
    ///         This is <b>not</b> an authorization signal — see the interface remarks. False here hides
    ///         the affordance; the caller still authorizes before invoking
    ///         <see cref="RequestRebirthAsync"/>, and the implementation still refuses a call it cannot
    ///         serve.
    ///     </para>
    /// </remarks>
    bool RebirthAvailable { get; }

    /// <summary>
    ///     Asks the addressed remote peer(s) to re-announce themselves.
    /// </summary>
    /// <param name="scope">
    ///     The protocol-specific target. For Sparkplug: a browse <c>NodeId</c> from this session's own
    ///     tree (group, edge node, device or metric — resolved up to the owning edge node), or a bare
    ///     <c>{group}/{edgeNode}</c> pair for a node that has not been observed yet.
    /// </param>
    /// <param name="cancellationToken">Cancellation token for the operation.</param>
    /// <returns>The number of request messages published.</returns>
    /// <exception cref="ArgumentException"><paramref name="scope"/> is empty or unusable.</exception>
    /// <exception cref="InvalidOperationException">
    ///     The scope resolves to no target, or to more targets than the implementation will fan out to
    ///     in one action. Nothing is published in either case.
    /// </exception>
    /// <exception cref="NotSupportedException">This session's mode has no re-announce action.</exception>
    Task<int> RequestRebirthAsync(string scope, CancellationToken cancellationToken);
}
