namespace AyCode.Core.Benchmarks.Workloads.Scenarios;

/// <summary>
/// Common contract for every per-engine, per-I/O-mode benchmark row. Each implementation captures
/// one (Engine × IoMode × OptionsPreset) combination — e.g. <c>AcBinary Byte[] FastMode SGen</c> —
/// and exposes a uniform <c>Serialize</c> / <c>Deserialize</c> hot-path that the benchmark loop
/// drives through warmup + adaptive-iter calibration + measurement.
///
/// <para>The default <see cref="WarmupSerialize"/> + <see cref="WarmupDeserialize"/> methods iterate
/// the hot path N times — overrides are only needed when an implementor wants Ser/Des-specific
/// warmup state (rare). Round-trip-only benchmarks (NamedPipe etc.) set <see cref="IsRoundTripOnly"/>
/// to true so the bench loop skips the Des-phase and routes timing into the RT columns.</para>
/// </summary>
public interface ISerializerBenchmark
{
    /// <summary>Serializer engine — typed enum, see <see cref="BenchmarkEnumExtensions.ToDisplay(BenchmarkEngine)"/> for the human-readable form.</summary>
    BenchmarkEngine Engine { get; }
    /// <summary>I/O mode — typed enum, see <see cref="BenchmarkEnumExtensions.ToDisplay(BenchmarkIoMode)"/> for the human-readable form.</summary>
    BenchmarkIoMode IoMode { get; }
    /// <summary>Dispatch mode — typed enum (<see cref="BenchmarkDispatchMode.SGen"/> / <see cref="BenchmarkDispatchMode.Runtime"/> / <see cref="BenchmarkDispatchMode.Hybrid"/>). For AcBinary derived from <c>UseGeneratedCode</c> + child-type SGen coverage; non-AcBinary engines report their own native dispatch model.</summary>
    BenchmarkDispatchMode DispatchMode { get; }
    /// <summary>Options preset name — e.g. "FastMode", "Default", "NoIntern", "WithCompression". Stays string because preset names are open-ended (per-instance constructor argument).</summary>
    string OptionsPreset { get; }
    /// <summary>
    /// CLR type of the order graph this benchmark serializes (e.g. <c>typeof(TestOrder_All_False)</c>,
    /// <c>typeof(TestOrder_All_True)</c>). Per-instance: AcBinary picks variant by options preset
    /// (caller-side dispatch rule), MemPack / MsgPack always use <c>_All_False</c>.
    /// Concrete benchmarks return <c>typeof(T)</c> for their generic parameter.
    /// </summary>
    Type OrderType { get; }
    /// <summary>
    /// Derived display name for the <see cref="OrderType"/>. Default-interface impl reads
    /// <c>OrderType.Name</c>; concrete classes don't need to override. Surfaced in the SERIALIZER
    /// OPTIONS section of every output (.log, .LLM, console) — not in the per-row tables — so the
    /// reader correlates each preset with its TestOrder variant without inflating the result columns.
    /// </summary>
    string OrderTypeName => OrderType.Name;
    /// <summary>Synthesized display name from Engine + IoMode + OptionsPreset.</summary>
    string Name => $"{Engine.ToDisplay()} ({IoMode.ToDisplay()}, {OptionsPreset})";
    int SerializedSize { get; }
    string? OptionsDescription => null;
    /// <summary>One-time SERIALIZER-side setup allocation cost (e.g., pre-allocated ArrayBufferWriter with internal buffer). Captured in constructor; 0 for byte[] API and Fresh-BufWriter variants.</summary>
    long SetupSerializeAllocBytes { get; }
    /// <summary>One-time DESERIALIZER-side setup allocation cost (e.g., long-lived AsyncPipeReaderInput's ArrayPool rent + ManualResetEventSlim, drain-task scaffolding). Captured in constructor; 0 for byte[] API and any setup-free deserialize path.</summary>
    long SetupDeserializeAllocBytes { get; }
    /// <summary>True when Serialize() does a full round-trip (e.g. NamedPipe) and Deserialize() is a no-op.
    /// Used by the SUMMARY: WINNERS section to skip such cells from "Fastest Serialize" and "Fastest Deserialize"
    /// rankings (because both metrics are misleading there) — they still participate in "Fastest Round-trip".
    /// Default false for in-memory IO modes which measure Ser and Des separately.</summary>
    bool IsRoundTripOnly => false;
    /// <summary>Warm only the Serialize path. Default body iterates <see cref="Serialize"/> N times.
    /// Overrides are only needed when the implementor wants Ser-specific warmup-state (e.g. pre-allocate buffers).
    /// On <see cref="IsRoundTripOnly"/> benchmarks (NamedPipe-style) <see cref="Serialize"/> performs the full RT,
    /// so this warms the entire round-trip path.</summary>
    void WarmupSerialize(int iterations)
    {
        for (var i = 0; i < iterations; i++) Serialize();
    }

    /// <summary>Warm only the Deserialize path. Default body iterates <see cref="Deserialize"/> N times.
    /// On <see cref="IsRoundTripOnly"/> benchmarks <see cref="Deserialize"/> is a no-op, so the bench loop
    /// skips the Des-phase entirely for those cells.</summary>
    void WarmupDeserialize(int iterations)
    {
        for (var i = 0; i < iterations; i++) Deserialize();
    }

    void Serialize();
    void Deserialize();
    /// <summary>Round-trip correctness check — called once per cell before warmup. Returns true if Serialize+Deserialize preserves data.</summary>
    bool VerifyRoundTrip();
}
