using System.Text.Json;
using Google.Protobuf;
using ZB.MOM.WW.MxGateway.Client;
using ZB.MOM.WW.MxGateway.Contracts.Proto;

namespace ZB.MOM.WW.MxGateway.Client.Tests;

public sealed class MxValueExtensionsTests
{
    /// <summary>Verifies that scalar values are converted to correctly-typed MxValue protobuf messages.</summary>
    [Fact]
    public void ToMxValue_WithScalarValues_CreatesTypedProtobufValues()
    {
        Assert.Equal(MxValue.KindOneofCase.BoolValue, true.ToMxValue().KindCase);
        Assert.Equal(MxValue.KindOneofCase.Int32Value, 123.ToMxValue().KindCase);
        Assert.Equal(MxValue.KindOneofCase.Int64Value, 123L.ToMxValue().KindCase);
        Assert.Equal(MxValue.KindOneofCase.FloatValue, 1.25F.ToMxValue().KindCase);
        Assert.Equal(MxValue.KindOneofCase.DoubleValue, 2.5D.ToMxValue().KindCase);
        Assert.Equal(MxValue.KindOneofCase.StringValue, "alpha".ToMxValue().KindCase);
    }

    /// <summary>Verifies that array values are converted to array-kind MxValue messages with correct element types and dimensions.</summary>
    [Fact]
    public void ToMxValue_WithArrays_CreatesTypedArrayProtobufValues()
    {
        MxValue value = new[] { "alpha", "beta" }.ToMxValue();

        Assert.Equal(MxValue.KindOneofCase.ArrayValue, value.KindCase);
        Assert.Equal(MxArray.ValuesOneofCase.StringValues, value.ArrayValue.ValuesCase);
        Assert.Equal(["alpha", "beta"], value.ArrayValue.StringValues.Values);
        Assert.Equal([2U], value.ArrayValue.Dimensions);
    }

    /// <summary>Verifies that fixture test cases project to expected MxValue kinds and preserve raw type metadata.</summary>
    [Fact]
    public void FixtureValues_ProjectExpectedKindsAndPreserveRawMetadata()
    {
        using JsonDocument document = JsonDocument.Parse(ReadFixture(
            "values",
            "value-conversion-cases.json"));

        foreach (JsonElement testCase in document.RootElement.GetProperty("cases").EnumerateArray())
        {
            string expectedKind = testCase.GetProperty("expectedKind").GetString()!;
            MxValue value = JsonParser.Default.Parse<MxValue>(
                testCase.GetProperty("value").GetRawText());

            Assert.Equal(expectedKind, value.GetProjectionKind());

            if (testCase.GetProperty("id").GetString() is "raw-fallback.variant")
            {
                Assert.Equal(32767, value.RawDataType);
                Assert.Equal([1, 2, 3, 4, 5], Assert.IsType<byte[]>(value.ToClrValue()));
            }
        }
    }

    private static string ReadFixture(string category, string fileName)
    {
        DirectoryInfo directory = new(AppContext.BaseDirectory);
        while (directory is not null)
        {
            string path = Path.Combine(
                directory.FullName,
                "clients",
                "proto",
                "fixtures",
                "behavior",
                category,
                fileName);

            if (File.Exists(path))
            {
                return File.ReadAllText(path);
            }

            directory = directory.Parent!;
        }

        throw new FileNotFoundException(fileName);
    }
}
