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

namespace ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.DeviceForms;

/// <summary>
///     Typed working model for an OPC UA Client <c>Device</c>'s connection endpoint. Authors the PascalCase
///     <c>EndpointUrl</c> key that <c>OpcUaClientDriverOptions</c> binds. (The driver-level <c>EndpointUrls</c>
///     failover list stays on the driver config; a device's <c>EndpointUrl</c> is used only when that list
///     is empty.) Preserves unrecognised keys across a load→save.
/// </summary>
public sealed class OpcUaClientDeviceModel
{
    /// <summary>The single upstream server endpoint URL (opc.tcp://…).</summary>
    public string EndpointUrl { get; set; } = "opc.tcp://localhost:4840";

    private System.Text.Json.Nodes.JsonObject _bag = new();

    /// <summary>Loads a model from a DeviceConfig JSON string, defaulting absent keys and retaining
    /// every original key so fields this editor doesn't expose survive a load→save.</summary>
    /// <param name="json">The raw DeviceConfig JSON string, or null for a new/empty device.</param>
    /// <returns>The populated <see cref="OpcUaClientDeviceModel"/>.</returns>
    public static OpcUaClientDeviceModel FromJson(string? json)
    {
        var o = TagConfigJson.ParseOrNew(json);
        return new OpcUaClientDeviceModel
        {
            EndpointUrl = TagConfigJson.GetString(o, "EndpointUrl") ?? "opc.tcp://localhost:4840",
            _bag = o,
        };
    }

    /// <summary>Serializes this model back to a DeviceConfig JSON string, writing the PascalCase
    /// <c>EndpointUrl</c> key over the preserved key bag.</summary>
    /// <returns>The serialised DeviceConfig JSON string.</returns>
    public string ToJson()
    {
        TagConfigJson.Set(_bag, "EndpointUrl", EndpointUrl);
        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 the model is valid.</returns>
    public string? Validate()
    {
        if (string.IsNullOrWhiteSpace(EndpointUrl)) return "Endpoint URL is required.";
        if (!EndpointUrl.Trim().StartsWith("opc.tcp://", StringComparison.OrdinalIgnoreCase))
            return "Endpoint URL must start with opc.tcp://";
        return null;
    }
}
