using ZB.MOM.WW.OtOpcUa.Core.Abstractions;

namespace ZB.MOM.WW.OtOpcUa.Driver.Mqtt;

/// <summary>
///     The driver's internal per-tag descriptor — the parsed form of an authored raw tag's
///     <c>TagConfig</c> JSON, produced by <see cref="MqttTagDefinitionFactory.FromTagConfig"/> and
///     looked up through the shared <see cref="EquipmentTagRefResolver{TDef}"/> exactly as Modbus does.
///     See <c>docs/plans/2026-07-15-mqtt-sparkplug-driver-design.md</c> §5.2 / §5.3.
///     <para>
///         The record carries <b>both</b> ingest shapes: the Plain-MQTT fields
///         (<see cref="Topic"/> … <see cref="RetainSeed"/>), populated by
///         <see cref="MqttTagDefinitionFactory.FromTagConfig"/>, and the Sparkplug B descriptor fields
///         (<see cref="GroupId"/>, <see cref="EdgeNodeId"/>, <see cref="DeviceId"/>,
///         <see cref="MetricName"/>), populated by
///         <see cref="MqttTagDefinitionFactory.FromSparkplugTagConfig"/> (Task 21). A Sparkplug tag is
///         resolved by the stable <c>(group, node, device, metricName)</c> tuple, <b>never</b> by the
///         per-birth metric alias — an alias may be reused across a rebirth for a different metric.
///     </para>
///     <para>
///         <b>Which factory runs is chosen by the driver's <see cref="MqttMode"/>, never sniffed from
///         the blob.</b> A blob carrying both shapes' keys is legal (the AdminUI editor preserves
///         unknown keys through a load→save, so a tag retyped from Plain to Sparkplug keeps its old
///         <c>topic</c>), and a heuristic would make the same authored tag mean two different things
///         depending on which key happened to survive.
///     </para>
/// </summary>
/// <param name="Name">
///     The definition's identity: the tag's <b>RawPath</b> — the cluster-scoped slash path that is the
///     v3 driver wire reference. This is the key <see cref="EquipmentTagRefResolver{TDef}"/> resolves
///     on, the key <c>OnDataChange</c> must publish under, and the key <c>DriverHostActor</c> fans out
///     to the raw + UNS NodeIds. It is emphatically <b>not</b> the authored <c>TagConfig</c> blob: the
///     pre-v3 blob-as-reference shape is retired, and publishing under a blob key would silently miss
///     the RawPath-keyed fan-out while every unit test still passed.
/// </param>
/// <param name="Topic">The concrete MQTT topic this tag subscribes to (Plain mode).</param>
/// <param name="PayloadFormat">How the received payload is decoded (Plain mode).</param>
/// <param name="JsonPath">
///     JSONPath selecting the value inside a <see cref="MqttPayloadFormat.Json"/> payload. Defaults
///     to the document root (<c>$</c>) when the blob omits it; meaningless for
///     <see cref="MqttPayloadFormat.Raw"/> / <see cref="MqttPayloadFormat.Scalar"/>.
/// </param>
/// <param name="DataType">
///     The tag's declared value type. Explicit authoring is strongly preferred — payload-shape
///     inference is the brittle fallback (design §4).
/// </param>
/// <param name="Qos">
///     The per-tag subscription QoS (0–2), or <see langword="null"/> to inherit the driver-level
///     <see cref="MqttPlainOptions.DefaultQos"/>.
/// </param>
/// <param name="RetainSeed">
///     Whether the broker's retained message for <see cref="Topic"/> seeds this tag's initial value
///     on subscribe (the OPC UA initial-data convention). Defaults to <see langword="true"/>.
/// </param>
/// <param name="DataTypeAuthored">
///     Whether <paramref name="DataType"/> came from an authored <c>dataType</c> key or is merely this
///     record's default. Load-bearing in <b>Sparkplug</b> mode only, where <c>dataType</c> is optional:
///     a Sparkplug metric's type is declared by its own birth certificate, so an unauthored tag must
///     take the type the NBIRTH/DBIRTH declared rather than silently coercing every value to the
///     <see cref="DriverDataType.String"/> default. Both factories set it from the key's presence so it
///     means the same thing in either mode; nothing on the Plain ingest path reads it.
/// </param>
/// <param name="GroupId">The Sparkplug group id; <see langword="null"/> for a Plain-mode tag.</param>
/// <param name="EdgeNodeId">The Sparkplug edge-node id; <see langword="null"/> for a Plain-mode tag.</param>
/// <param name="DeviceId">
///     The Sparkplug device id, or <see langword="null"/> for a metric published by the edge node
///     itself (and for every Plain-mode tag).
/// </param>
/// <param name="MetricName">
///     The Sparkplug metric name — the tag's stable identity across rebirths, and the key the ingest
///     state machine binds by. <see langword="null"/> for a Plain-mode tag.
/// </param>
public sealed record MqttTagDefinition(
    string Name,
    string Topic,
    MqttPayloadFormat PayloadFormat,
    string JsonPath,
    DriverDataType DataType,
    int? Qos,
    bool RetainSeed,
    bool DataTypeAuthored = true,
    string? GroupId = null,
    string? EdgeNodeId = null,
    string? DeviceId = null,
    string? MetricName = null);
