using AyCode.Core.Serializers;
using AyCode.Core.Tests.TestModels;
using MemoryPack;
using System.Buffers;
using System.Runtime.CompilerServices;

namespace AyCode.Core.Benchmarks.Workloads.Scenarios;

/// <summary>
/// Benchmarks MemoryPack via the IBufferWriter overload with a pre-allocated, reused ArrayBufferWriter.
/// Apples-to-apples counterpart to <see cref="AcBinaryBufferWriterBenchmark{T}"/> — MemoryPack's IBufferWriter
/// is the path it's designed for.
/// </summary>
public sealed class MemoryPackBufferWriterBenchmark<T> : ISerializerBenchmark where T : class
{
    private readonly T _order;
    private readonly MemoryPackSerializerOptions _options;
    private readonly byte[] _serialized;
    private readonly ArrayBufferWriter<byte> _bufferWriter;

    public BenchmarkEngine Engine => BenchmarkEngine.MemoryPack;
    public BenchmarkIoMode IoMode => BenchmarkIoMode.BufWrReuse;
    public BenchmarkDispatchMode DispatchMode => BenchmarkDispatchMode.SGen; // MemoryPack always uses [MemoryPackable] source-generated formatters
    public Type OrderType => typeof(T);
    public string OptionsPreset { get; }
    public int SerializedSize => _serialized.Length;
    public long SetupSerializeAllocBytes { get; }
    public long SetupDeserializeAllocBytes => 0;
    public string? OptionsDescription => $"StringEncoding={_options.StringEncoding}";

    public MemoryPackBufferWriterBenchmark(T order, WireMode wireMode, string optionsPreset)
    {
        _order = order;
        OptionsPreset = optionsPreset;
        _options = BenchmarkOptions.GetMemPack(wireMode);
        _serialized = MemoryPackSerializer.Serialize(order, _options);

        // Serialize-side setup only — see AcBinaryBufferWriterBenchmark for the full rationale.
        GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
        var beforeSetup = GC.GetAllocatedBytesForCurrentThread();
        _bufferWriter = new ArrayBufferWriter<byte>(_serialized.Length * 2);
        var afterSetup = GC.GetAllocatedBytesForCurrentThread();
        SetupSerializeAllocBytes = afterSetup - beforeSetup;
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    public void Serialize()
    {
        _bufferWriter.ResetWrittenCount();
        MemoryPackSerializer.Serialize(_bufferWriter, _order, _options);
    }

    // BufWr semantic: read from a ReadOnlySequence<byte> overload (apples-to-apples with AcBinary's
    // BufWr Deser path). MemoryPack's ROS overload also single-segment-fast-paths internally.
    [MethodImpl(MethodImplOptions.NoInlining)]
    public void Deserialize() => MemoryPackSerializer.Deserialize<T>(new ReadOnlySequence<byte>(_serialized), _options);

    public bool VerifyRoundTrip()
    {
        _bufferWriter.ResetWrittenCount();
        MemoryPackSerializer.Serialize(_bufferWriter, _order, _options);
        var roundTripped = MemoryPackSerializer.Deserialize<T>(new ReadOnlySequence<byte>(_bufferWriter.WrittenMemory), _options);
        return RoundTripValidator.DeepEqualsViaJson(_order, roundTripped);
    }
}
