using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Tests.TestModels;

namespace AyCode.Core.Tests.Serialization;

/// <summary>
/// Tests for Source Generator based IGeneratedBinaryWriter integration.
/// </summary>
[TestClass]
public class GeneratedSerializerIntegrationTests
{
    [TestMethod]
    public void GeneratedWriterType_Exists_ForMarkedTypes()
    {
        var type = typeof(GeneratedSerializerTestModel);
        var writerTypeName = $"{type.FullName}_GeneratedWriter";
        var writerType = type.Assembly.GetType(writerTypeName);

        Assert.IsNotNull(writerType,
            $"Generated writer type '{writerTypeName}' should exist for [AcBinarySerializable] marked type");
    }

    [TestMethod]
    public void GeneratedWriterType_ImplementsInterface()
    {
        var type = typeof(SimpleGeneratedModel);
        var writerTypeName = $"{type.FullName}_GeneratedWriter";
        var writerType = type.Assembly.GetType(writerTypeName);

        Assert.IsNotNull(writerType, $"Generated writer type '{writerTypeName}' should exist");
        Assert.IsTrue(typeof(IGeneratedBinaryWriter).IsAssignableFrom(writerType),
            "Generated writer should implement IGeneratedBinaryWriter");
    }

    [TestMethod]
    public void Serialization_WorksCorrectly_WithGeneratedWriterPresent()
    {
        var original = new GeneratedSerializerTestModel
        {
            Id = 42,
            Name = "Test",
            IsActive = true,
            Value = 3.14,
            Created = new DateTime(2025, 1, 5, 10, 30, 0, DateTimeKind.Utc),
            UniqueId = Guid.NewGuid(),
            Price = 99.99m,
            BigNumber = 9999999999L
        };

        var bytes = AcBinarySerializer.Serialize(original, AcBinarySerializerOptions.WithoutReferenceHandling);
        var deserialized = AcBinaryDeserializer.Deserialize<GeneratedSerializerTestModel>(bytes);

        Assert.IsNotNull(deserialized);
        Assert.AreEqual(original.Id, deserialized.Id);
        Assert.AreEqual(original.Name, deserialized.Name);
        Assert.AreEqual(original.IsActive, deserialized.IsActive);
        Assert.AreEqual(original.Value, deserialized.Value);
        Assert.AreEqual(original.Created, deserialized.Created);
        Assert.AreEqual(original.UniqueId, deserialized.UniqueId);
        Assert.AreEqual(original.Price, deserialized.Price);
        Assert.AreEqual(original.BigNumber, deserialized.BigNumber);
    }

    [TestMethod]
    public void GeneratedWriter_PrimitiveClass_RoundTrip()
    {
        var original = new PrimitiveTestClass
        {
            IntValue = 42,
            StringValue = "TestName",
            BoolValue = true,
            DoubleValue = 3.14,
            DateTimeValue = new DateTime(2025, 1, 5, 10, 30, 0, DateTimeKind.Utc),
            GuidValue = Guid.NewGuid(),
            DecimalValue = 99.99m,
            LongValue = 9999999999L,
            FloatValue = 1.5f,
            ByteValue = 42,
            ShortValue = 123,
            EnumValue = TestStatus.Active,
            NullableInt = 7,
            NullableIntNull = null
        };

        var options = AcBinarySerializerOptions.FastMode;
        var bytes = AcBinarySerializer.Serialize(original, options);
        var deserialized = AcBinaryDeserializer.Deserialize<PrimitiveTestClass>(bytes, options);

        Assert.IsNotNull(deserialized);
        Assert.AreEqual(original.IntValue, deserialized.IntValue);
        Assert.AreEqual(original.StringValue, deserialized.StringValue);
        Assert.AreEqual(original.BoolValue, deserialized.BoolValue);
        Assert.AreEqual(original.DoubleValue, deserialized.DoubleValue);
        Assert.AreEqual(original.DateTimeValue, deserialized.DateTimeValue);
        Assert.AreEqual(original.GuidValue, deserialized.GuidValue);
        Assert.AreEqual(original.DecimalValue, deserialized.DecimalValue);
        Assert.AreEqual(original.LongValue, deserialized.LongValue);
        Assert.AreEqual(original.NullableInt, deserialized.NullableInt);
        Assert.IsNull(deserialized.NullableIntNull);
    }

    [TestMethod]
    public void GeneratedWriter_ComplexHierarchy_RoundTrip()
    {
        TestDataFactory.ResetIdCounter();
        var sharedTag = TestDataFactory.CreateTag("SharedTag_All_True");
        var sharedUser = TestDataFactory.CreateUser("shareduser");

        var order = TestDataFactory.CreateOrder(
            itemCount: 2,
            palletsPerItem: 2,
            measurementsPerPallet: 2,
            pointsPerMeasurement: 2,
            sharedTag: sharedTag,
            sharedUser: sharedUser);

        var options = AcBinarySerializerOptions.FastMode;
        var bytes = AcBinarySerializer.Serialize(order, options);
        var deserialized = AcBinaryDeserializer.Deserialize<TestOrder_All_True>(bytes, options);

        Assert.IsNotNull(deserialized);
        Assert.AreEqual(order.Id, deserialized.Id);
        Assert.AreEqual(order.OrderNumber, deserialized.OrderNumber);
        Assert.AreEqual(order.Status, deserialized.Status);
        Assert.AreEqual(order.TotalAmount, deserialized.TotalAmount);
        Assert.AreEqual(order.Items.Count, deserialized.Items.Count);

        for (var i = 0; i < order.Items.Count; i++)
        {
            Assert.AreEqual(order.Items[i].Id, deserialized.Items[i].Id);
            Assert.AreEqual(order.Items[i].Pallets.Count, deserialized.Items[i].Pallets.Count);
        }
    }
}
