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

/// <summary>One node in a driver-agnostic browse tree.</summary>
/// <param name="NodeId">Stable identifier passed back to the picker on commit. For OPC UA
/// this is the <c>nsu=...;...</c> form; for Galaxy this is the <c>tag_name</c>.</param>
/// <param name="DisplayName">Label shown in the tree.</param>
/// <param name="Kind">Whether this node terminates the address (Leaf) or has children
/// (Folder). Galaxy never returns Leaves; only the attribute side-panel terminates.</param>
/// <param name="HasChildrenHint">When true, the UI renders an expand affordance before
/// the children have been fetched.</param>
public sealed record BrowseNode(
    string NodeId,
    string DisplayName,
    BrowseNodeKind Kind,
    bool HasChildrenHint);

/// <summary>Discriminates terminal vs. expandable nodes for UI rendering.</summary>
public enum BrowseNodeKind
{
    /// <summary>Expandable — has (or may have) children. UI shows expand affordance.</summary>
    Folder,
    /// <summary>Terminal — commit on select.</summary>
    Leaf,
}

/// <summary>Metadata for an attribute of a Galaxy object (or the equivalent
/// per-driver concept). Surfaced in the picker's attribute side-panel.</summary>
/// <param name="IsAlarm">True when this attribute is itself an alarm condition (Galaxy: carries an
/// <c>AlarmExtension</c> primitive). The picker pre-fills a default native-alarm <c>alarm</c> object
/// into the TagConfig when an alarm attribute is selected. Defaults to false so non-alarm-aware
/// drivers (e.g. the OPC UA client browser) aren't forced to flow a flag they don't produce.</param>
/// <param name="AddressFields">
///     The <b>structured</b> address this leaf binds by, as <c>TagConfig</c> key → value in the
///     driver's own key vocabulary, for a driver whose address is a <i>tuple</i> rather than a single
///     reference string. Null (the default) for every driver whose address IS the
///     <see cref="BrowseNode.NodeId"/> — nothing changes for them.
///     <para>
///         It exists because a tuple cannot be recovered from an id string in general. MQTT/Sparkplug
///         is the case in point: a browse node id is
///         <c>{group}/{node}[/{device}]::{metric}</c>, and a metric name may itself contain <c>/</c>
///         (<c>Node Control/Rebirth</c>) — so the session keeps the decomposition it already has and
///         <b>states</b> it here, rather than the AdminUI's browse-commit mapper re-deriving it by
///         splitting the id and hoping. Same discipline as the v3 address space carrying
///         <c>AddressSpaceRealm</c> explicitly instead of parsing it out of a NodeId.
///     </para>
///     <para>
///         The producer states the binding <em>shape</em> too, by which keys it emits — so a consumer
///         never has to infer a driver's sub-mode from the tree's geometry. Consumers must read the
///         keys they know and ignore the rest; this is a description, not a config blob to splat into
///         a persisted TagConfig.
///     </para>
/// </param>
public sealed record AttributeInfo(
    string Name,
    string DriverDataType,
    bool   IsArray,
    string SecurityClass,
    bool   IsAlarm = false,
    IReadOnlyDictionary<string, string>? AddressFields = null);
