using AyCode.Core.Extensions;
using AyCode.Core.Tests.TestModels;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using MessagePack;
using MessagePack.Resolvers;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using JsonSerializer = System.Text.Json.JsonSerializer;
using MongoDB.Bson;
using MongoDB.Bson.IO;
using MongoDB.Bson.Serialization;
using System.IO;
using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Serializers.Jsons;
using AyCode.Core.Serializers;

namespace AyCode.Core.Benchmarks;

/// <summary>
/// Minimal benchmark to test if BenchmarkDotNet works without stack overflow.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
public class MinimalBenchmark
{
    private byte[] _data = null!;
    private string _json = null!;

    [GlobalSetup]
    public void Setup()
    {
        // Use very simple data - no circular references
        var simpleData = new { Id = 1, Name = "Test", Value = 42.5 };
        _json = System.Text.Json.JsonSerializer.Serialize(simpleData);
        _data = Encoding.UTF8.GetBytes(_json);
        Console.WriteLine($"Setup complete. Data size: {_data.Length} bytes");
    }

    [Benchmark]
    public int GetLength() => _data.Length;

    [Benchmark]
    public string GetJson() => _json;
}

/// <summary>
/// Binary vs JSON benchmark with simple flat objects (no circular references).
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
public class SimpleBinaryBenchmark
{
    private PrimitiveTestClass _testData = null!;
    private byte[] _binaryData = null!;
    private string _jsonData = null!;

    [GlobalSetup]
    public void Setup()
    {
        _testData = TestDataFactory.CreatePrimitiveTestData();
        _binaryData = AcBinarySerializer.Serialize(_testData, AcBinarySerializerOptions.WithoutReferenceHandling);
        _jsonData = AcJsonSerializer.Serialize(_testData, AcJsonSerializerOptions.WithoutReferenceHandling);
        
        Console.WriteLine($"Binary: {_binaryData.Length} bytes, JSON: {_jsonData.Length} chars");
    }

    [Benchmark(Description = "Binary Serialize")]
    public byte[] SerializeBinary() => AcBinarySerializer.Serialize(_testData, AcBinarySerializerOptions.WithoutReferenceHandling);

    [Benchmark(Description = "JSON Serialize", Baseline = true)]
    public string SerializeJson() => AcJsonSerializer.Serialize(_testData, AcJsonSerializerOptions.WithoutReferenceHandling);

    [Benchmark(Description = "Binary Deserialize")]
    public PrimitiveTestClass? DeserializeBinary() => AcBinaryDeserializer.Deserialize<PrimitiveTestClass>(_binaryData);

    [Benchmark(Description = "JSON Deserialize")]
    public PrimitiveTestClass? DeserializeJson() => AcJsonDeserializer.Deserialize<PrimitiveTestClass>(_jsonData, AcJsonSerializerOptions.WithoutReferenceHandling);
}

/// <summary>
/// Complex hierarchy benchmark - AcBinary vs JSON only (no MessagePack to isolate the issue).
/// Uses AcBinary without reference handling.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class ComplexBinaryBenchmark
{
    private TestOrder _testOrder = null!;
    private byte[] _acBinaryData = null!;
    private string _jsonData = null!;

    private AcBinarySerializerOptions _binaryOptions = null!;
    private AcJsonSerializerOptions _jsonOptions = null!;

    [GlobalSetup]
    public void Setup()
    {
        Console.WriteLine("Creating test data...");
        _testOrder = TestDataFactory.CreateBenchmarkOrder(
            itemCount: 2,
            palletsPerItem: 2,
            measurementsPerPallet: 2,
            pointsPerMeasurement: 3);
        Console.WriteLine($"Created order with {_testOrder.Items.Count} items");

        _binaryOptions = AcBinarySerializerOptions.WithoutReferenceHandling;
        _jsonOptions = AcJsonSerializerOptions.WithoutReferenceHandling;

        Console.WriteLine("Serializing AcBinary...");
        _acBinaryData = AcBinarySerializer.Serialize(_testOrder, _binaryOptions);
        Console.WriteLine($"AcBinary size: {_acBinaryData.Length} bytes");

        Console.WriteLine("Serializing JSON...");
        _jsonData = AcJsonSerializer.Serialize(_testOrder, _jsonOptions);
        Console.WriteLine($"JSON size: {_jsonData.Length} chars");

        var jsonBytes = Encoding.UTF8.GetByteCount(_jsonData);
        Console.WriteLine($"\n=== SIZE COMPARISON ===");
        Console.WriteLine($"AcBinary: {_acBinaryData.Length,8:N0} bytes ({100.0 * _acBinaryData.Length / jsonBytes:F1}%)");
        Console.WriteLine($"JSON:     {jsonBytes,8:N0} bytes (100.0%)");
    }

    [Benchmark(Description = "AcBinary Serialize")]
    public byte[] Serialize_AcBinary() => AcBinarySerializer.Serialize(_testOrder, _binaryOptions);

    [Benchmark(Description = "JSON Serialize", Baseline = true)]
    public string Serialize_Json() => AcJsonSerializer.Serialize(_testOrder, _jsonOptions);

    [Benchmark(Description = "AcBinary Deserialize")]
    public TestOrder? Deserialize_AcBinary() => AcBinaryDeserializer.Deserialize<TestOrder>(_acBinaryData);

    [Benchmark(Description = "JSON Deserialize")]
    public TestOrder? Deserialize_Json() => AcJsonDeserializer.Deserialize<TestOrder>(_jsonData, _jsonOptions);
}

/// <summary>
/// Full comparison with MessagePack and BSON - AcBinary uses NO reference handling everywhere.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class MessagePackComparisonBenchmark
{
    private TestOrder _testOrder = null!;
    private byte[] _acBinaryData = null!;
    private byte[] _msgPackData = null!;
    private byte[] _bsonData = null!;
    private string _jsonData = null!;

    private AcBinarySerializerOptions _binaryOptions = null!;
    private MessagePackSerializerOptions _msgPackOptions = null!;
    private AcJsonSerializerOptions _jsonOptions = null!;

    [GlobalSetup]
    public void Setup()
    {
        Console.WriteLine("Creating test data...");
        _testOrder = TestDataFactory.CreateBenchmarkOrder(
            itemCount: 2,
            palletsPerItem: 2,
            measurementsPerPallet: 2,
            pointsPerMeasurement: 3);

        _binaryOptions = AcBinarySerializerOptions.WithoutReferenceHandling;
        _msgPackOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);
        _jsonOptions = AcJsonSerializerOptions.WithoutReferenceHandling;

        _acBinaryData = AcBinarySerializer.Serialize(_testOrder, _binaryOptions);
        _jsonData = AcJsonSerializer.Serialize(_testOrder, _jsonOptions);
        
        // MessagePack serialization in try-catch to see if it fails
        try
        {
            Console.WriteLine("Serializing MessagePack...");
            _msgPackData = MessagePackSerializer.Serialize(_testOrder, _msgPackOptions);
            Console.WriteLine($"MessagePack size: {_msgPackData.Length} bytes");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"MessagePack serialization failed: {ex.Message}");
            _msgPackData = Array.Empty<byte>();
        }

        // BSON serialization
        try
        {
            Console.WriteLine("Serializing BSON...");
            var bsonDoc = _testOrder.ToBsonDocument();
            _bsonData = bsonDoc.ToBson();
            Console.WriteLine($"BSON size: {_bsonData.Length} bytes");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"BSON serialization failed: {ex.Message}");
            _bsonData = Array.Empty<byte>();
        }

        var jsonBytes = Encoding.UTF8.GetByteCount(_jsonData);
        Console.WriteLine($"\n=== SIZE COMPARISON ===");
        Console.WriteLine($"AcBinary:    {_acBinaryData.Length,8:N0} bytes ({100.0 * _acBinaryData.Length / jsonBytes:F1}%)");
        Console.WriteLine($"MessagePack: {_msgPackData.Length,8:N0} bytes ({100.0 * _msgPackData.Length / jsonBytes:F1}%)");
        Console.WriteLine($"BSON:         {_bsonData.Length,8:N0} bytes ({100.0 * _bsonData.Length / jsonBytes:F1}%)");
        Console.WriteLine($"JSON:        {jsonBytes,8:N0} bytes (100.0%)");
    }

    [Benchmark(Description = "AcBinary Serialize")]
    public byte[] Serialize_AcBinary() => AcBinarySerializer.Serialize(_testOrder, _binaryOptions);

    [Benchmark(Description = "MessagePack Serialize", Baseline = true)]
    public byte[] Serialize_MsgPack() => MessagePackSerializer.Serialize(_testOrder, _msgPackOptions);

    [Benchmark(Description = "BSON Serialize")]
    public byte[] Serialize_Bson() => _testOrder.ToBsonDocument().ToBson();

    [Benchmark(Description = "AcBinary Deserialize")]
    public TestOrder? Deserialize_AcBinary() => AcBinaryDeserializer.Deserialize<TestOrder>(_acBinaryData);

    [Benchmark(Description = "MessagePack Deserialize")]
    public TestOrder? Deserialize_MsgPack() => MessagePackSerializer.Deserialize<TestOrder>(_msgPackData, _msgPackOptions);

    [Benchmark(Description = "BSON Deserialize")]
    public TestOrder? Deserialize_Bson()
    {
        if (_bsonData == null || _bsonData.Length == 0) return null;
        using var ms = new MemoryStream(_bsonData);
        using var reader = new BsonBinaryReader(ms);
        return BsonSerializer.Deserialize<TestOrder>(reader);
    }
}

/// <summary>
/// Comprehensive AcBinary vs MessagePack comparison benchmark.
/// Tests: NoRef (everywhere), Populate, Serialize, Deserialize, Size
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class AcBinaryVsMessagePackFullBenchmark
{
    // Test data
    private TestOrder _testOrder = null!;
    private TestOrder _populateTarget = null!;
    
    // Serialized data - AcBinary
    private byte[] _acBinaryWithRef = null!;
    private byte[] _acBinaryNoRef = null!;
    
    // Serialized data - MessagePack
    private byte[] _msgPackData = null!;
    private byte[] _bsonData = null!;
    
    // Options
    private AcBinarySerializerOptions _withRefOptions = null!;
    private AcBinarySerializerOptions _noRefOptions = null!;
    private MessagePackSerializerOptions _msgPackOptions = null!;

    [GlobalSetup]
    public void Setup()
    {
        // Create test data with shared references
        TestDataFactory.ResetIdCounter();
        var sharedTag = TestDataFactory.CreateTag("SharedTag");
        var sharedUser = TestDataFactory.CreateUser("shareduser");
        var sharedMeta = TestDataFactory.CreateMetadata("shared", withChild: true);
        
        _testOrder = TestDataFactory.CreateOrder(
            itemCount: 3,
            palletsPerItem: 3,
            measurementsPerPallet: 3,
            pointsPerMeasurement: 4,
            sharedTag: sharedTag,
            sharedUser: sharedUser,
            sharedMetadata: sharedMeta);

        // Setup options - WithRef uses Default (which has reference handling), NoRef explicitly disables it
        _withRefOptions = AcBinarySerializerOptions.Default;
        _noRefOptions = AcBinarySerializerOptions.WithoutReferenceHandling;
        _msgPackOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);

        // Serialize with different options
        _acBinaryWithRef = AcBinarySerializer.Serialize(_testOrder, _withRefOptions);
        _acBinaryNoRef = AcBinarySerializer.Serialize(_testOrder, _noRefOptions);
        _msgPackData = MessagePackSerializer.Serialize(_testOrder, _msgPackOptions);

        // BSON
        try
        {
            _bsonData = _testOrder.ToBsonDocument().ToBson();
        }
        catch
        {
            _bsonData = Array.Empty<byte>();
        }

        // Create populate target
        _populateTarget = new TestOrder { Id = _testOrder.Id };
        foreach (var item in _testOrder.Items)
        {
            _populateTarget.Items.Add(new TestOrderItem { Id = item.Id });
        }

        // Print size comparison
        PrintSizeComparison();
    }

    private void PrintSizeComparison()
    {
        Console.WriteLine("\n" + new string('=', 60));
        Console.WriteLine("?? SIZE COMPARISON (AcBinary vs MessagePack vs BSON)");
        Console.WriteLine(new string('=', 60));
        Console.WriteLine($"  AcBinary WithRef:  {_acBinaryWithRef.Length,8:N0} bytes");
        Console.WriteLine($"  AcBinary NoRef:    {_acBinaryNoRef.Length,8:N0} bytes");
        Console.WriteLine($"  MessagePack:       {_msgPackData.Length,8:N0} bytes");
        Console.WriteLine($"  BSON:              {_bsonData.Length,8:N0} bytes");
        Console.WriteLine(new string('-', 60));
        Console.WriteLine($"  AcBinary/MsgPack:  {100.0 * _acBinaryWithRef.Length / Math.Max(1, _msgPackData.Length):F1}% (WithRef)");
        Console.WriteLine($"  AcBinary/MsgPack:  {100.0 * _acBinaryNoRef.Length / Math.Max(1, _msgPackData.Length):F1}% (NoRef)");
        Console.WriteLine(new string('=', 60) + "\n");
    }

    #region Serialize Benchmarks

    [Benchmark(Description = "AcBinary Serialize WithRef")]
    public byte[] Serialize_AcBinary_WithRef() => AcBinarySerializer.Serialize(_testOrder, _withRefOptions);

    [Benchmark(Description = "AcBinary Serialize NoRef")]
    public byte[] Serialize_AcBinary_NoRef() => AcBinarySerializer.Serialize(_testOrder, _noRefOptions);

    [Benchmark(Description = "MessagePack Serialize", Baseline = true)]
    public byte[] Serialize_MsgPack() => MessagePackSerializer.Serialize(_testOrder, _msgPackOptions);

    [Benchmark(Description = "BSON Serialize")]
    public byte[] Serialize_Bson() => _testOrder.ToBsonDocument().ToBson();

    #endregion

    #region Deserialize Benchmarks

    [Benchmark(Description = "AcBinary Deserialize WithRef")]
    public TestOrder? Deserialize_AcBinary_WithRef() => AcBinaryDeserializer.Deserialize<TestOrder>(_acBinaryWithRef);

    [Benchmark(Description = "AcBinary Deserialize NoRef")]
    public TestOrder? Deserialize_AcBinary_NoRef() => AcBinaryDeserializer.Deserialize<TestOrder>(_acBinaryNoRef);

    [Benchmark(Description = "MessagePack Deserialize")]
    public TestOrder? Deserialize_MsgPack() => MessagePackSerializer.Deserialize<TestOrder>(_msgPackData, _msgPackOptions);

    [Benchmark(Description = "BSON Deserialize")]
    public TestOrder? Deserialize_Bson()
    {
        if (_bsonData == null || _bsonData.Length == 0) return null;
        using var ms = new MemoryStream(_bsonData);
        using var reader = new BsonBinaryReader(ms);
        return BsonSerializer.Deserialize<TestOrder>(reader);
    }

    #endregion

    #region Populate Benchmarks

    [Benchmark(Description = "AcBinary Populate WithRef")]
    public void Populate_AcBinary_WithRef()
    {
        var target = CreatePopulateTarget();
        AcBinaryDeserializer.Populate(_acBinaryWithRef, target);
    }

    [Benchmark(Description = "AcBinary Populate NoRef")]
    public void Populate_AcBinary_NoRef()
    {
        // Create fresh target each time to avoid state accumulation
        var target = CreatePopulateTarget();
        AcBinaryDeserializer.Populate(_acBinaryNoRef, target);
    }

    [Benchmark(Description = "AcBinary PopulateMerge WithRef")]
    public void PopulateMerge_AcBinary_WithRef()
    {
        var target = CreatePopulateTarget();
        AcBinaryDeserializer.PopulateMerge(_acBinaryWithRef, target);
    }

    [Benchmark(Description = "AcBinary PopulateMerge NoRef")]
    public void PopulateMerge_AcBinary_NoRef()
    {
        // Create fresh target each time to avoid state accumulation
        var target = CreatePopulateTarget();
        AcBinaryDeserializer.PopulateMerge(_acBinaryNoRef, target);
    }

    private TestOrder CreatePopulateTarget()
    {
        var target = new TestOrder { Id = _testOrder.Id };
        foreach (var item in _testOrder.Items)
        {
            target.Items.Add(new TestOrderItem { Id = item.Id });
        }
        return target;
    }

    #endregion
}

/// <summary>
/// Detailed size comparison - not a performance benchmark, just size output.
/// Now includes BSON size output and uses AcBinary without reference handling.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
public class SizeComparisonBenchmark
{
    private TestOrder _smallOrder = null!;
    private TestOrder _mediumOrder = null!;
    private TestOrder _largeOrder = null!;
    
    private MessagePackSerializerOptions _msgPackOptions = null!;
    private AcBinarySerializerOptions _withRefOptions = null!;
    private AcBinarySerializerOptions _noRefOptions = null!;

    [GlobalSetup]
    public void Setup()
    {
        _msgPackOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);
        _withRefOptions = AcBinarySerializerOptions.WithoutReferenceHandling;
        _noRefOptions = AcBinarySerializerOptions.WithoutReferenceHandling;

        // Small order
        TestDataFactory.ResetIdCounter();
        _smallOrder = TestDataFactory.CreateOrder(itemCount: 1, palletsPerItem: 1, measurementsPerPallet: 1, pointsPerMeasurement: 2);
        
        // Medium order
        TestDataFactory.ResetIdCounter();
        var sharedTag = TestDataFactory.CreateTag("Shared");
        var sharedUser = TestDataFactory.CreateUser("shared");
        _mediumOrder = TestDataFactory.CreateOrder(
            itemCount: 3, palletsPerItem: 2, measurementsPerPallet: 2, pointsPerMeasurement: 3,
            sharedTag: sharedTag, sharedUser: sharedUser);
        
        // Large order
        TestDataFactory.ResetIdCounter();
        sharedTag = TestDataFactory.CreateTag("SharedLarge");
        sharedUser = TestDataFactory.CreateUser("sharedlarge");
        var sharedMeta = TestDataFactory.CreateMetadata("meta", withChild: true);
        _largeOrder = TestDataFactory.CreateOrder(
            itemCount: 5, palletsPerItem: 4, measurementsPerPallet: 3, pointsPerMeasurement: 5,
            sharedTag: sharedTag, sharedUser: sharedUser, sharedMetadata: sharedMeta);

        PrintDetailedSizeComparison();
    }

    private void PrintDetailedSizeComparison()
    {
        Console.WriteLine("\n" + new string('=', 80));
        Console.WriteLine("?? DETAILED SIZE COMPARISON: AcBinary vs MessagePack vs BSON");
        Console.WriteLine(new string('=', 80));
        
        PrintOrderSize("Small Order (1x1x1x2)", _smallOrder);
        PrintOrderSize("Medium Order (3x2x2x3) + SharedRefs", _mediumOrder);
        PrintOrderSize("Large Order (5x4x3x5) + SharedRefs", _largeOrder);
        
        Console.WriteLine(new string('=', 80) + "\n");
    }

    private void PrintOrderSize(string name, TestOrder order)
    {
        var acWithRef = AcBinarySerializer.Serialize(order, _withRefOptions);
        var acNoRef = AcBinarySerializer.Serialize(order, _noRefOptions);
        var msgPack = MessagePackSerializer.Serialize(order, _msgPackOptions);
        byte[] bson;
        try { bson = order.ToBsonDocument().ToBson(); } catch { bson = Array.Empty<byte>(); }
        
        Console.WriteLine($"\n  {name}:");
        Console.WriteLine($"    AcBinary WithRef:  {acWithRef.Length,8:N0} bytes ({100.0 * acWithRef.Length / Math.Max(1, msgPack.Length),5:F1}% of MsgPack)");
        Console.WriteLine($"    AcBinary NoRef:    {acNoRef.Length,8:N0} bytes ({100.0 * acNoRef.Length / Math.Max(1, msgPack.Length),5:F1}% of MsgPack)");
        Console.WriteLine($"    MessagePack:       {msgPack.Length,8:N0} bytes (100.0%)");
        Console.WriteLine($"    BSON:              {bson.Length,8:N0} bytes (compared to MsgPack)");
        
        var withRefSaving = msgPack.Length - acWithRef.Length;
        var noRefSaving = msgPack.Length - acNoRef.Length;
        if (withRefSaving > 0)
            Console.WriteLine($"    ?? AcBinary WithRef saves: {withRefSaving:N0} bytes ({100.0 * withRefSaving / msgPack.Length:F1}%)");
        else
            Console.WriteLine($"    ?? AcBinary WithRef larger by: {-withRefSaving:N0} bytes");
    }

    [Benchmark(Description = "Placeholder")]
    public int Placeholder() => 1; // Just to make BenchmarkDotNet happy
}

public enum BinaryBenchmarkMode
{
    Default,
    NoReferenceHandling,
    FastMode
}

public abstract class AcBinaryOptionsBenchmarkBase
{
    protected TestOrder TestOrder = null!;
    protected AcBinarySerializerOptions BinaryOptions = null!;
    protected MessagePackSerializerOptions MsgPackOptions = null!;
    protected byte[] AcBinaryData = null!;
    protected byte[] MsgPackData = null!;

    [Params(BinaryBenchmarkMode.Default, BinaryBenchmarkMode.NoReferenceHandling, BinaryBenchmarkMode.FastMode)]
    public BinaryBenchmarkMode Mode { get; set; }

    [GlobalSetup]
    public void GlobalSetup()
    {
        TestDataFactory.ResetIdCounter();
        TestOrder = TestDataFactory.CreateBenchmarkOrder(
            itemCount: 4,
            palletsPerItem: 3,
            measurementsPerPallet: 3,
            pointsPerMeasurement: 6);

        BinaryOptions = CreateBinaryOptions(Mode);
        MsgPackOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);

        AcBinaryData = AcBinarySerializer.Serialize(TestOrder, BinaryOptions);
        MsgPackData = MessagePackSerializer.Serialize(TestOrder, MsgPackOptions);

        var ratio = MsgPackData.Length == 0 ? 0 : 100.0 * AcBinaryData.Length / MsgPackData.Length;
        Console.WriteLine($"[BenchmarkSetup] Mode={Mode} | AcBinary={AcBinaryData.Length} bytes | MessagePack={MsgPackData.Length} bytes | Ratio={ratio:F1}%");
    }

    private static AcBinarySerializerOptions CreateBinaryOptions(BinaryBenchmarkMode mode) => mode switch
    {
        BinaryBenchmarkMode.Default => new AcBinarySerializerOptions(),
        BinaryBenchmarkMode.NoReferenceHandling => AcBinarySerializerOptions.WithoutReferenceHandling,
        BinaryBenchmarkMode.FastMode => new AcBinarySerializerOptions
        {
            UseMetadata = false,
            UseStringInterning = StringInterningMode.None,
            ReferenceHandling = ReferenceHandlingMode.None,
        },
        _ => new AcBinarySerializerOptions()
    };
}

[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class AcBinaryOptionsSerializeBenchmark : AcBinaryOptionsBenchmarkBase
{
    [Benchmark(Description = "MessagePack Serialize", Baseline = true)]
    public byte[] Serialize_MessagePack() => MessagePackSerializer.Serialize(TestOrder, MsgPackOptions);

    [Benchmark(Description = "AcBinary Serialize")]
    public byte[] Serialize_AcBinary() => AcBinarySerializer.Serialize(TestOrder, BinaryOptions);
}

[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class AcBinaryOptionsDeserializeBenchmark : AcBinaryOptionsBenchmarkBase
{
    [Benchmark(Description = "MessagePack Deserialize", Baseline = true)]
    public TestOrder? Deserialize_MessagePack() => MessagePackSerializer.Deserialize<TestOrder>(MsgPackData, MsgPackOptions);

    [Benchmark(Description = "AcBinary Deserialize")]
    public TestOrder? Deserialize_AcBinary() => AcBinaryDeserializer.Deserialize<TestOrder>(AcBinaryData);
}

/// <summary>
/// Large-scale benchmark simulating production workloads.
/// Tests with ~50,000+ IId objects with deep hierarchy and shared references.
/// This is closer to real-world scenarios with 2200 root items and 4-5MB binary data.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class LargeScaleBinaryBenchmark
{
    // Test data - smaller scale for benchmark (500 items ? 25K objects)
    // Production would be 2200 items ? 100K+ objects
    private TestOrder _testOrder = null!;
    private TestOrder _populateTarget = null!;
    
    // Serialized data
    private byte[] _acBinaryData = null!;
    private byte[] _msgPackData = null!;

    // Options
    private AcBinarySerializerOptions _binaryOptions = null!;
    private MessagePackSerializerOptions _msgPackOptions = null!;

    private int _objectCount;

    [GlobalSetup]
    public void Setup()
    {
        Console.WriteLine("Creating large-scale test data...");
        
        // Use 500 root items for benchmark (?25K objects)
        // Production would use 2200 (?100K+ objects)
        const int rootItems = 500;
        const int pallets = 3;
        const int measurements = 3;
        const int points = 4;
        
        _objectCount = TestDataFactory.CalculateObjectCount(rootItems, pallets, measurements, points);
        Console.WriteLine($"Creating ~{_objectCount:N0} IId objects...");
        
        _testOrder = TestDataFactory.CreateLargeScaleBenchmarkOrder(rootItems, pallets, measurements, points);
        Console.WriteLine($"Created order with {_testOrder.Items.Count} root items");

        _binaryOptions = AcBinarySerializerOptions.WithoutReferenceHandling;
        _msgPackOptions = ContractlessStandardResolver.Options.WithCompression(MessagePackCompression.None);

        Console.WriteLine("Serializing AcBinary...");
        _acBinaryData = AcBinarySerializer.Serialize(_testOrder, _binaryOptions);
        
        Console.WriteLine("Serializing MessagePack...");
        _msgPackData = MessagePackSerializer.Serialize(_testOrder, _msgPackOptions);

        // Create populate target
        _populateTarget = new TestOrder { Id = _testOrder.Id };
        foreach (var item in _testOrder.Items.Take(10)) // Only first 10 for populate target
        {
            _populateTarget.Items.Add(new TestOrderItem { Id = item.Id });
        }

        PrintStats();
    }

    private void PrintStats()
    {
        Console.WriteLine("\n" + new string('=', 70));
        Console.WriteLine("?? LARGE-SCALE BENCHMARK STATS");
        Console.WriteLine(new string('=', 70));
        Console.WriteLine($"  Root Items:     {_testOrder.Items.Count:N0}");
        Console.WriteLine($"  Total Objects:  ~{_objectCount:N0} IId objects");
        Console.WriteLine($"  AcBinary Size:  {_acBinaryData.Length:N0} bytes ({_acBinaryData.Length / 1024.0 / 1024.0:F2} MB)");
        Console.WriteLine($"  MsgPack Size:   {_msgPackData.Length:N0} bytes ({_msgPackData.Length / 1024.0 / 1024.0:F2} MB)");
        Console.WriteLine($"  Size Ratio:     {100.0 * _acBinaryData.Length / _msgPackData.Length:F1}% of MsgPack");
        Console.WriteLine(new string('=', 70) + "\n");
    }

    [Benchmark(Description = "LargeScale AcBinary Deserialize")]
    public TestOrder? Deserialize_AcBinary() => AcBinaryDeserializer.Deserialize<TestOrder>(_acBinaryData);

    [Benchmark(Description = "LargeScale MsgPack Deserialize", Baseline = true)]
    public TestOrder? Deserialize_MsgPack() => MessagePackSerializer.Deserialize<TestOrder>(_msgPackData, _msgPackOptions);

    [Benchmark(Description = "LargeScale AcBinary Serialize")]
    public byte[] Serialize_AcBinary() => AcBinarySerializer.Serialize(_testOrder, _binaryOptions);

    [Benchmark(Description = "LargeScale MsgPack Serialize")]
    public byte[] Serialize_MsgPack() => MessagePackSerializer.Serialize(_testOrder, _msgPackOptions);
}

/// <summary>
/// AcJson vs System.Text.Json comparison - measures Newtonsoft.Json based AcJson against modern STJ.
/// Uses simple flat object (PrimitiveTestClass) to avoid circular reference issues.
/// </summary>
[ShortRunJob]
[MemoryDiagnoser]
[RankColumn]
public class AcJsonVsSystemTextJsonBenchmark
{
    private PrimitiveTestClass _testData = null!;
    private string _acJsonData = null!;
    private string _stjData = null!;
    private AcJsonSerializerOptions _acJsonOptions = null!;
    private JsonSerializerOptions _stjOptions = null!;

    [GlobalSetup]
    public void Setup()
    {
        Console.WriteLine("Creating test data for AcJson vs System.Text.Json...");
        
        // Use simple flat object to avoid circular reference issues
        _testData = TestDataFactory.CreatePrimitiveTestData();

        // Setup options
        _acJsonOptions = AcJsonSerializerOptions.WithoutReferenceHandling;
        _stjOptions = new JsonSerializerOptions
        {
            WriteIndented = false,
            DefaultIgnoreCondition = JsonIgnoreCondition.Never,
            ReferenceHandler = null // No reference handling
        };

        // Pre-serialize
        _acJsonData = AcJsonSerializer.Serialize(_testData, _acJsonOptions);
        _stjData = JsonSerializer.Serialize(_testData, _stjOptions);
        
        Console.WriteLine($"AcJson size: {_acJsonData.Length:N0} chars");
        Console.WriteLine($"STJ size:    {_stjData.Length:N0} chars");
        Console.WriteLine($"Size ratio:  {100.0 * _acJsonData.Length / _stjData.Length:F1}%");
    }

    [Benchmark(Description = "AcJson Serialize", Baseline = true)]
    public string Serialize_AcJson() => 
        AcJsonSerializer.Serialize(_testData, _acJsonOptions);

    [Benchmark(Description = "System.Text.Json Serialize")]
    public string Serialize_STJ() => 
        JsonSerializer.Serialize(_testData, _stjOptions);

    [Benchmark(Description = "AcJson Deserialize")]
    public PrimitiveTestClass? Deserialize_AcJson() => 
        AcJsonDeserializer.Deserialize<PrimitiveTestClass>(_acJsonData, _acJsonOptions);

    [Benchmark(Description = "System.Text.Json Deserialize")]
    public PrimitiveTestClass? Deserialize_STJ() => 
        JsonSerializer.Deserialize<PrimitiveTestClass>(_stjData, _stjOptions);
}