using System.Text.Json.Nodes;

namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;

/// <summary>Typed working model for an OPC UA Client (gateway) equipment tag's TagConfig JSON. The tag
/// is bound to the upstream OPC UA server node by its full reference (<c>nodeId</c> — the persisted
/// stable <c>nsu=…;s=…</c> or plain <c>ns=2;s=…</c> NodeId the driver reads/writes/subscribes against).
/// Preserves unrecognised JSON keys across a load→save.</summary>
/// <remarks>
///     Under the v3 identity contract, <c>TagConfig</c> no longer carries identity — the upstream
///     node reference is an ordinary driver-specific address field. The key is the camelCase
///     <c>nodeId</c> (replacing the legacy PascalCase <c>FullName</c> identity convention). The
///     driver-agnostic server-side HistoryRead intent keys (<c>isHistorized</c> /
///     <c>historianTagname</c>) are NOT modelled here — they are owned by the TagModal-merge seam
///     (<see cref="TagHistorizeConfig"/>) and survive a load→save of this model as preserved unknown keys.
/// </remarks>
public sealed class OpcUaClientTagConfigModel
{
    /// <summary>The camelCase TagConfig JSON key holding the upstream node reference.</summary>
    internal const string NodeIdKey = "nodeId";

    /// <summary>Upstream OPC UA node reference the tag binds to (the driver-side full reference). Required.</summary>
    public string NodeId { get; set; } = "";

    private JsonObject _bag = new();

    /// <summary>Loads a model from a TagConfig JSON string, defaulting any absent field and retaining
    /// every original key (so fields this editor doesn't expose survive a load→save).</summary>
    /// <param name="json">The tag's TagConfig JSON (null/blank/malformed ⇒ defaults).</param>
    /// <returns>The populated model.</returns>
    public static OpcUaClientTagConfigModel FromJson(string? json)
    {
        var o = TagConfigJson.ParseOrNew(json);
        return new OpcUaClientTagConfigModel
        {
            NodeId = TagConfigJson.GetString(o, NodeIdKey) ?? "",
            _bag = o,
        };
    }

    /// <summary>Serialises this model back to a TagConfig JSON string over the preserved key bag.
    /// <c>nodeId</c> is written camelCase (the v3 address key); any history keys merged by the TagModal
    /// (<c>isHistorized</c> / <c>historianTagname</c>) are carried through untouched as preserved unknown
    /// keys.</summary>
    /// <returns>The serialised TagConfig JSON string.</returns>
    public string ToJson()
    {
        TagConfigJson.Set(_bag, NodeIdKey, NodeId.Trim());
        return TagConfigJson.Serialize(_bag);
    }

    /// <summary>Validation hook; returns an error message or null when the model is valid.</summary>
    /// <returns>An error message describing the validation failure, or <c>null</c> when valid.</returns>
    public string? Validate()
        => string.IsNullOrWhiteSpace(NodeId) ? "An upstream node reference (nodeId) is required." : null;
}
