<?php

declare(strict_types=1);

namespace App\Framework\OutputBuffer;

/**
 * Handle to an active output buffer
 *
 * Represents a reference to an active output buffer that can be manipulated
 */
final readonly class BufferHandle
{
    public function __construct(
        public int $level,
        public OutputBufferConfig $config
    ) {}

    /**
     * Get and clean the buffer contents
     *
     * @throws OutputBufferException if buffer cannot be cleaned
     */
    public function clean(): string
    {
        $this->validateBufferLevel();

        $content = ob_get_clean();

        if ($content === false) {
            throw OutputBufferException::failedToClean($this->level);
        }

        return $content;
    }

    /**
     * End the buffer and discard contents
     *
     * @throws OutputBufferException if buffer cannot be ended
     */
    public function end(): void
    {
        $this->validateBufferLevel();

        if (!ob_end_clean()) {
            throw OutputBufferException::failedToClean($this->level);
        }
    }

    /**
     * Flush the buffer contents
     *
     * @throws OutputBufferException if buffer cannot be flushed
     */
    public function flush(): void
    {
        $this->validateBufferLevel();

        if (!ob_flush()) {
            throw OutputBufferException::failedToClean($this->level);
        }
    }

    /**
     * Get current buffer contents without clearing
     *
     * @throws OutputBufferException if buffer cannot be read
     */
    public function getContents(): string
    {
        $this->validateBufferLevel();

        $content = ob_get_contents();

        if ($content === false) {
            throw OutputBufferException::failedToCapture();
        }

        return $content;
    }

    /**
     * Get current buffer length
     */
    public function getLength(): int
    {
        $this->validateBufferLevel();

        return ob_get_length() ?: 0;
    }

    /**
     * Check if this buffer is still active
     */
    public function isActive(): bool
    {
        return ob_get_level() >= $this->level;
    }

    /**
     * Validate that the buffer level matches expectations
     *
     * @throws OutputBufferException if levels don't match
     */
    private function validateBufferLevel(): void
    {
        $currentLevel = ob_get_level();

        if ($currentLevel < $this->level) {
            throw OutputBufferException::noActiveBuffer();
        }

        if ($currentLevel !== $this->level) {
            throw OutputBufferException::levelMismatch($this->level, $currentLevel);
        }
    }
}
