using AyCode.Core.Benchmarks.Workloads.Scenarios;
using AyCode.Core.Serializers;
using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Tests.TestModels;
using BenchmarkDotNet.Attributes;

namespace AyCode.Core.Benchmarks;

/// <summary>
/// BDN benchmark mirroring the Console app's "F" menu (<c>SerializerSelectionMode.FastestByte</c>) —
/// the focused 1:1 comparison between <b>AcBinary FastMode Byte[]</b> and <b>MemoryPack Default Byte[]</b>
/// across the 5 production-shaped test data cells (Small / Medium / Large / Repeated / Deep).
///
/// <para>Why this exists: the Console app's adaptive measurement engine gives fast turnaround but is
/// noise-prone; BDN's warmup + iteration + outlier-removal stack tightens the inter-engine delta to
/// the point where ~1-2% micro-optimizations become detectable. Both runners feed the SAME
/// <see cref="ISerializerBenchmark"/>-implementing workload (<see cref="AcBinaryBenchmark{T}"/> /
/// <see cref="MemoryPackBenchmark{T}"/>) — so the BDN numbers are directly comparable to Console's
/// <c>Console.FullBenchmark_Release_*.LLM</c> rows, only with tighter confidence intervals.</para>
///
/// <para>Output: BDN writes its native artifacts to <c>Test_Benchmark_Results/Benchmark/BDN/</c> (set
/// globally in <c>Program.cs</c> via <c>WithArtifactsPath</c>). <see cref="BdnSummaryAdapter"/> then
/// translates the <see cref="BenchmarkDotNet.Reports.Summary"/> into <see cref="Reporting.BenchmarkResult"/>
/// rows and emits the unified <c>Bdn.FullBenchmark_*.{log,LLM,output}</c> triplet next to Console's
/// counterparts in <c>Test_Benchmark_Results/Benchmark/</c>.</para>
/// </summary>
[MemoryDiagnoser]
public class AcBinaryVsMemPackBenchmark
{
    /// <summary>
    /// The 5 TestData cells matching Console's <c>BenchmarkLayer.Core</c> set —
    /// Small (2x2x2x2) / Medium (3x3x3x4) / Large (5x5x5x10) / Repeated (10 items) / Deep (2x4x4x8).
    /// Resolved at <see cref="GlobalSetup"/> time via <see cref="BenchmarkTestDataProvider_All_False.CreateTestDataSets"/>
    /// (same provider Console uses) so the workload graphs are bit-for-bit identical.
    /// </summary>
    public static IEnumerable<string> TestDataNames => new[] { "Small", "Medium", "Large", "Repeated", "Deep" };

    [ParamsSource(nameof(TestDataNames))]
    public string TestData { get; set; } = "";

    /// <summary>
    /// Engine axis: AcBinary FastMode + Compact wire (UTF-8) vs MemoryPack Default (UTF-8). Compact-on-both-sides
    /// keeps the string-encoding dimension constant so the comparison reflects engine differences only.
    /// </summary>
    [Params("AcBinary", "MemoryPack")]
    public string Engine { get; set; } = "";

    private ISerializerBenchmark _serializer = null!;

    [GlobalSetup]
    public void Setup()
    {
        // BDN runs each benchmark in an isolated child process — the parent's charset selection (a static
        // field) does NOT cross the process boundary, so the child would otherwise fall back to the
        // compile-time default (Latin1Long). Pin the BDN serializer benchmark to Latin1Short here so its
        // cells line up with the Console Latin1Short runs. (Mirrored in BdnSummaryAdapter.WriteResults
        // for the parent process — .LLM charset label + Size(B) column.)
        BenchmarkTestDataProvider.LongStringSuffix = CharsetSuffixes.Latin1Short;

        var allTestData = BenchmarkTestDataProvider_All_False.CreateTestDataSets();
        var testDataSet = (TestDataSet<TestOrder_All_False>)allTestData.First(t => t.Name.StartsWith(TestData));

        if (Engine == "AcBinary")
        {
            var options = AcBinarySerializerOptions.FastMode;
            options.WireMode = WireMode.Compact;
            _serializer = new AcBinaryBenchmark<TestOrder_All_False>(testDataSet.Order, options, "FastMode");
        }
        else
        {
            // MemoryPack's wire-mode-aligned ctor — Compact ↔ UTF-8 default for apples-to-apples vs AcBinary Compact.
            _serializer = new MemoryPackBenchmark<TestOrder_All_False>(testDataSet.Order, WireMode.Compact, "Default");
        }

        // Round-trip correctness check before the BDN harness starts measuring — same gate the Console
        // runner enforces. Fails the run early if anything's broken (rather than producing meaningless numbers).
        if (!_serializer.VerifyRoundTrip())
            throw new InvalidOperationException($"Round-trip verification FAILED for {Engine} on {TestData}.");
    }

    [Benchmark]
    public void Serialize() => _serializer.Serialize();

    [Benchmark]
    public void Deserialize() => _serializer.Deserialize();
}
