using System.Reflection;
using MessagePack;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Shared.Contracts;

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

[Trait("Category", "Unit")]
public sealed class ContractRoundTripTests
{
    /// <summary>
    ///     Every MessagePack contract in the Shared project must round-trip. Byte-for-byte equality
    ///     on re-serialization proves the contract is deterministic — critical for the Hello
    ///     version-negotiation hash and for debugging wire dumps.
    /// </summary>
    [Fact]
    public void All_MessagePackObject_contracts_round_trip_byte_for_byte()
    {
        var contractTypes = typeof(Hello).Assembly.GetTypes()
            .Where(t => t.GetCustomAttribute<MessagePackObjectAttribute>() is not null)
            .ToList();

        contractTypes.Count.ShouldBeGreaterThan(15, "scan should find all contracts");

        foreach (var type in contractTypes)
        {
            var instance = Activator.CreateInstance(type);
            var bytes1 = MessagePackSerializer.Serialize(type, instance);
            var hydrated = MessagePackSerializer.Deserialize(type, bytes1);
            var bytes2 = MessagePackSerializer.Serialize(type, hydrated);

            bytes2.ShouldBe(bytes1, $"{type.Name} did not round-trip byte-for-byte");
        }
    }

    [Fact]
    public void Hello_default_reports_current_protocol_version()
    {
        var h = new Hello { PeerName = "Proxy", SharedSecret = "x" };
        h.ProtocolMajor.ShouldBe(Hello.CurrentMajor);
        h.ProtocolMinor.ShouldBe(Hello.CurrentMinor);
    }

    [Fact]
    public void OpenSessionRequest_round_trips_values()
    {
        var req = new OpenSessionRequest { DriverInstanceId = "gal-1", DriverConfigJson = "{\"x\":1}" };
        var bytes = MessagePackSerializer.Serialize(req);
        var hydrated = MessagePackSerializer.Deserialize<OpenSessionRequest>(bytes);

        hydrated.DriverInstanceId.ShouldBe("gal-1");
        hydrated.DriverConfigJson.ShouldBe("{\"x\":1}");
    }

    [Fact]
    public void Contracts_reference_only_BCL_and_MessagePack()
    {
        var asm = typeof(Hello).Assembly;
        var references = asm.GetReferencedAssemblies()
            .Select(n => n.Name!)
            .Where(n => !n.StartsWith("System.") && n != "mscorlib" && n != "netstandard")
            .ToList();

        // Only MessagePack should appear outside BCL — no System.Text.Json, no EF, no AspNetCore.
        references.ShouldAllBe(n => n == "MessagePack" || n == "MessagePack.Annotations");
    }
}
