using System.Text.Json.Nodes;
using ZB.MOM.WW.OtOpcUa.Driver.Modbus;

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

/// <summary>Typed working model for a Modbus tag's TagConfig JSON (the driver-specific addressing/encoding
/// fields; name/writable live on the Tag entity). Preserves unrecognised JSON keys across a load→save.</summary>
public sealed class ModbusTagConfigModel
{
    /// <summary>Register region (HoldingRegisters/InputRegisters/Coils/DiscreteInputs).</summary>
    public ModbusRegion Region { get; set; } = ModbusRegion.HoldingRegisters;

    /// <summary>Starting register/coil address.</summary>
    public int Address { get; set; }

    /// <summary>Wire value type the register block decodes to.</summary>
    public ModbusDataType DataType { get; set; } = ModbusDataType.Int16;

    /// <summary>Word/byte ordering for multi-register values.</summary>
    public ModbusByteOrder ByteOrder { get; set; } = ModbusByteOrder.BigEndian;

    /// <summary>Bit index (0-15) for BitInRegister tags.</summary>
    public int BitIndex { get; set; }

    /// <summary>String length in characters for String tags.</summary>
    public int StringLength { get; set; }

    /// <summary>Optional writable flag (R2-11). <c>null</c> ⇒ the key is omitted (driver default true);
    /// an explicit <c>false</c> makes the equipment tag read-only. Preserved across load→save so an absent
    /// key stays absent.</summary>
    public bool? Writable { 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 raw TagConfig JSON string, or <c>null</c> for a new/empty config.</param>
    /// <returns>The populated <see cref="ModbusTagConfigModel"/>.</returns>
    public static ModbusTagConfigModel FromJson(string? json)
    {
        var o = TagConfigJson.ParseOrNew(json);
        return new ModbusTagConfigModel
        {
            Region = TagConfigJson.GetEnum(o, "region", ModbusRegion.HoldingRegisters),
            Address = TagConfigJson.GetInt(o, "address"),
            DataType = TagConfigJson.GetEnum(o, "dataType", ModbusDataType.Int16),
            ByteOrder = TagConfigJson.GetEnum(o, "byteOrder", ModbusByteOrder.BigEndian),
            BitIndex = TagConfigJson.GetInt(o, "bitIndex"),
            StringLength = TagConfigJson.GetInt(o, "stringLength"),
            Writable = TagConfigJson.GetBoolNullable(o, "writable"),
            _bag = o,
        };
    }

    /// <summary>Serialises this model back to a TagConfig JSON string, writing the six exposed fields
    /// (enums as their name strings) over the preserved key bag.</summary>
    /// <returns>The serialised TagConfig JSON string.</returns>
    public string ToJson()
    {
        TagConfigJson.Set(_bag, "region", Region);
        TagConfigJson.Set(_bag, "address", Address);
        TagConfigJson.Set(_bag, "dataType", DataType);
        TagConfigJson.Set(_bag, "byteOrder", ByteOrder);
        TagConfigJson.Set(_bag, "bitIndex", BitIndex);
        TagConfigJson.Set(_bag, "stringLength", StringLength);
        TagConfigJson.Set(_bag, "writable", Writable);
        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() => null;
}
