using Google.Protobuf.WellKnownTypes;
using ZB.MOM.WW.MxGateway.Contracts.Proto;

namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;

/// <summary>
///     Translates gateway-side <see cref="MxValue"/> instances into the boxed CLR objects
///     <c>DataValueSnapshot.Value</c> carries. Mirrors the seven Galaxy data types in
///     <c>DataTypeMap</c> (Boolean, Int32, Int64, Float32, Float64, String, DateTime), plus
///     the array variants exposed by <see cref="MxArray"/>. Unknown / awkward values fall
///     back to the <c>raw_value</c> bytes so a forward-compatible MXAccess deployment
///     doesn't lose data on the wire — the consumer can opt to deserialise the bytes.
/// </summary>
internal static class MxValueDecoder
{
    /// <summary>Decodes a gateway MxValue into a boxed CLR object.</summary>
    /// <param name="value">The MxValue to decode, or null.</param>
    /// <returns>The decoded boxed CLR value, or null when <paramref name="value"/> is null or carries a null MxValue.</returns>
    public static object? Decode(MxValue? value)
    {
        if (value is null) return null;
        if (value.IsNull) return null;

        return value.KindCase switch
        {
            MxValue.KindOneofCase.BoolValue       => value.BoolValue,
            MxValue.KindOneofCase.Int32Value      => value.Int32Value,
            MxValue.KindOneofCase.Int64Value      => value.Int64Value,
            MxValue.KindOneofCase.FloatValue      => value.FloatValue,
            MxValue.KindOneofCase.DoubleValue     => value.DoubleValue,
            MxValue.KindOneofCase.StringValue     => value.StringValue,
            MxValue.KindOneofCase.TimestampValue  => DecodeTimestamp(value.TimestampValue),
            MxValue.KindOneofCase.ArrayValue      => DecodeArray(value.ArrayValue),
            MxValue.KindOneofCase.RawValue        => value.RawValue.ToByteArray(),
            _ => null,
        };
    }

    private static DateTime? DecodeTimestamp(Timestamp? ts) => ts?.ToDateTime();

    private static object? DecodeArray(MxArray? array)
    {
        if (array is null) return null;

        return array.ValuesCase switch
        {
            MxArray.ValuesOneofCase.BoolValues       => array.BoolValues.Values.ToArray(),
            MxArray.ValuesOneofCase.Int32Values      => array.Int32Values.Values.ToArray(),
            MxArray.ValuesOneofCase.Int64Values      => array.Int64Values.Values.ToArray(),
            MxArray.ValuesOneofCase.FloatValues      => array.FloatValues.Values.ToArray(),
            MxArray.ValuesOneofCase.DoubleValues     => array.DoubleValues.Values.ToArray(),
            MxArray.ValuesOneofCase.StringValues     => array.StringValues.Values.ToArray(),
            MxArray.ValuesOneofCase.TimestampValues  => array.TimestampValues.Values.Select(t => t.ToDateTime()).ToArray(),
            _ => null,
        };
    }
}
