using AyCode.Core.Serializers.Binaries;
using System;
using System.IO.Pipelines;
using System.Threading;
using System.Threading.Tasks;

namespace AyCode.Core.Tests.Serialization;

/// <summary>
/// Test/benchmark-only extension methods for populating <see cref="AsyncPipeReaderInput"/>
/// from <see cref="System.IO.Pipelines.PipeReader"/>-backed transports (NamedPipe, FileStream,
/// custom pipe sources).
///
/// <para><b>Why test-only:</b> in real production, the consuming application already has its own
/// reader-task that reads from the pipe and pushes bytes via <c>AsyncPipeReaderInput.Feed</c>
/// — providing this drain extension publicly would duplicate that responsibility and confuse
/// the canonical push-pattern. The extension is kept here for unit-test scaffolding and the
/// streaming benchmark; production NuGet consumers should write their own drain logic in their
/// own reader-task following the application's threading model.</para>
/// </summary>
public static class AsyncPipeReaderInputExtensions
{
    /// <summary>
    /// Drains a <see cref="PipeReader"/> end-to-end into the <see cref="AsyncPipeReaderInput"/>:
    /// calls <see cref="AsyncPipeReaderInput.Feed"/> on each segment and
    /// <see cref="AsyncPipeReaderInput.Complete"/> when the pipe completes.
    ///
    /// <para>Typical usage (test-only): NamedPipe IPC and FileStream-via-PipeReader transports
    /// schedule this on a background task while the deserialization context reads from the same
    /// input on another thread.</para>
    ///
    /// <para><see cref="AsyncPipeReaderInput.Complete"/> is invoked in a <c>finally</c> block —
    /// ensures the consumer always wakes up even if the pipe read throws or the operation is
    /// cancelled. Exceptions (including <see cref="OperationCanceledException"/>) propagate to
    /// the caller after <c>Complete</c> runs.</para>
    /// </summary>
    /// <param name="input">The receive-side input to feed.</param>
    /// <param name="reader">The pipe reader to drain.</param>
    /// <param name="cancellationToken">Optional cancellation token.</param>
    /// <exception cref="ArgumentNullException">If <paramref name="input"/> or <paramref name="reader"/> is <c>null</c>.</exception>
    public static async Task DrainFromAsync(this AsyncPipeReaderInput input, PipeReader reader, CancellationToken cancellationToken = default)
    {
        if (input is null) throw new ArgumentNullException(nameof(input));
        if (reader is null) throw new ArgumentNullException(nameof(reader));

        try
        {
            while (true)
            {
                var result = await reader.ReadAsync(cancellationToken).ConfigureAwait(false);

                foreach (var segment in result.Buffer) input.Feed(segment.Span);

                reader.AdvanceTo(result.Buffer.End);
                if (result.IsCompleted) break;
            }
        }
        finally
        {
            input.Complete();
        }
    }
}
