<?php

declare(strict_types=1);

namespace App\Framework\Async;

use Fiber;
use SplQueue;

/**
 * Semaphore für begrenzte Ressourcen-Zugriffe
 */
final class AsyncSemaphore
{
    private int $currentCount;

    /** @var SplQueue<Fiber> */
    private SplQueue $waitingFibers;

    public function __construct(
        private readonly int $maxCount,
        private readonly string $name = ''
    ) {
        $this->currentCount = $maxCount;
        $this->waitingFibers = new SplQueue();
    }

    /**
     * Erwirbt eine Semaphore-Erlaubnis (blockierend)
     */
    public function acquire(): void
    {
        if ($this->currentCount > 0) {
            $this->currentCount--;

            return;
        }

        // Wenn keine Erlaubnisse verfügbar, warte
        $fiber = new Fiber(function () {
            while ($this->currentCount <= 0) {
                Fiber::suspend();
            }
            $this->currentCount--;
        });

        $this->waitingFibers->enqueue($fiber);
        $fiber->start();
        $fiber->getReturn();
    }

    /**
     * Versucht eine Erlaubnis zu erwerben (non-blocking)
     */
    public function tryAcquire(): bool
    {
        if ($this->currentCount <= 0) {
            return false;
        }

        $this->currentCount--;

        return true;
    }

    /**
     * Gibt eine Erlaubnis zurück
     */
    public function release(): void
    {
        if ($this->currentCount >= $this->maxCount) {
            throw new \RuntimeException("Cannot release more permits than maximum");
        }

        $this->currentCount++;

        // Wecke wartende Fibers auf
        if (! $this->waitingFibers->isEmpty()) {
            $nextFiber = $this->waitingFibers->dequeue();
            if (! $nextFiber->isTerminated()) {
                $nextFiber->resume();
            }
        }
    }

    /**
     * Führt eine Funktion mit automatischer Erlaubnis aus
     */
    public function withPermit(callable $callback): mixed
    {
        $this->acquire();

        try {
            return $callback();
        } finally {
            $this->release();
        }
    }

    /**
     * Gibt verfügbare Erlaubnisse zurück
     */
    public function availablePermits(): int
    {
        return $this->currentCount;
    }

    /**
     * Gibt Semaphore-Statistiken zurück
     */
    public function getStats(): array
    {
        return [
            'name' => $this->name,
            'max_count' => $this->maxCount,
            'current_count' => $this->currentCount,
            'waiting_fibers' => $this->waitingFibers->count(),
        ];
    }
}
