namespace AyCode.Core.Serializers.Console.Benchmarks;

/// <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>
internal interface ISerializerBenchmark
{
    /// <summary>Serializer engine — e.g. "AcBinary", "MemoryPack", "MessagePack".</summary>
    string Engine { get; }
    /// <summary>I/O mode — e.g. "Byte[]", "BufWr reuse", "BufWr new", "NamedPipe", "FileStream".</summary>
    string IoMode { get; }
    /// <summary>Dispatch mode — "SGen", "Runtime", or "Hybrid". For AcBinary derived from <c>UseGeneratedCode</c> + child-type SGen coverage; non-AcBinary engines report their own native dispatch model.</summary>
    string DispatchMode { get; }
    /// <summary>Options preset name — e.g. "FastMode", "Default", "NoIntern", "WithCompression".</summary>
    string OptionsPreset { get; }
    /// <summary>Synthesized display name from Engine + IoMode + OptionsPreset.</summary>
    string Name => $"{Engine} ({IoMode}, {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();
}
