using System;
using System.Buffers;
using System.Runtime.CompilerServices;

namespace AyCode.Core.Serializers.Binaries;

/// <summary>
/// High-performance ArrayPool-backed IBufferWriter.
/// Designed for pooling and reuse - supports Reset() without deallocation.
/// Unlike BCL ArrayBufferWriter, this uses ArrayPool for zero-alloc buffer management.
/// </summary>
internal sealed class ArrayPooledBufferWriter : IBufferWriter<byte>, IDisposable
{
    private byte[] _buffer;
    private int _written;

    public ArrayPooledBufferWriter(int initialCapacity)
    {
        _buffer = ArrayPool<byte>.Shared.Rent(Math.Max(initialCapacity, 256));
    }

    /// <summary>Written data as ReadOnlySpan (no allocation).</summary>
    public ReadOnlySpan<byte> WrittenSpan
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get => _buffer.AsSpan(0, _written);
    }

    /// <summary>Written data as ReadOnlyMemory.</summary>
    public ReadOnlyMemory<byte> WrittenMemory
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get => _buffer.AsMemory(0, _written);
    }

    /// <summary>Number of bytes written so far.</summary>
    public int WrittenCount
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get => _written;
    }

    /// <summary>Direct access to backing array for Unsafe.WriteUnaligned operations.</summary>
    public byte[] RawBuffer
    {
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        get => _buffer;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Advance(int count) => _written += count;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public Memory<byte> GetMemory(int sizeHint = 0)
    {
        EnsureCapacity(sizeHint);
        return _buffer.AsMemory(_written);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public Span<byte> GetSpan(int sizeHint = 0)
    {
        EnsureCapacity(sizeHint);
        return _buffer.AsSpan(_written);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    private void EnsureCapacity(int sizeHint)
    {
        if (sizeHint <= _buffer.Length - _written) return;
        Grow(sizeHint);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    private void Grow(int sizeHint)
    {
        var newSize = Math.Max(_buffer.Length * 2, _written + Math.Max(sizeHint, 1));
        var newBuffer = ArrayPool<byte>.Shared.Rent(newSize);
        _buffer.AsSpan(0, _written).CopyTo(newBuffer);
        ArrayPool<byte>.Shared.Return(_buffer);
        _buffer = newBuffer;
    }

    /// <summary>Copy written data to a new exactly-sized array.</summary>
    public byte[] ToArray()
    {
        var result = GC.AllocateUninitializedArray<byte>(_written);
        _buffer.AsSpan(0, _written).CopyTo(result);
        return result;
    }

    /// <summary>Copy written data to an external IBufferWriter (single memcpy).</summary>
    public void CopyTo(IBufferWriter<byte> destination)
    {
        var span = destination.GetSpan(_written);
        _buffer.AsSpan(0, _written).CopyTo(span);
        destination.Advance(_written);
    }

    /// <summary>
    /// Detach the backing buffer for zero-copy transfer.
    /// Caller is responsible for returning the buffer to ArrayPool.
    /// </summary>
    public (byte[] Buffer, int Length) Detach()
    {
        var buf = _buffer;
        var len = _written;
        _buffer = ArrayPool<byte>.Shared.Rent(256);
        _written = 0;
        return (buf, len);
    }

    /// <summary>Reset for reuse (no deallocation).</summary>
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public void Reset() => _written = 0;

    /// <summary>Reset with optional buffer resize if too small.</summary>
    public void Reset(int newMinCapacity)
    {
        _written = 0;
        if (_buffer.Length < newMinCapacity)
        {
            ArrayPool<byte>.Shared.Return(_buffer);
            _buffer = ArrayPool<byte>.Shared.Rent(newMinCapacity);
        }
    }

    public void Dispose()
    {
        if (_buffer != null)
        {
            ArrayPool<byte>.Shared.Return(_buffer);
            _buffer = null!;
        }
    }
}
