using System;
using FluentAssertions;
using Xunit;
using ZB.MOM.WW.LmxProxy.Host.Domain;

namespace ZB.MOM.WW.LmxProxy.Host.Tests.Domain
{
    public class TypedValueConverterTests
    {
        [Fact]
        public void Null_RoundTrips()
        {
            var tv = TypedValueConverter.ToTypedValue(null);
            tv.Should().BeNull();
            TypedValueConverter.FromTypedValue(null).Should().BeNull();
        }

        [Fact]
        public void DBNull_MapsToNull()
        {
            var tv = TypedValueConverter.ToTypedValue(DBNull.Value);
            tv.Should().BeNull();
        }

        [Fact]
        public void Bool_RoundTrips()
        {
            var tv = TypedValueConverter.ToTypedValue(true);
            tv.Should().NotBeNull();
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.BoolValue);
            tv.BoolValue.Should().BeTrue();
            TypedValueConverter.FromTypedValue(tv).Should().Be(true);

            var tvFalse = TypedValueConverter.ToTypedValue(false);
            tvFalse!.BoolValue.Should().BeFalse();
            TypedValueConverter.FromTypedValue(tvFalse).Should().Be(false);
        }

        [Fact]
        public void Short_WidensToInt32()
        {
            var tv = TypedValueConverter.ToTypedValue((short)42);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.Int32Value);
            tv.Int32Value.Should().Be(42);
            TypedValueConverter.FromTypedValue(tv).Should().Be(42);
        }

        [Fact]
        public void Int_RoundTrips()
        {
            var tv = TypedValueConverter.ToTypedValue(int.MaxValue);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.Int32Value);
            tv.Int32Value.Should().Be(int.MaxValue);
            TypedValueConverter.FromTypedValue(tv).Should().Be(int.MaxValue);
        }

        [Fact]
        public void Long_RoundTrips()
        {
            var tv = TypedValueConverter.ToTypedValue(long.MaxValue);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.Int64Value);
            tv.Int64Value.Should().Be(long.MaxValue);
            TypedValueConverter.FromTypedValue(tv).Should().Be(long.MaxValue);
        }

        [Fact]
        public void UShort_WidensToInt32()
        {
            var tv = TypedValueConverter.ToTypedValue((ushort)65535);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.Int32Value);
            tv.Int32Value.Should().Be(65535);
        }

        [Fact]
        public void UInt_WidensToInt64()
        {
            var tv = TypedValueConverter.ToTypedValue(uint.MaxValue);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.Int64Value);
            tv.Int64Value.Should().Be(uint.MaxValue);
        }

        [Fact]
        public void ULong_MapsToInt64()
        {
            var tv = TypedValueConverter.ToTypedValue((ulong)12345678);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.Int64Value);
            tv.Int64Value.Should().Be(12345678);
        }

        [Fact]
        public void Float_RoundTrips()
        {
            var tv = TypedValueConverter.ToTypedValue(3.14159f);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.FloatValue);
            tv.FloatValue.Should().Be(3.14159f);
            TypedValueConverter.FromTypedValue(tv).Should().Be(3.14159f);
        }

        [Fact]
        public void Double_RoundTrips()
        {
            var tv = TypedValueConverter.ToTypedValue(2.718281828459045);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.DoubleValue);
            tv.DoubleValue.Should().Be(2.718281828459045);
            TypedValueConverter.FromTypedValue(tv).Should().Be(2.718281828459045);
        }

        [Fact]
        public void String_RoundTrips()
        {
            var tv = TypedValueConverter.ToTypedValue("Hello World");
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.StringValue);
            tv.StringValue.Should().Be("Hello World");
            TypedValueConverter.FromTypedValue(tv).Should().Be("Hello World");
        }

        [Fact]
        public void DateTime_RoundTrips_AsUtcTicks()
        {
            var dt = new DateTime(2026, 3, 21, 12, 0, 0, DateTimeKind.Utc);
            var tv = TypedValueConverter.ToTypedValue(dt);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.DatetimeValue);
            tv.DatetimeValue.Should().Be(dt.Ticks);
            var result = (DateTime)TypedValueConverter.FromTypedValue(tv)!;
            result.Kind.Should().Be(DateTimeKind.Utc);
            result.Ticks.Should().Be(dt.Ticks);
        }

        [Fact]
        public void ByteArray_RoundTrips()
        {
            var bytes = new byte[] { 0x00, 0xFF, 0x42 };
            var tv = TypedValueConverter.ToTypedValue(bytes);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.BytesValue);
            var result = (byte[])TypedValueConverter.FromTypedValue(tv)!;
            result.Should().BeEquivalentTo(bytes);
        }

        [Fact]
        public void Decimal_MapsToDouble()
        {
            var tv = TypedValueConverter.ToTypedValue(123.456m);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.DoubleValue);
            tv.DoubleValue.Should().BeApproximately(123.456, 0.001);
        }

        [Fact]
        public void FloatArray_RoundTrips()
        {
            var arr = new float[] { 1.0f, 2.0f, 3.0f };
            var tv = TypedValueConverter.ToTypedValue(arr);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.ArrayValue);
            var result = (float[])TypedValueConverter.FromTypedValue(tv)!;
            result.Should().BeEquivalentTo(arr);
        }

        [Fact]
        public void IntArray_RoundTrips()
        {
            var arr = new int[] { 10, 20, 30 };
            var tv = TypedValueConverter.ToTypedValue(arr);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.ArrayValue);
            var result = (int[])TypedValueConverter.FromTypedValue(tv)!;
            result.Should().BeEquivalentTo(arr);
        }

        [Fact]
        public void StringArray_RoundTrips()
        {
            var arr = new string[] { "a", "b", "c" };
            var tv = TypedValueConverter.ToTypedValue(arr);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.ArrayValue);
            var result = (string[])TypedValueConverter.FromTypedValue(tv)!;
            result.Should().BeEquivalentTo(arr);
        }

        [Fact]
        public void DoubleArray_RoundTrips()
        {
            var arr = new double[] { 1.1, 2.2, 3.3 };
            var tv = TypedValueConverter.ToTypedValue(arr);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.ArrayValue);
            var result = (double[])TypedValueConverter.FromTypedValue(tv)!;
            result.Should().BeEquivalentTo(arr);
        }

        [Fact]
        public void UnrecognizedType_FallsBackToString()
        {
            var guid = Guid.NewGuid();
            var tv = TypedValueConverter.ToTypedValue(guid);
            tv!.ValueCase.Should().Be(Scada.TypedValue.ValueOneofCase.StringValue);
            tv.StringValue.Should().Be(guid.ToString());
        }
    }
}
