using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Threading;

namespace EonaCat.LogStack.Pooling;

// This file is part of the EonaCat project(s) which is released under the Apache License.
// See the LICENSE file or go to https://EonaCat.com/License for full license details.

/// <summary>
/// High-performance buffer pool using ArrayPool for optimal memory efficiency.
/// Manages buffers across multiple size tiers for flexible allocation patterns.
/// </summary>
public sealed class BufferPool : IDisposable
{
    private readonly int[] _standardSizes = { 512, 1024, 4096, 8192, 16384, 65536 };
    private readonly ConcurrentDictionary<int, ArrayPool<byte>> _pools;
    private bool _isDisposed;

    public BufferPool()
    {
        _pools = new ConcurrentDictionary<int, ArrayPool<byte>>();
        foreach (var size in _standardSizes)
        {
            _pools[size] = ArrayPool<byte>.Shared;
        }
    }

    /// <summary>
    /// Rents a buffer of at least the specified length
    /// </summary>
    public byte[] Rent(int minimumLength)
    {
        if (_isDisposed)
        {
            throw new ObjectDisposedException(nameof(BufferPool));
        }

        // Find the smallest standard size that fits
        int selectedSize = minimumLength;
        foreach (var size in _standardSizes)
        {
            if (size >= minimumLength)
            {
                selectedSize = size;
                break;
            }
        }

        return ArrayPool<byte>.Shared.Rent(selectedSize);
    }

    /// <summary>
    /// Returns a buffer to the pool
    /// </summary>
    public void Return(byte[] buffer, bool clearBuffer = false)
    {
        if (_isDisposed || buffer == null)
        {
            return;
        }

        ArrayPool<byte>.Shared.Return(buffer, clearBuffer);
    }

    /// <summary>
    /// Gets statistics about pool usage
    /// </summary>
    public ArrayPoolStatistics GetStatistics()
    {
        return new ArrayPoolStatistics
        {
            TotalAllocations = ArrayPool<byte>.Shared.GetTotalAllocations(),
            BytesAllocated = ArrayPool<byte>.Shared.GetTotalBytesAllocated()
        };
    }

    public void Dispose()
    {
        if (_isDisposed)
        {
            return;
        }

        _isDisposed = true;
        // ArrayPool.Shared is static and managed by runtime
    }
}

/// <summary>
/// Statistics about array pool usage
/// </summary>
public class ArrayPoolStatistics
{
    public long TotalAllocations { get; set; }
    public long BytesAllocated { get; set; }
}

/// <summary>
/// Rented buffer that automatically returns itself to the pool when disposed
/// </summary>
public sealed class RentedBuffer : IDisposable
{
    private byte[] _buffer;
    private readonly int _actualLength;
    private readonly BufferPool? _pool;

    public RentedBuffer(BufferPool? pool, int minimumLength)
    {
        _pool = pool;
        _buffer = pool?.Rent(minimumLength) ?? new byte[minimumLength];
        _actualLength = minimumLength;
    }

    public byte[] Buffer => _buffer;
    public Span<byte> AsSpan() => new Span<byte>(_buffer, 0, _actualLength);
    public Memory<byte> AsMemory() => new Memory<byte>(_buffer, 0, _actualLength);

    public void Dispose()
    {
        if (_buffer != null)
        {
            _pool?.Return(_buffer, clearBuffer: true);
            _buffer = null!;
        }
    }
}

/// <summary>
/// Extension methods for ArrayPool integration
/// </summary>
public static class ArrayPoolExtensions
{
    /// <summary>
    /// Gets total allocations from the shared ArrayPool (via reflection/diagnostics)
    /// </summary>
    public static long GetTotalAllocations(this ArrayPool<byte> pool)
    {
        // This is a placeholder - actual statistics would require reflection or instrumentation
        // For now, this provides a hook for future diagnostics integration
        return 0;
    }

    /// <summary>
    /// Gets total bytes allocated from the shared ArrayPool
    /// </summary>
    public static long GetTotalBytesAllocated(this ArrayPool<byte> pool)
    {
        // This is a placeholder for diagnostics integration
        return 0;
    }
}
