using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ZB.MOM.WW.MxGateway.Contracts.Proto;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;

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

/// <summary>
///     Round-trip tests for <see cref="MxValueDecoder"/>. Each scenario constructs a
///     gateway-style <see cref="MxValue"/>, decodes, and asserts the boxed CLR value
///     matches the expected type and value.
/// </summary>
public sealed class MxValueDecoderTests
{
    /// <summary>Verifies that decoding null returns null.</summary>
    [Fact]
    public void Decode_Null_ReturnsNull()
    {
        MxValueDecoder.Decode(null).ShouldBeNull();
    }

    /// <summary>Verifies that decoding IsNull flag returns null.</summary>
    [Fact]
    public void Decode_IsNullFlag_ReturnsNull()
    {
        var v = new MxValue { IsNull = true };
        MxValueDecoder.Decode(v).ShouldBeNull();
    }

    /// <summary>Verifies that bool values decode correctly.</summary>
    [Fact]
    public void Decode_Bool() => MxValueDecoder.Decode(new MxValue { BoolValue = true }).ShouldBe(true);

    /// <summary>Verifies that Int32 values decode correctly.</summary>
    [Fact]
    public void Decode_Int32() => MxValueDecoder.Decode(new MxValue { Int32Value = -42 }).ShouldBe(-42);

    /// <summary>Verifies that Int64 values decode correctly.</summary>
    [Fact]
    public void Decode_Int64() => MxValueDecoder.Decode(new MxValue { Int64Value = 123456789012L }).ShouldBe(123456789012L);

    /// <summary>Verifies that float values decode correctly.</summary>
    [Fact]
    public void Decode_Float() => MxValueDecoder.Decode(new MxValue { FloatValue = 3.14f }).ShouldBe(3.14f);

    /// <summary>Verifies that double values decode correctly.</summary>
    [Fact]
    public void Decode_Double() => MxValueDecoder.Decode(new MxValue { DoubleValue = 2.71828 }).ShouldBe(2.71828);

    /// <summary>Verifies that string values decode correctly.</summary>
    [Fact]
    public void Decode_String() => MxValueDecoder.Decode(new MxValue { StringValue = "hello" }).ShouldBe("hello");

    /// <summary>Verifies that timestamp values decode to UTC datetime.</summary>
    [Fact]
    public void Decode_Timestamp_ReturnsUtcDateTime()
    {
        var when = new DateTime(2026, 4, 29, 12, 0, 0, DateTimeKind.Utc);
        var v = new MxValue { TimestampValue = Timestamp.FromDateTime(when) };
        MxValueDecoder.Decode(v).ShouldBe(when);
    }

    /// <summary>Verifies that bool arrays decode correctly.</summary>
    [Fact]
    public void Decode_BoolArray()
    {
        var v = new MxValue
        {
            ArrayValue = new MxArray
            {
                BoolValues = new BoolArray(),
            },
        };
        v.ArrayValue.BoolValues.Values.AddRange(new[] { true, false, true });

        var decoded = MxValueDecoder.Decode(v);
        decoded.ShouldBe(new[] { true, false, true });
    }

    /// <summary>Verifies that double arrays decode correctly.</summary>
    [Fact]
    public void Decode_DoubleArray()
    {
        var v = new MxValue
        {
            ArrayValue = new MxArray { DoubleValues = new DoubleArray() },
        };
        v.ArrayValue.DoubleValues.Values.AddRange(new[] { 1.0, 2.0, 3.5 });

        var decoded = MxValueDecoder.Decode(v);
        decoded.ShouldBe(new[] { 1.0, 2.0, 3.5 });
    }

    /// <summary>Verifies that string arrays decode correctly.</summary>
    [Fact]
    public void Decode_StringArray()
    {
        var v = new MxValue
        {
            ArrayValue = new MxArray { StringValues = new StringArray() },
        };
        v.ArrayValue.StringValues.Values.AddRange(new[] { "a", "b" });

        var decoded = MxValueDecoder.Decode(v);
        decoded.ShouldBe(new[] { "a", "b" });
    }

    /// <summary>Verifies that raw byte values decode correctly.</summary>
    [Fact]
    public void Decode_RawValue_ReturnsBytes()
    {
        var bytes = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF };
        var v = new MxValue { RawValue = ByteString.CopyFrom(bytes) };

        var decoded = (byte[])MxValueDecoder.Decode(v)!;
        decoded.ShouldBe(bytes);
    }
}
