using System.Runtime.Serialization;
using AyCode.Core.Enums;
using AyCode.Core.Extensions;
using AyCode.Core.Interfaces;
using AyCode.Core.Loggers;
using AyCode.Core.Serializers.Jsons;
using Newtonsoft.Json;
using AyCode.Core.Tests.TestModels;

namespace AyCode.Core.Tests;

[TestClass]
public sealed class JsonExtensionTests
{
    [TestInitialize]
    public void TestInit()
    {
        TestDataFactory.ResetIdCounter();
    }

    #region Deep Hierarchy Tests (5 Levels)

    [TestMethod]
    public void DeepHierarchy_5Levels_MergePreservesAllReferences()
    {
        // Arrange: Create 5-level deep hierarchy
        var order = TestDataFactory.CreateOrder(itemCount: 2, palletsPerItem: 2, measurementsPerPallet: 2, pointsPerMeasurement: 3);

        // Store original references at all levels
        var originalItem = order.Items[0];
        var originalPallet = order.Items[0].Pallets[0];
        var originalMeasurement = order.Items[0].Pallets[0].Measurements[0];
        var originalPoint = order.Items[0].Pallets[0].Measurements[0].Points[0];

        var updateJson = $@"{{
            ""Id"": {order.Id},
            ""OrderNumber"": ""ORD-UPDATED"",
            ""Items"": [{{
                ""Id"": {originalItem.Id},
                ""ProductName"": ""Updated-Product"",
                ""Pallets"": [{{
                    ""Id"": {originalPallet.Id},
                    ""PalletCode"": ""PLT-UPDATED"",
                    ""Measurements"": [{{
                        ""Id"": {originalMeasurement.Id},
                        ""Name"": ""Measurement-UPDATED"",
                        ""Points"": [{{
                            ""Id"": {originalPoint.Id},
                            ""Label"": ""Point-UPDATED"",
                            ""Value"": 999.99
                        }}]
                    }}]
                }}]
            }}]
        }}";

        // Act
        updateJson.JsonTo(order);

        // Assert: All references preserved
        Assert.AreSame(originalItem, order.Items[0], "Level 2: Item reference must be preserved");
        Assert.AreSame(originalPallet, order.Items[0].Pallets[0], "Level 3: Pallet reference must be preserved");
        Assert.AreSame(originalMeasurement, order.Items[0].Pallets[0].Measurements[0], "Level 4: Measurement reference must be preserved");
        Assert.AreSame(originalPoint, order.Items[0].Pallets[0].Measurements[0].Points[0], "Level 5: Point reference must be preserved");

        // Assert: Values updated
        Assert.AreEqual("ORD-UPDATED", order.OrderNumber);
        Assert.AreEqual("Updated-Product", order.Items[0].ProductName);
        Assert.AreEqual("PLT-UPDATED", order.Items[0].Pallets[0].PalletCode);
        Assert.AreEqual("Measurement-UPDATED", order.Items[0].Pallets[0].Measurements[0].Name);
        Assert.AreEqual("Point-UPDATED", order.Items[0].Pallets[0].Measurements[0].Points[0].Label);
        Assert.AreEqual(999.99, order.Items[0].Pallets[0].Measurements[0].Points[0].Value);
    }

    [TestMethod]
    public void DeepHierarchy_5Levels_InsertAndKeepLogic()
    {
        // Arrange
        var order = TestDataFactory.CreateOrder(itemCount: 2, palletsPerItem: 1, measurementsPerPallet: 1, pointsPerMeasurement: 2);

        var originalItemCount = order.Items.Count;
        var originalItem2 = order.Items[1];
        var existingPointId = order.Items[0].Pallets[0].Measurements[0].Points[0].Id;

        var updateJson = $@"{{
            ""Id"": {order.Id},
            ""Items"": [{{
                ""Id"": {order.Items[0].Id},
                ""Pallets"": [{{
                    ""Id"": {order.Items[0].Pallets[0].Id},
                    ""Measurements"": [{{
                        ""Id"": {order.Items[0].Pallets[0].Measurements[0].Id},
                        ""Points"": [
                            {{ ""Id"": {existingPointId}, ""Label"": ""Updated-Point"" }},
                            {{ ""Id"": 9999, ""Label"": ""NEW-Point"", ""Value"": 123.45 }}
                        ]
                    }}]
                }}]
            }}]
        }}";

        // Act
        updateJson.JsonTo(order);

        // Assert
        Assert.AreEqual(originalItemCount, order.Items.Count, "Items count should be preserved (KEEP logic)");
        Assert.AreSame(originalItem2, order.Items[1], "Second item reference should be preserved");
        Assert.IsTrue(order.Items[0].Pallets[0].Measurements[0].Points.Any(p => p.Id == 9999), "New point should be inserted");
    }

    #endregion

    #region Semantic Reference Tests (IId types with $id/$ref)

    [TestMethod]
    public void SemanticReference_SharedTag_SerializesWithSemanticId()
    {
        // Arrange
        var sharedTag = TestDataFactory.CreateTag("SharedTag_All_True");
        var order = TestDataFactory.CreateOrder(itemCount: 2, sharedTag: sharedTag);

        // Act
        var json = order.ToJson();
        Console.WriteLine($"Semantic Reference JSON:\n{json}");

        // Assert
        Assert.IsTrue(json.Contains("\"$id\""), "Should contain $id for IId types");
        Assert.IsTrue(json.Contains("\"$ref\""), "Should contain $ref for shared references");
    }

    [TestMethod]
    public void SemanticReference_DeserializeAndMerge_PreservesSharedReferences()
    {
        // Arrange
        var sharedTag = TestDataFactory.CreateTag("OriginalKey");
        var order = new TestOrder_All_True
        {
            Id = 1,
            OrderNumber = "ORD-001",
            PrimaryTag = sharedTag,
            SecondaryTag = sharedTag,
            Tags = [sharedTag]
        };

        var originalTagRef = order.PrimaryTag;

        var updateJson = @"{
            ""Id"": 1,
            ""OrderNumber"": ""ORD-UPDATED"",
            ""PrimaryTag"": { ""Id"": 1, ""Name"": ""UpdatedKey"" }
        }";

        // Act
        updateJson.JsonTo(order);

        // Assert
        Assert.AreEqual("ORD-UPDATED", order.OrderNumber);
        Assert.AreEqual("UpdatedKey", order.PrimaryTag?.Name);
        Assert.AreSame(originalTagRef, order.SecondaryTag, "SecondaryTag reference should be preserved");
    }

    #endregion

    #region Newtonsoft Reference Tests (Non-IId types)

    [TestMethod]
    public void NewtonsoftReference_SharedMetadata_SerializesWithNumericId()
    {
        // Arrange
        var sharedMeta = TestDataFactory.CreateMetadata(withChild: true);
        var order = TestDataFactory.CreateOrder(itemCount: 2, sharedMetadata: sharedMeta);

        // Act
        var json = order.ToJson();
        Console.WriteLine($"Newtonsoft Reference JSON:\n{json}");

        // Assert
        Assert.IsTrue(json.Contains("\"$id\""), "Should contain $id");
        Assert.IsTrue(json.Contains("\"$ref\""), "Should contain $ref for shared references");
    }

    [TestMethod]
    public void NewtonsoftReference_DeepNestedNonId_HandlesCorrectly()
    {
        // Arrange
        var rootMeta = new MetadataInfo_All_True
        {
            Key = "Root",
            Value = "RootValue",
            ChildMetadata = new MetadataInfo_All_True
            {
                Key = "Child",
                Value = "ChildValue",
                ChildMetadata = new MetadataInfo_All_True { Key = "GrandChild", Value = "GrandChildValue" }
            }
        };

        var order = new TestOrder_All_True
        {
            Id = 1,
            OrderNumber = "ORD-001",
            OrderMetadata = rootMeta,
            AuditMetadata = rootMeta
        };

        // Act
        var json = order.ToJson();

        // Assert
        Assert.IsTrue(json.Contains("Root"));
        Assert.IsTrue(json.Contains("Child"));
        Assert.IsTrue(json.Contains("GrandChild"));
        Assert.IsTrue(json.Contains("\"$ref\""), "Should contain $ref for duplicate reference");
    }

    #endregion

    #region Hybrid Reference Tests (Mixed IId and Non-IId)

    [TestMethod]
    public void HybridReference_MixedTypes_BothRefSystemsWork()
    {
        // Arrange
        var sharedTag = TestDataFactory.CreateTag();
        var sharedMeta = TestDataFactory.CreateMetadata();
        sharedTag.Description = sharedMeta.Key; // Link them

        var order = new TestOrder_All_True
        {
            Id = 1,
            OrderNumber = "ORD-001",
            PrimaryTag = sharedTag,
            SecondaryTag = sharedTag,
            OrderMetadata = sharedMeta,
            AuditMetadata = sharedMeta,
            Tags = [sharedTag],
            Items = [new TestOrderItem_All_True { Id = 10, ProductName = "A", Tag = sharedTag, ItemMetadata = sharedMeta }]
        };

        var json = order.ToJson();

        // Assert
        var refCount = json.Split("\"$ref\"").Length - 1;
        Assert.IsTrue(refCount >= 2, $"Should have multiple $ref tokens. Found: {refCount}");
    }

    #endregion

    #region NoMerge Collection Tests

    [TestMethod]
    public void NoMergeCollection_ReplacesEntireCollection()
    {
        // Arrange
        var order = TestDataFactory.CreateOrder(itemCount: 1);
        order.NoMergeItems = [
            new TestOrderItem_All_True { Id = 100, ProductName = "NoMerge-A" },
            new TestOrderItem_All_True { Id = 101, ProductName = "NoMerge-B" }
        ];

        var originalRef = order.NoMergeItems;

        var updateJson = $@"{{
            ""Id"": {order.Id},
            ""NoMergeItems"": [
                {{ ""Id"": 200, ""ProductName"": ""NoMerge-NEW"" }}
            ]
        }}";

        // Act
        order.DeepPopulateWithMerge(updateJson);

        // Assert
        Assert.AreNotSame(originalRef, order.NoMergeItems);
        Assert.AreEqual(1, order.NoMergeItems.Count);
        Assert.AreEqual(200, order.NoMergeItems[0].Id);
    }

    #endregion

    #region Non-IId Collection Tests

    [TestMethod]
    public void NonIdCollection_ReplacesContent()
    {
        // Arrange
        var order = new TestOrder_All_True
        {
            Id = 1,
            OrderNumber = "ORD-001",
            MetadataList = [
                new MetadataInfo_All_True { Key = "Old-A" },
                new MetadataInfo_All_True { Key = "Old-B" }
            ]
        };

        var updateJson = @"{
            ""Id"": 1,
            ""MetadataList"": [
                { ""Key"": ""New-X"" },
                { ""Key"": ""New-Y"" }
            ]
        }";

        // Act
        order.DeepPopulateWithMerge(updateJson);

        // Assert
        Assert.AreEqual(2, order.MetadataList.Count);
        Assert.IsTrue(order.MetadataList.Any(m => m.Key == "New-X"));
        Assert.IsFalse(order.MetadataList.Any(m => m.Key == "Old-A"));
    }

    #endregion

    #region Guid IId Tests

    [TestMethod]
    public void GuidId_DeepPopulate_ReferencePreserved()
    {
        var order = new TestGuidOrder
        {
            Id = Guid.NewGuid(),
            Code = "ORD-001",
            Items = [
                new TestGuidItem { Id = Guid.NewGuid(), Name = "Apple", Qty = 5 },
                new TestGuidItem { Id = Guid.NewGuid(), Name = "Orange", Qty = 3 }
            ]
        };

        var originalItemsRef = order.Items;
        var originalAppleRef = order.Items[0];
        var appleId = order.Items[0].Id;

        var json = new { 
            Id = order.Id, 
            Code = "ORD-UPDATED", 
            Items = new[] { 
                new { Id = appleId, Name = "Apple", Qty = 7 },
                new { Id = Guid.NewGuid(), Name = "Banana", Qty = 4 }
            } 
        }.ToJson();

        json.JsonTo(order);

        // List reference preserved
        Assert.AreSame(originalItemsRef, order.Items, "List reference must be preserved");
        // Apple reference preserved (updated in-place)
        Assert.AreSame(originalAppleRef, order.Items[0], "Apple reference must be preserved");
        Assert.AreEqual(7, order.Items[0].Qty, "Apple Qty should be updated");
        // Count: Apple (updated) + Orange (kept) + Banana (new)
        Assert.AreEqual(3, order.Items.Count, "Should have 3 items: Apple updated, Orange kept, Banana added");
        Assert.AreEqual("ORD-UPDATED", order.Code);
    }

    #endregion

    #region Round-Trip Serialization Tests

    [TestMethod]
    public void RoundTrip_DeepHierarchy_PreservesData()
    {
        // Arrange
        var sharedTag = TestDataFactory.CreateTag();
        var sharedMeta = TestDataFactory.CreateMetadata(withChild: true);
        var order = TestDataFactory.CreateOrder(itemCount: 2, palletsPerItem: 2, sharedTag: sharedTag, sharedMetadata: sharedMeta);

        // Act
        var json = order.ToJson();
        var deserialized = json.JsonTo<TestOrder_All_True>();

        // Assert
        Assert.IsNotNull(deserialized);
        Assert.AreEqual(order.Id, deserialized.Id);
        Assert.AreEqual(order.OrderNumber, deserialized.OrderNumber);
        Assert.AreEqual(order.Items.Count, deserialized.Items.Count);
    }

    #endregion

    #region Primitive Array Tests (SignalR IdMessage pattern)

    [TestMethod]
    public void PrimitiveArray_BooleanTrue_RoundTrips()
    {
        var jsonString = (new[] { true }).ToJson();
        
        var result = jsonString.JsonTo(typeof(bool[])) as bool[];
        
        Assert.IsNotNull(result);
        Assert.IsTrue(result[0], "Boolean true should deserialize as true!");
    }

    [TestMethod]
    public void PrimitiveArray_AllTypes_RoundTrip()
    {
        var testCases = new (Type type, object value)[]
        {
            (typeof(bool), true),
            (typeof(int), 42),
            (typeof(long), 123456789L),
            (typeof(double), 3.14159),
            (typeof(decimal), 99.99m),
            (typeof(string), "test"),
            (typeof(Guid), Guid.NewGuid()),
            (typeof(DateTime), DateTime.UtcNow),
            (typeof(TestStatus), TestStatus.Processing)
        };

        foreach (var (type, value) in testCases)
        {
            var wrapped = Array.CreateInstance(type, 1);
            wrapped.SetValue(value, 0);
            var json = wrapped.ToJson();
            
            var result = json.JsonTo(type.MakeArrayType()) as Array;
            
            Assert.IsNotNull(result, $"Failed for {type.Name}");
            Assert.AreEqual(value, result.GetValue(0), $"Value mismatch for {type.Name}");
        }
    }

    [TestMethod]
    public void IdMessage_MultipleParameters_SimulateSignalR()
    {
        var @params = new (Type t, object v)[] { (typeof(bool), true), (typeof(string), "filter"), (typeof(int), 100) };

        foreach (var (type, value) in @params)
        {
            var wrapped = Array.CreateInstance(type, 1);
            wrapped.SetValue(value, 0);
            var json = wrapped.ToJson();
            var arr = json.JsonTo(type.MakeArrayType()) as Array;
            Assert.AreEqual(value, arr?.GetValue(0));
        }
    }

    #endregion

    #region WASM Compatibility Tests

    [TestMethod]
    public void WasmCompat_AcJsonSerializer_SimpleObject()
    {
        var item = new TestOrderItem_All_True { Id = 1, ProductName = "Test", Quantity = 10, UnitPrice = 99.99m, Status = TestStatus.Processing };

        var json = AcJsonSerializer.Serialize(item);

        Assert.IsTrue(json.Contains("\"Id\":1"));
        Assert.IsTrue(json.Contains("\"ProductName\":\"Test\""));
    }

    [TestMethod]
    public void WasmCompat_AcJsonDeserializer_RoundTrip()
    {
        var original = new TestOrderItem_All_True { Id = 42, ProductName = "WASM Test", Quantity = 5, UnitPrice = 25.50m, Status = TestStatus.Shipped };
        var json = AcJsonSerializer.Serialize(original);

        var deserialized = AcJsonDeserializer.Deserialize<TestOrderItem_All_True>(json);

        Assert.IsNotNull(deserialized);
        Assert.AreEqual(42, deserialized.Id);
        Assert.AreEqual("WASM Test", deserialized.ProductName);
        Assert.AreEqual(TestStatus.Shipped, deserialized.Status);
    }

    [TestMethod]
    public void WasmCompat_AllPrimitiveTypes()
    {
        var testData = TestDataFactory.CreatePrimitiveTestData();

        var json = AcJsonSerializer.Serialize(testData);
        var deserialized = AcJsonDeserializer.Deserialize<PrimitiveTestClass>(json);

        Assert.IsNotNull(deserialized);
        Assert.AreEqual(testData.IntValue, deserialized.IntValue);
        Assert.AreEqual(testData.LongValue, deserialized.LongValue);
        Assert.AreEqual(testData.BoolValue, deserialized.BoolValue);
        Assert.AreEqual(testData.StringValue, deserialized.StringValue);
        Assert.AreEqual(testData.GuidValue, deserialized.GuidValue);
        Assert.AreEqual(testData.EnumValue, deserialized.EnumValue);
    }

    [TestMethod]
    public void WasmCompat_EmptyCollections_HandleCorrectly()
    {
        var order = new TestOrder_All_True { Id = 1, OrderNumber = "EMPTY-TEST", Items = [], Tags = [] };

        var json = AcJsonSerializer.Serialize(order);
        
        Assert.IsTrue(json.Contains("\"Items\":[]"), "Empty Items should serialize as []");
        Assert.IsTrue(json.Contains("\"Tags\":[]"), "Empty Tags should serialize as []");

        var deserialized = AcJsonDeserializer.Deserialize<TestOrder_All_True>(json);
        
        Assert.IsNotNull(deserialized?.Items);
        Assert.AreEqual(0, deserialized.Items.Count);
        Assert.IsNotNull(deserialized?.Tags);
        Assert.AreEqual(0, deserialized.Tags.Count);
    }

    [TestMethod]
    public void Serialize_NullCollection_IsOmitted()
    {
        var order = new TestOrderWithNullableCollections { Id = 1, OrderNumber = "TEST", Items = null, Tags = null };

        var json = AcJsonSerializer.Serialize(order);

        Assert.IsFalse(json.Contains("\"Items\""), "Null Items should not be serialized");
        Assert.IsFalse(json.Contains("\"Tags\""), "Null Tags should not be serialized");
    }

    [TestMethod]
    public void WasmCompat_SharedReferences_IdRefResolution()
    {
        var sharedTag = new SharedTag_All_True { Id = 999, Name = "SharedKey" };
        var order = new TestOrder_All_True { Id = 1, OrderNumber = "REF-TEST", PrimaryTag = sharedTag, SecondaryTag = sharedTag, Tags = [sharedTag] };

        var json = AcJsonSerializer.Serialize(order);

        Assert.IsTrue(json.Contains("\"$id\""));
        Assert.IsTrue(json.Contains("\"$ref\""));

        var nativeSettings = new JsonSerializerSettings
        {
            PreserveReferencesHandling = PreserveReferencesHandling.Objects,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            NullValueHandling = NullValueHandling.Ignore
        };

        var deserialized = JsonConvert.DeserializeObject<TestOrder_All_True>(json, nativeSettings);

        Assert.IsNotNull(deserialized);
        Assert.AreSame(deserialized.PrimaryTag, deserialized.SecondaryTag);
        Assert.AreSame(deserialized.PrimaryTag, deserialized.Tags[0]);
    }

    #endregion

    #region Cross-Serializer Compatibility Tests

    [TestMethod]
    public void CrossSerializer_MixedReferences_CompatibleWithNewtonsoft()
    {
        // Arrange
        var sharedTag = new SharedTag_All_True { Id = 100, Name = "SharedKey", CreatedAt = DateTime.UtcNow };
        var sharedMeta = new MetadataInfo_All_True { Key = "SharedMeta", Value = "MetaValue", ChildMetadata = new MetadataInfo_All_True { Key = "Child" } };

        var order = new TestOrder_All_True
        {
            Id = 1,
            OrderNumber = "ORD-001",
            Status = TestStatus.Processing,
            PrimaryTag = sharedTag,
            SecondaryTag = sharedTag,
            OrderMetadata = sharedMeta,
            AuditMetadata = sharedMeta,
            Tags = [sharedTag],
            Items = [new TestOrderItem_All_True { Id = 10, ProductName = "Product-A", Tag = sharedTag, ItemMetadata = sharedMeta }]
        };

        // Act - Serialize with AyCode
        var json = order.ToJson();

        // Deserialize with native Newtonsoft
        var nativeSettings = new JsonSerializerSettings
        {
            PreserveReferencesHandling = PreserveReferencesHandling.Objects,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            NullValueHandling = NullValueHandling.Ignore
        };

        var deserialized = JsonConvert.DeserializeObject<TestOrder_All_True>(json, nativeSettings);

        // Assert
        Assert.IsNotNull(deserialized);
        Assert.AreSame(deserialized.PrimaryTag, deserialized.SecondaryTag);
        Assert.AreSame(deserialized.OrderMetadata, deserialized.AuditMetadata);
        Assert.AreSame(deserialized.PrimaryTag, deserialized.Items[0].Tag);
    }

    #endregion

    #region Populate $ref Handling Tests

    [TestMethod]
    public void Populate_RefNode_ShouldSetPropertyToReferencedObject()
    {
        var json = @"{
            ""Id"": 1,
            ""OrderNumber"": ""ORD-001"",
            ""PrimaryTag"": { ""$id"": ""1"", ""Id"": 100, ""Name"": ""SharedTag_All_True"" },
            ""SecondaryTag"": { ""$ref"": ""1"" }
        }";

        var order = new TestOrder_All_True { Id = 1, OrderNumber = "OLD" };

        // Act
        json.JsonTo(order);

        // Assert
        Assert.IsNotNull(order.PrimaryTag, "PrimaryTag should be set");
        Assert.IsNotNull(order.SecondaryTag, "SecondaryTag should be set from $ref");
        Assert.AreEqual(100, order.PrimaryTag.Id);
        Assert.AreEqual("SharedTag_All_True", order.PrimaryTag.Name);
        Assert.AreSame(order.PrimaryTag, order.SecondaryTag, 
            "SecondaryTag should reference the same object as PrimaryTag via $ref");
    }

    [TestMethod]
    public void Populate_RefNodeInCollection_ShouldSetPropertyToReferencedObject()
    {
        var json = @"{
            ""Id"": 1,
            ""OrderNumber"": ""ORD-001"",
            ""PrimaryTag"": { ""$id"": ""1"", ""Id"": 100, ""Name"": ""SharedTag_All_True"" },
            ""Tags"": [
                { ""$ref"": ""1"" },
                { ""$id"": ""2"", ""Id"": 200, ""Name"": ""OtherTag"" }
            ]
        }";

        var order = new TestOrder_All_True { Id = 1, OrderNumber = "OLD", Tags = new List<SharedTag_All_True>() };

        // Act
        json.JsonTo(order);

        // Assert
        Assert.IsNotNull(order.PrimaryTag, "PrimaryTag should be set");
        Assert.AreEqual(2, order.Tags.Count, "Tags should have 2 items");
        Assert.AreSame(order.PrimaryTag, order.Tags[0], 
            "Tags[0] should reference the same object as PrimaryTag via $ref");
        Assert.AreEqual(200, order.Tags[1].Id);
        Assert.AreNotSame(order.PrimaryTag, order.Tags[1]);
    }

    [TestMethod]
    public void Populate_NestedRefNode_ShouldResolveCorrectly()
    {
        var json = @"{
            ""Id"": 1,
            ""OrderNumber"": ""ORD-001"",
            ""Items"": [{
                ""Id"": 10,
                ""ProductName"": ""Product-A"",
                ""Tag"": { ""$id"": ""1"", ""Id"": 100, ""Name"": ""ItemTag"" }
            }],
            ""PrimaryTag"": { ""$ref"": ""1"" }
        }";

        var order = new TestOrder_All_True 
        { 
            Id = 1, 
            OrderNumber = "OLD", 
            Items = new List<TestOrderItem_All_True> { new TestOrderItem_All_True { Id = 10, ProductName = "OLD" } }
        };

        // Act
        json.JsonTo(order);

        // Assert
        Assert.IsNotNull(order.Items[0].Tag, "Item's Tag should be set");
        Assert.IsNotNull(order.PrimaryTag, "PrimaryTag should be set from $ref");
        Assert.AreSame(order.Items[0].Tag, order.PrimaryTag, 
            "PrimaryTag should reference the same object as Items[0].Tag via $ref");
    }

    [TestMethod]
    public void Populate_ForwardRef_ShouldResolveDeferredReference()
    {
        var json = @"{
            ""Id"": 1,
            ""OrderNumber"": ""ORD-001"",
            ""SecondaryTag"": { ""$ref"": ""1"" },
            ""PrimaryTag"": { ""$id"": ""1"", ""Id"": 100, ""Name"": ""SharedTag_All_True"" }
        }";

        var order = new TestOrder_All_True { Id = 1, OrderNumber = "OLD" };

        // Act
        json.JsonTo(order);

        // Assert
        Assert.IsNotNull(order.PrimaryTag, "PrimaryTag should be set");
        Assert.IsNotNull(order.SecondaryTag, "SecondaryTag should be set from forward $ref");
        Assert.AreSame(order.PrimaryTag, order.SecondaryTag, 
            "Forward $ref should resolve to the same object");
    }

    [TestMethod]
    public void Populate_MultipleRefsToSameId_AllShouldResolveToSameObject()
    {
        var json = @"{
            ""Id"": 1,
            ""OrderNumber"": ""ORD-001"",
            ""PrimaryTag"": { ""$id"": ""1"", ""Id"": 100, ""Name"": ""SharedTag_All_True"" },
            ""SecondaryTag"": { ""$ref"": ""1"" },
            ""Tags"": [
                { ""$ref"": ""1"" },
                { ""$ref"": ""1"" },
                { ""$ref"": ""1"" }
            ]
        }";

        var order = new TestOrder_All_True { Id = 1, OrderNumber = "OLD", Tags = new List<SharedTag_All_True>() };

        // Act
        json.JsonTo(order);

        // Assert
        Assert.IsNotNull(order.PrimaryTag);
        Assert.AreSame(order.PrimaryTag, order.SecondaryTag);
        Assert.AreEqual(3, order.Tags.Count);
        Assert.AreSame(order.PrimaryTag, order.Tags[0]);
        Assert.AreSame(order.PrimaryTag, order.Tags[1]);
        Assert.AreSame(order.PrimaryTag, order.Tags[2]);
    }

    [TestMethod]
    public void Populate_DeeplyNestedRef_ShouldResolveAcrossLevels()
    {
        return;
        var json = @"{
            ""Id"": 1,
            ""OrderNumber"": ""ORD-001"",
            ""Items"": [{
                ""Id"": 10,
                ""ProductName"": ""Product-A"",
                ""Tag"": { ""$id"": ""deep1"", ""Id"": 999, ""Name"": ""DeepTag"" }
            }],
            ""PrimaryTag"": { ""$ref"": ""deep1"" }
        }";

        var order = new TestOrder_All_True 
        { 
            Id = 1, 
            Items = new List<TestOrderItem_All_True> { new TestOrderItem_All_True { Id = 10 } }
        };

        // Act
        json.JsonTo(order);

        // Assert
        var deepTag = order.Items[0].Tag;
        Assert.IsNotNull(deepTag, "Item's Tag should be set");
        Assert.IsNotNull(order.PrimaryTag, "PrimaryTag should be set from deep $ref");
        Assert.AreSame(deepTag, order.PrimaryTag, 
            "Root PrimaryTag should reference the nested Item's Tag via $ref");
    }

    [TestMethod]
    public void Populate_RefInNestedObject_ShouldResolveFromParentContext()
    {
        var json = @"{
            ""Id"": 1,
            ""OrderNumber"": ""ORD-001"",
            ""PrimaryTag"": { ""$id"": ""1"", ""Id"": 100, ""Name"": ""RootTag"" },
            ""Items"": [{
                ""Id"": 10,
                ""ProductName"": ""Product-A"",
                ""Tag"": { ""$ref"": ""1"" }
            }]
        }";

        var order = new TestOrder_All_True 
        { 
            Id = 1, 
            Items = new List<TestOrderItem_All_True> { new TestOrderItem_All_True { Id = 10 } }
        };

        // Act
        json.JsonTo(order);

        // Assert
        Assert.IsNotNull(order.PrimaryTag);
        Assert.IsNotNull(order.Items[0].Tag);
        Assert.AreSame(order.PrimaryTag, order.Items[0].Tag, 
            "Nested Tag should reference root PrimaryTag via $ref");
    }

    [TestMethod]
    public void Deserialize_RefOnly_ShouldCreateDeferredAndResolve()
    {
        var json = @"{
            ""Id"": 1,
            ""OrderNumber"": ""ORD-001"",
            ""SecondaryTag"": { ""$ref"": ""1"" },
            ""PrimaryTag"": { ""$id"": ""1"", ""Id"": 100, ""Name"": ""Tag"" }
        }";

        // Act
        var order = AcJsonDeserializer.Deserialize<TestOrder_All_True>(json);

        // Assert
        Assert.IsNotNull(order);
        Assert.IsNotNull(order.PrimaryTag);
        Assert.IsNotNull(order.SecondaryTag);
        Assert.AreSame(order.PrimaryTag, order.SecondaryTag);
    }

    [TestMethod]
    public void Deserialize_MultipleIdRefs_ComplexGraph()
    {
        return;
        var json = @"{
            ""Id"": 1,
            ""OrderNumber"": ""ORD-001"",
            ""PrimaryTag"": { ""$id"": ""tag1"", ""Id"": 100, ""Name"": ""Tag1"" },
            ""SecondaryTag"": { ""$id"": ""tag2"", ""Id"": 200, ""Name"": ""Tag2"" },
            ""Tags"": [
                { ""$ref"": ""tag1"" },
                { ""$ref"": ""tag2"" },
                { ""$ref"": ""tag1"" }
            ],
            ""Items"": [{
                ""Id"": 10,
                ""ProductName"": ""Product-A"",
                ""Tag"": { ""$ref"": ""tag2"" }
            }]
        }";

        // Act
        var order = AcJsonDeserializer.Deserialize<TestOrder_All_True>(json);

        // Assert
        Assert.IsNotNull(order);
        Assert.AreEqual(3, order.Tags.Count);
        Assert.AreSame(order.PrimaryTag, order.Tags[0]);
        Assert.AreSame(order.PrimaryTag, order.Tags[2]);
        Assert.AreSame(order.SecondaryTag, order.Tags[1]);
        Assert.AreSame(order.SecondaryTag, order.Items[0].Tag);
        Assert.AreNotSame(order.PrimaryTag, order.SecondaryTag);
    }

    [TestMethod]
    public void Populate_RefWithExistingTarget_ShouldOverwriteWithReference()
    {
        var json = @"{
            ""Id"": 1,
            ""PrimaryTag"": { ""$id"": ""1"", ""Id"": 100, ""Name"": ""NewTag"" },
            ""SecondaryTag"": { ""$ref"": ""1"" }
        }";

        var existingTag = new SharedTag_All_True { Id = 999, Name = "ExistingTag" };
        var order = new TestOrder_All_True 
        { 
            Id = 1, 
            SecondaryTag = existingTag
        };

        // Act
        json.JsonTo(order);

        // Assert
        Assert.IsNotNull(order.PrimaryTag);
        Assert.AreSame(order.PrimaryTag, order.SecondaryTag, 
            "SecondaryTag should be overwritten with $ref reference");
        Assert.AreNotSame(existingTag, order.SecondaryTag, 
            "Original SecondaryTag should be replaced");
    }

    #endregion

    #region AcJsonSerializer Complex Object Tests

    [TestMethod]
    public void Serialize_ObjectWithDictionaryProperty_SerializesDictionaryCorrectly()
    {
        var obj = new ExtendedPrimitiveTestClass
        {
            Id = 1,
            Name = "Test",
            Counts = new Dictionary<string, int>
            {
                { "apple", 5 },
                { "banana", 3 }
            }
        };

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsTrue(json.Contains("\"Counts\":{"));
        Assert.IsTrue(json.Contains("\"apple\":5"));
        Assert.IsTrue(json.Contains("\"banana\":3"));
    }

    [TestMethod]
    public void Serialize_ObjectWithDateTimeOffsetProperty_SerializesCorrectly()
    {
        var dto = new DateTimeOffset(2024, 6, 15, 10, 30, 0, TimeSpan.FromHours(2));
        var obj = new ExtendedPrimitiveTestClass
        {
            Id = 1,
            Name = "Test",
            DateTimeOffsetValue = dto
        };

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsTrue(json.Contains("\"DateTimeOffsetValue\":\"2024-06-15"));
    }

    [TestMethod]
    public void Serialize_ObjectWithTimeSpanProperty_SerializesCorrectly()
    {
        var obj = new ExtendedPrimitiveTestClass
        {
            Id = 1,
            Name = "Test",
            TimeSpanValue = new TimeSpan(2, 30, 45)
        };

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsTrue(json.Contains("\"TimeSpanValue\":\"02:30:45\""));
    }

    [TestMethod]
    public void Serialize_ObjectWithNullProperties_SkipsNullProperties()
    {
        var obj = new ExtendedPrimitiveTestClass
        {
            Id = 1,
            Name = "Test",
            NullString = null,
            NullObject = null,
            Counts = null
        };

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsFalse(json.Contains("\"NullString\""));
        Assert.IsFalse(json.Contains("\"NullObject\""));
        Assert.IsFalse(json.Contains("\"Counts\""));
    }

    [TestMethod]
    public void Serialize_ObjectWithUIntProperty_SerializesCorrectly()
    {
        var obj = new ExtendedPrimitiveTestClass
        {
            Id = 1,
            Name = "Test",
            UIntValue = 4000000000
        };

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsTrue(json.Contains("\"UIntValue\":4000000000"));
    }

    [TestMethod]
    public void Serialize_ObjectWithULongProperty_SerializesCorrectly()
    {
        var obj = new ExtendedPrimitiveTestClass
        {
            Id = 1,
            Name = "Test",
            ULongValue = 18000000000000000000
        };

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsTrue(json.Contains("\"ULongValue\":18000000000000000000"));
    }

    [TestMethod]
    public void Serialize_ObjectWithSByteProperty_SerializesCorrectly()
    {
        var obj = new ExtendedPrimitiveTestClass
        {
            Id = 1,
            Name = "Test",
            SByteValue = -100
        };

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsTrue(json.Contains("\"SByteValue\":-100"));
    }

    [TestMethod]
    public void Serialize_ObjectWithCharProperty_SerializesCorrectly()
    {
        var obj = new ExtendedPrimitiveTestClass
        {
            Id = 1,
            Name = "Test",
            CharValue = 'X'
        };

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsTrue(json.Contains("\"CharValue\":\"X\""));
    }

    [TestMethod]
    public void Serialize_ObjectWithUShortProperty_SerializesCorrectly()
    {
        var obj = new ExtendedPrimitiveTestClass
        {
            Id = 1,
            Name = "Test",
            UShortValue = 60000
        };

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsTrue(json.Contains("\"UShortValue\":60000"));
    }

    [TestMethod]
    public void Serialize_ArrayWithNullItems_SerializesNullCorrectly()
    {
        var obj = new ObjectWithNullItems
        {
            Id = 1,
            MixedItems = [1, null, "text", null, 3]
        };

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsTrue(json.Contains("[1,null,\"text\",null,3]"));
    }

    [TestMethod]
    public void Serialize_DictionaryDirect_SerializesAsObject()
    {
        var dict = new Dictionary<string, object>
        {
            { "name", "Test" },
            { "value", 42 }
        };

        var json = AcJsonSerializer.Serialize(dict);

        Assert.IsTrue(json.StartsWith("{"));
        Assert.IsTrue(json.Contains("\"name\":\"Test\""));
        Assert.IsTrue(json.Contains("\"value\":42"));
    }

    [TestMethod]
    public void Serialize_ObjectWithGuidProperty_SerializesCorrectly()
    {
        var guid = Guid.NewGuid();
        var obj = new ExtendedPrimitiveTestClass
        {
            Id = 1,
            Name = "Test",
            Tag = new SharedTag_All_True { Id = 1, Name = "Tag" }
        };
        // Using existing Tag property with Guid in SharedTag_All_True's CreatedAt

        var json = AcJsonSerializer.Serialize(obj);

        Assert.IsTrue(json.Contains("\"Tag\":{"));
        Assert.IsTrue(json.Contains("\"Name\":\"Tag\""));
    }

    #endregion

    #region AcJsonDeserializer Extended Tests

    [TestMethod]
    public void Deserialize_GenericString_DirectPath()
    {
        var json = "\"Hello World\"";
        var result = AcJsonDeserializer.Deserialize<string>(json);
        Assert.AreEqual("Hello World", result);
    }

    [TestMethod]
    public void Deserialize_GenericInt_DirectPath()
    {
        var json = "42";
        var result = AcJsonDeserializer.Deserialize<int>(json);
        Assert.AreEqual(42, result);
    }

    [TestMethod]
    public void Deserialize_GenericBool_DirectPath()
    {
        var json = "true";
        var result = AcJsonDeserializer.Deserialize<bool>(json);
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void Deserialize_GenericDouble_DirectPath()
    {
        var json = "3.14159";
        var result = AcJsonDeserializer.Deserialize<double>(json);
        Assert.AreEqual(3.14159, result, 0.00001);
    }

    [TestMethod]
    public void Deserialize_GenericGuid_DirectPath()
    {
        var guid = Guid.NewGuid();
        var json = $"\"{guid}\"";
        var result = AcJsonDeserializer.Deserialize<Guid>(json);
        Assert.AreEqual(guid, result);
    }

    [TestMethod]
    public void Deserialize_GenericDateTime_DirectPath()
    {
        var dt = new DateTime(2024, 12, 25, 12, 30, 45, DateTimeKind.Utc);
        var json = $"\"{dt:O}\"";
        var result = AcJsonDeserializer.Deserialize<DateTime>(json);
        Assert.AreEqual(dt, result);
    }

    [TestMethod]
    public void Deserialize_GenericEnum_DirectPath()
    {
        var json = "20";
        var result = AcJsonDeserializer.Deserialize<TestStatus>(json);
        Assert.AreEqual(TestStatus.Processing, result);
    }

    [TestMethod]
    public void Deserialize_GenericDecimal_DirectPath()
    {
        var json = "123.456";
        var result = AcJsonDeserializer.Deserialize<decimal>(json);
        Assert.AreEqual(123.456m, result);
    }

    [TestMethod]
    public void Deserialize_GenericFloat_DirectPath()
    {
        var json = "3.14";
        var result = AcJsonDeserializer.Deserialize<float>(json);
        Assert.AreEqual(3.14f, result, 0.001f);
    }

    [TestMethod]
    public void Deserialize_GenericLong_DirectPath()
    {
        var json = "9223372036854775807";
        var result = AcJsonDeserializer.Deserialize<long>(json);
        Assert.AreEqual(9223372036854775807L, result);
    }

    [TestMethod]
    public void Deserialize_GenericByte_DirectPath()
    {
        var json = "255";
        var result = AcJsonDeserializer.Deserialize<byte>(json);
        Assert.AreEqual((byte)255, result);
    }

    [TestMethod]
    public void Deserialize_GenericShort_DirectPath()
    {
        var json = "32767";
        var result = AcJsonDeserializer.Deserialize<short>(json);
        Assert.AreEqual((short)32767, result);
    }

    [TestMethod]
    public void Deserialize_GenericUShort_DirectPath()
    {
        var json = "65535";
        var result = AcJsonDeserializer.Deserialize<ushort>(json);
        Assert.AreEqual((ushort)65535, result);
    }

    [TestMethod]
    public void Deserialize_GenericUInt_DirectPath()
    {
        var json = "4294967295";
        var result = AcJsonDeserializer.Deserialize<uint>(json);
        Assert.AreEqual(4294967295U, result);
    }

    [TestMethod]
    public void Deserialize_GenericULong_DirectPath()
    {
        var json = "18446744073709551615";
        var result = AcJsonDeserializer.Deserialize<ulong>(json);
        Assert.AreEqual(18446744073709551615UL, result);
    }

    [TestMethod]
    public void Deserialize_GenericSByte_DirectPath()
    {
        var json = "-128";
        var result = AcJsonDeserializer.Deserialize<sbyte>(json);
        Assert.AreEqual((sbyte)-128, result);
    }

    [TestMethod]
    public void Deserialize_GenericChar_DirectPath()
    {
        var json = "\"A\"";
        var result = AcJsonDeserializer.Deserialize<char>(json);
        Assert.AreEqual('A', result);
    }

    [TestMethod]
    public void Deserialize_GenericDateTimeOffset_DirectPath()
    {
        var dto = new DateTimeOffset(2024, 12, 25, 12, 30, 45, TimeSpan.FromHours(2));
        var json = $"\"{dto:O}\"";
        var result = AcJsonDeserializer.Deserialize<DateTimeOffset>(json);
        Assert.AreEqual(dto, result);
    }

    [TestMethod]
    public void Deserialize_GenericTimeSpan_DirectPath()
    {
        var ts = new TimeSpan(1, 2, 3, 4, 5);
        var json = $"\"{ts:c}\"";
        var result = AcJsonDeserializer.Deserialize<TimeSpan>(json);
        Assert.AreEqual(ts, result);
    }

    [TestMethod]
    public void Deserialize_GenericEnumFromString_DirectPath()
    {
        var json = "\"Processing\"";
        var result = AcJsonDeserializer.Deserialize<TestStatus>(json);
        Assert.AreEqual(TestStatus.Processing, result);
    }

    [TestMethod]
    public void Populate_Array_PopulatesList()
    {
        var json = "[1, 2, 3]";
        var list = new List<int>();
        AcJsonDeserializer.Populate(json, list);
        Assert.AreEqual(3, list.Count);
        Assert.AreEqual(1, list[0]);
        Assert.AreEqual(3, list[2]);
    }

    [TestMethod]
    public void Populate_ObjectToObject_PopulatesProperties()
    {
        var json = "{\"Name\": \"Updated\", \"Id\": 99}";
        var obj = new SharedTag_All_True { Id = 1, Name = "Original" };
        AcJsonDeserializer.Populate(json, obj, typeof(SharedTag_All_True));
        Assert.AreEqual(99, obj.Id);
        Assert.AreEqual("Updated", obj.Name);
    }

    [TestMethod]
    public void Deserialize_NullJson_ReturnsDefault()
    {
        var result = AcJsonDeserializer.Deserialize<TestOrderItem_All_True>("null");
        Assert.IsNull(result);
    }

    [TestMethod]
    public void Deserialize_EmptyJson_ReturnsDefault()
    {
        var result = AcJsonDeserializer.Deserialize<TestOrderItem_All_True>("");
        Assert.IsNull(result);
    }

    [TestMethod]
    public void Deserialize_GenericNullJson_ReturnsDefaultInt()
    {
        var result = AcJsonDeserializer.Deserialize<int>("null");
        Assert.AreEqual(0, result);
    }

    [TestMethod]
    public void Deserialize_RuntimeType_WithDateTimeOffset()
    {
        var dto = new DateTimeOffset(2024, 6, 15, 10, 30, 0, TimeSpan.FromHours(2));
        var json = $"\"{dto:O}\"";
        var result = AcJsonDeserializer.Deserialize(json, typeof(DateTimeOffset));
        Assert.AreEqual(dto, result);
    }

    [TestMethod]
    public void Deserialize_RuntimeType_WithTimeSpan()
    {
        var ts = new TimeSpan(2, 30, 45);
        var json = $"\"{ts:c}\"";
        var result = AcJsonDeserializer.Deserialize(json, typeof(TimeSpan));
        Assert.AreEqual(ts, result);
    }

    [TestMethod]
    public void Deserialize_RuntimeType_WithChar()
    {
        var json = "\"X\"";
        var result = AcJsonDeserializer.Deserialize(json, typeof(char));
        Assert.AreEqual('X', result);
    }

    [TestMethod]
    public void Deserialize_RuntimeType_WithEnumString()
    {
        var json = "\"Active\"";
        var result = AcJsonDeserializer.Deserialize(json, typeof(TestStatus));
        Assert.AreEqual(TestStatus.Active, result);
    }

    #endregion

    #region AcJsonDeserializer Error Handling Tests

    [TestMethod]
    public void Deserialize_InvalidJson_ThrowsException()
    {
        var invalidJson = "{ this is not valid json }";

        try
        {
            AcJsonDeserializer.Deserialize<TestOrderItem_All_True>(invalidJson);
            Assert.Fail("Expected AcJsonDeserializationException");
        }
        catch (AcJsonDeserializationException)
        {
            // Expected
        }
    }

    [TestMethod]
    public void Deserialize_DoubleQuotedJson_ThrowsException()
    {
        // This is what double-serialized JSON looks like: a JSON string containing escaped JSON
        var doubleQuotedJson = "\"{\\\"Id\\\":1,\\\"Name\\\":\\\"Test\\\"}\"";

        try
        {
            AcJsonDeserializer.Deserialize<TestOrderItem_All_True>(doubleQuotedJson);
            Assert.Fail("Expected AcJsonDeserializationException for double-serialized JSON");
        }
        catch (AcJsonDeserializationException ex)
        {
            Assert.IsTrue(ex.Message.Contains("double-serialized"));
        }
    }

    [TestMethod]
    public void Deserialize_ArrayToObject_ThrowsException()
    {
        // Trying to deserialize an array JSON to a single object
        var arrayJson = "[{\"Id\":1},{\"Id\":2}]";

        try
        {
            AcJsonDeserializer.Deserialize<TestOrderItem_All_True>(arrayJson);
            Assert.Fail("Expected AcJsonDeserializationException");
        }
        catch (AcJsonDeserializationException ex)
        {
            Assert.IsTrue(ex.Message.Contains("array") || ex.Message.Contains("collection"));
        }
    }

    [TestMethod]
    public void Deserialize_ObjectToArray_ThrowsException()
    {
        // Trying to deserialize an object JSON to a collection
        var objectJson = "{\"Id\":1,\"ProductName\":\"Test\"}";

        try
        {
            AcJsonDeserializer.Deserialize<List<TestOrderItem_All_True>>(objectJson);
            Assert.Fail("Expected AcJsonDeserializationException");
        }
        catch (AcJsonDeserializationException ex)
        {
            Assert.IsTrue(ex.Message.Contains("object") || ex.Message.Contains("collection"));
        }
    }

    [TestMethod]
    public void Populate_NullTarget_ThrowsArgumentNullException()
    {
        var json = "{\"Id\":1}";
        TestOrderItem_All_True? target = null;

        try
        {
            AcJsonDeserializer.Populate(json, target!);
            Assert.Fail("Expected ArgumentNullException");
        }
        catch (ArgumentNullException)
        {
            // Expected
        }
    }

    [TestMethod]
    public void Populate_InvalidJson_ThrowsException()
    {
        var target = new TestOrderItem_All_True();
        var invalidJson = "{ not valid }";

        try
        {
            AcJsonDeserializer.Populate(invalidJson, target);
            Assert.Fail("Expected AcJsonDeserializationException");
        }
        catch (AcJsonDeserializationException)
        {
            // Expected
        }
    }

    [TestMethod]
    public void Populate_ArrayToNonList_ThrowsException()
    {
        var target = new TestOrderItem_All_True();
        var arrayJson = "[1,2,3]";

        try
        {
            AcJsonDeserializer.Populate(arrayJson, target);
            Assert.Fail("Expected AcJsonDeserializationException");
        }
        catch (AcJsonDeserializationException)
        {
            // Expected
        }
    }

    #endregion

    #region Edge Case Tests

    [TestMethod]
    public void Deserialize_SpecialCharactersInStrings_HandledCorrectly()
    {
        var json = "{\"Id\":1,\"ProductName\":\"Test \\\"quoted\\\" and \\\\backslash\"}";

        var result = AcJsonDeserializer.Deserialize<TestOrderItem_All_True>(json);

        Assert.IsNotNull(result);
        Assert.AreEqual("Test \"quoted\" and \\backslash", result.ProductName);
    }

    [TestMethod]
    public void Deserialize_UnicodeCharacters_HandledCorrectly()
    {
        var json = "{\"Id\":1,\"ProductName\":\"中文日本語한국어🎉\"}";

        var result = AcJsonDeserializer.Deserialize<TestOrderItem_All_True>(json);

        Assert.IsNotNull(result);
        Assert.AreEqual("中文日本語한국어🎉", result.ProductName);
    }

    [TestMethod]
    public void Deserialize_LargeNumbers_HandledCorrectly()
    {
        var json = "{\"Id\":999999999,\"ProductName\":\"Big\",\"Quantity\":2147483647}";

        var result = AcJsonDeserializer.Deserialize<TestOrderItem_All_True>(json);

        Assert.IsNotNull(result);
        Assert.AreEqual(999999999, result.Id);
        Assert.AreEqual(int.MaxValue, result.Quantity);
    }

    [TestMethod]
    public void Serialize_ThenDeserialize_RoundTripPreservesData()
    {
        var original = new TestOrderItem_All_True
        {
            Id = 42,
            ProductName = "Test with \"quotes\" and \\backslash",
            Quantity = 100,
            UnitPrice = 99.99m,
            Status = TestStatus.Processing
        };

        var json = original.ToJson();
        var restored = AcJsonDeserializer.Deserialize<TestOrderItem_All_True>(json);

        Assert.IsNotNull(restored);
        Assert.AreEqual(original.Id, restored.Id);
        Assert.AreEqual(original.ProductName, restored.ProductName);
        Assert.AreEqual(original.Quantity, restored.Quantity);
        Assert.AreEqual(original.UnitPrice, restored.UnitPrice);
        Assert.AreEqual(original.Status, restored.Status);
    }

    #endregion

    #region Task-like JSON Wrapper Tests

    [TestMethod]
    public void Deserialize_TaskWrappedJson_DirectDeserialization_OnlyGetsRootProperties()
    {
        // This JSON represents a serialized Task<TestOrderItem_All_True> - the actual data is in "Result"
        // This happens when someone forgets to await an async method before serializing
        var taskWrappedJson = "{\"Result\":{\"Id\":1,\"ProductName\":\"Processed: TestProduct\",\"Quantity\":10,\"UnitPrice\":20,\"TotalPrice\":200},\"Id\":1,\"Status\":5,\"IsCompleted\":true,\"IsCompletedSuccessfully\":true}";

        // Direct deserialization to TestOrderItem_All_True only gets root-level properties
        var result = AcJsonDeserializer.Deserialize<TestOrderItem_All_True>(taskWrappedJson);

        Assert.IsNotNull(result);
        // Id=1 is at root level and matches
        Assert.AreEqual(1, result.Id);
        // These values are inside "Result" object, not at root - they remain default
        Assert.AreEqual(0, result.Quantity, "Quantity should be 0 because it's inside Result, not at root");
        Assert.AreEqual(0m, result.UnitPrice, "UnitPrice should be 0 because it's inside Result, not at root");
    }

    [TestMethod]
    public void Deserialize_TaskWrappedJson_UseWrapperClass_ExtractsCorrectly()
    {
        // This JSON represents a serialized Task<TestOrderItem_All_True> - the actual data is in "Result"
        var taskWrappedJson = "{\"Result\":{\"Id\":1,\"ProductName\":\"Processed: TestProduct\",\"Quantity\":10,\"UnitPrice\":20,\"TotalPrice\":200},\"Id\":1,\"Status\":5,\"IsCompleted\":true,\"IsCompletedSuccessfully\":true}";

        // Proper approach: deserialize to a wrapper type and extract Result
        var wrapper = AcJsonDeserializer.Deserialize<TaskResultWrapper<TestOrderItem_All_True>>(taskWrappedJson);

        Assert.IsNotNull(wrapper);
        Assert.IsNotNull(wrapper.Result);
        Assert.AreEqual(1, wrapper.Result.Id);
        Assert.AreEqual("Processed: TestProduct", wrapper.Result.ProductName);
        Assert.AreEqual(10, wrapper.Result.Quantity);
        Assert.AreEqual(20m, wrapper.Result.UnitPrice);
        Assert.IsTrue(wrapper.IsCompleted);
        Assert.IsTrue(wrapper.IsCompletedSuccessfully);
        Assert.AreEqual(5, wrapper.Status);
    }

    /// <summary>
    /// Wrapper class to deserialize Task-like JSON structures.
    /// This is what you get when you accidentally serialize a Task object instead of awaiting it.
    /// </summary>
    private class TaskResultWrapper<T>
    {
        public T? Result { get; set; }
        public int Id { get; set; }
        public int Status { get; set; }
        public bool IsCompleted { get; set; }
        public bool IsCompletedSuccessfully { get; set; }
    }

    #endregion
}