using AyCode.Core.Extensions;
using static AyCode.Core.Tests.TestModels.AcSerializerModels;
using static AyCode.Core.Tests.Serialization.AcSerializerTestHelper;

namespace AyCode.Core.Tests.Serialization;

/// <summary>
/// Tests for DateTime type handling and potential type mismatch issues.
/// </summary>
[TestClass]
public class AcBinarySerializerDateTimeTests
{
    [TestMethod]
    public void Deserialize_DateTimeProperty_FromDifferentPropertyOrder_RoundTrip()
    {
        var entity = new TestEntityWithDateTimeAndInt
        {
            Id = 42,
            IntValue = 100,
            Created = new DateTime(2024, 12, 25, 10, 30, 45, DateTimeKind.Utc),
            Modified = new DateTime(2024, 12, 26, 11, 45, 30, DateTimeKind.Utc),
            StatusCode = 5,
            Name = "TestEntity"
        };

        var binary = entity.ToBinary();
        var result = binary.BinaryTo<TestEntityWithDateTimeAndInt>();

        Assert.IsNotNull(result);
        Assert.AreEqual(42, result.Id);
        Assert.AreEqual(100, result.IntValue);
        Assert.AreEqual(entity.Created, result.Created, "Created DateTime should match");
        Assert.AreEqual(entity.Modified, result.Modified, "Modified DateTime should match");
        Assert.AreEqual(5, result.StatusCode);
        Assert.AreEqual("TestEntity", result.Name);
    }

    [TestMethod]
    public void Deserialize_ListOfEntitiesWithDateTimeProperties_RoundTrip()
    {
        var entities = CreateDateTimeEntities(10);
        var binary = entities.ToBinary();
        var result = binary.BinaryTo<List<TestEntityWithDateTimeAndInt>>();

        Assert.IsNotNull(result);
        Assert.AreEqual(10, result.Count);

        for (int i = 0; i < 10; i++)
        {
            var original = entities[i];
            var deserialized = result[i];

            Assert.AreEqual(original.Id, deserialized.Id, $"Id mismatch at index {i}");
            Assert.AreEqual(original.IntValue, deserialized.IntValue, $"IntValue mismatch at index {i}");
            Assert.AreEqual(original.Created.Ticks, deserialized.Created.Ticks, $"Created mismatch at index {i}");
            Assert.AreEqual(original.Modified.Ticks, deserialized.Modified.Ticks, $"Modified mismatch at index {i}");
            Assert.AreEqual(original.StatusCode, deserialized.StatusCode, $"StatusCode mismatch at index {i}");
            Assert.AreEqual(original.Name, deserialized.Name, $"Name mismatch at index {i}");
        }
    }

    [TestMethod]
    public void Deserialize_EntityWithManyIntPropertiesBeforeDateTime_RoundTrip()
    {
        var entity = new TestEntityWithManyIntsBeforeDateTime
        {
            Id = 1,
            Value1 = 10,
            Value2 = 20,
            Value3 = 30,
            Value4 = 40,
            Value5 = 50,
            FirstDateTime = new DateTime(2024, 1, 15, 10, 0, 0, DateTimeKind.Utc),
            SecondDateTime = new DateTime(2024, 6, 20, 15, 30, 0, DateTimeKind.Utc),
            FinalValue = 999
        };

        var binary = entity.ToBinary();
        var result = binary.BinaryTo<TestEntityWithManyIntsBeforeDateTime>();

        Assert.IsNotNull(result);
        Assert.AreEqual(1, result.Id);
        Assert.AreEqual(10, result.Value1);
        Assert.AreEqual(20, result.Value2);
        Assert.AreEqual(30, result.Value3);
        Assert.AreEqual(40, result.Value4);
        Assert.AreEqual(50, result.Value5);
        Assert.AreEqual(entity.FirstDateTime, result.FirstDateTime, "FirstDateTime should match");
        Assert.AreEqual(entity.SecondDateTime, result.SecondDateTime, "SecondDateTime should match");
        Assert.AreEqual(999, result.FinalValue);
    }

    [TestMethod]
    public void Deserialize_NestedEntityWithDateTimeInChild_RoundTrip()
    {
        var parent = new TestParentEntityWithDateTimeChild
        {
            ParentId = 1,
            ParentName = "Parent",
            Child = new TestEntityWithDateTimeAndInt
            {
                Id = 100,
                IntValue = 200,
                Created = DateTime.UtcNow.AddDays(-5),
                Modified = DateTime.UtcNow,
                StatusCode = 3,
                Name = "Child"
            }
        };

        var binary = parent.ToBinary();
        var result = binary.BinaryTo<TestParentEntityWithDateTimeChild>();

        Assert.IsNotNull(result);
        Assert.AreEqual(1, result.ParentId);
        Assert.AreEqual("Parent", result.ParentName);
        Assert.IsNotNull(result.Child);
        Assert.AreEqual(100, result.Child.Id);
        Assert.AreEqual(200, result.Child.IntValue);
        Assert.AreEqual(parent.Child.Created.Ticks, result.Child.Created.Ticks, "Child.Created should match");
        Assert.AreEqual(parent.Child.Modified.Ticks, result.Child.Modified.Ticks, "Child.Modified should match");
    }

    [TestMethod]
    public void Deserialize_EntityWithCollectionContainingDateTimeItems_RoundTrip()
    {
        var parent = new TestParentWithDateTimeItemCollection
        {
            Id = 1,
            Name = "Parent",
            Created = DateTime.UtcNow.AddDays(-10),
            Items = CreateDateTimeEntities(5)
        };

        var binary = parent.ToBinary();
        var result = binary.BinaryTo<TestParentWithDateTimeItemCollection>();

        Assert.IsNotNull(result);
        Assert.AreEqual(1, result.Id);
        Assert.AreEqual("Parent", result.Name);
        Assert.AreEqual(parent.Created.Ticks, result.Created.Ticks, "Parent.Created should match");
        Assert.IsNotNull(result.Items);
        Assert.AreEqual(5, result.Items.Count);

        for (int i = 0; i < 5; i++)
        {
            var originalItem = parent.Items[i];
            var deserializedItem = result.Items[i];

            Assert.AreEqual(originalItem.Id, deserializedItem.Id, $"Items[{i}].Id should match");
            Assert.AreEqual(originalItem.IntValue, deserializedItem.IntValue, $"Items[{i}].IntValue should match");
            Assert.AreEqual(originalItem.Created.Ticks, deserializedItem.Created.Ticks, $"Items[{i}].Created should match");
            Assert.AreEqual(originalItem.Modified.Ticks, deserializedItem.Modified.Ticks, $"Items[{i}].Modified should match");
        }
    }

    [TestMethod]
    public void Deserialize_ListOfParentEntitiesWithDateTimeChildCollections_RoundTrip()
    {
        var parents = CreateParentWithDateTimeItems(3, 3);
        var binary = parents.ToBinary();
        var result = binary.BinaryTo<List<TestParentWithDateTimeItemCollection>>();

        Assert.IsNotNull(result);
        Assert.AreEqual(3, result.Count);

        for (int p = 0; p < 3; p++)
        {
            var originalParent = parents[p];
            var deserializedParent = result[p];
            
            Assert.AreEqual(originalParent.Id, deserializedParent.Id, $"Parent[{p}].Id should match");
            Assert.AreEqual(originalParent.Name, deserializedParent.Name, $"Parent[{p}].Name should match");
            Assert.AreEqual(originalParent.Created.Ticks, deserializedParent.Created.Ticks, $"Parent[{p}].Created should match");
            Assert.IsNotNull(deserializedParent.Items);
            Assert.AreEqual(3, deserializedParent.Items.Count, $"Parent[{p}].Items.Count should match");

            for (int i = 0; i < 3; i++)
            {
                var originalItem = originalParent.Items![i];
                var deserializedItem = deserializedParent.Items[i];

                Assert.AreEqual(originalItem.Id, deserializedItem.Id, $"Parent[{p}].Items[{i}].Id should match");
                Assert.AreEqual(originalItem.Created.Ticks, deserializedItem.Created.Ticks, $"Parent[{p}].Items[{i}].Created should match. Expected: {originalItem.Created}, Got: {deserializedItem.Created}");
            }
        }
    }
}
