using System.Collections.Concurrent;

namespace LiquidCode.Tester.Worker.Services.Isolate;

/// <summary>
/// Pool manager for Isolate sandbox boxes
/// Manages box IDs for parallel execution
/// </summary>
public class IsolateBoxPool
{
    private readonly ConcurrentBag<int> _availableBoxes;
    private readonly SemaphoreSlim _semaphore;
    private readonly int _maxBoxes;
    private readonly ILogger<IsolateBoxPool> _logger;

    public IsolateBoxPool(int maxBoxes, ILogger<IsolateBoxPool> logger)
    {
        if (maxBoxes <= 0)
            throw new ArgumentException("Max boxes must be greater than 0", nameof(maxBoxes));

        _maxBoxes = maxBoxes;
        _logger = logger;
        _availableBoxes = new ConcurrentBag<int>();
        _semaphore = new SemaphoreSlim(maxBoxes, maxBoxes);

        // Initialize pool with box IDs
        for (int i = 0; i < maxBoxes; i++)
        {
            _availableBoxes.Add(i);
        }

        _logger.LogInformation("Initialized Isolate box pool with {MaxBoxes} boxes", maxBoxes);
    }

    /// <summary>
    /// Acquire a box ID from the pool (async, waits if all boxes are in use)
    /// </summary>
    public async Task<int> AcquireBoxAsync(CancellationToken cancellationToken = default)
    {
        await _semaphore.WaitAsync(cancellationToken);

        if (_availableBoxes.TryTake(out var boxId))
        {
            _logger.LogDebug("Acquired box {BoxId}, remaining: {Remaining}",
                boxId, _availableBoxes.Count);
            return boxId;
        }

        // Should not happen due to semaphore, but handle anyway
        _semaphore.Release();
        throw new InvalidOperationException("Failed to acquire box from pool");
    }

    /// <summary>
    /// Release a box ID back to the pool
    /// </summary>
    public void ReleaseBox(int boxId)
    {
        if (boxId < 0 || boxId >= _maxBoxes)
        {
            _logger.LogWarning("Attempted to release invalid box ID: {BoxId}", boxId);
            return;
        }

        _availableBoxes.Add(boxId);
        _semaphore.Release();

        _logger.LogDebug("Released box {BoxId}, available: {Available}",
            boxId, _availableBoxes.Count);
    }

    /// <summary>
    /// Get current pool statistics
    /// </summary>
    public (int Total, int Available, int InUse) GetStatistics()
    {
        var available = _availableBoxes.Count;
        return (_maxBoxes, available, _maxBoxes - available);
    }
}
