<?php
declare(strict_types=1);

namespace Archive\StreamWrapper\Filter\Filters;

use Archive\StreamWrapper\Filter\StreamFilterInterface;

/**
 * Stream-Filter für AES-256-Entschlüsselung
 */
class AES256DecryptFilter extends \php_user_filter implements StreamFilterInterface
{
    private string $filterName = 'decrypt.aes256';
    private array $supportedModes = ['read'];
    private array $params = [];

    private string $key = '';
    private string $method = 'AES-256-CBC';

    public function onCreate(): bool
    {
        // Parameter aus dem Stream-Context extrahieren
        if (isset($this->params)) {
            $this->params = $this->params;
        }

        $this->key = $this->getParam('key', '');
        $this->method = $this->getParam('method', 'AES-256-CBC');

        return !empty($this->key);
    }

    public function onClose(): void
    {
        // Cleanup falls nötig
    }

    public function getFilterName(): string
    {
        return $this->filterName;
    }

    public function getSupportedModes(): array
    {
        return $this->supportedModes;
    }

    public function filter($in, $out, &$consumed, bool $closing): int
    {
        $data = $this->readFromBrigade($in);
        $consumed = strlen($data);

        if (!empty($data)) {
            $ivLength = openssl_cipher_iv_length($this->method);

            if (strlen($data) < $ivLength) {
                return PSFS_ERR_FATAL;
            }

            $iv = substr($data, 0, $ivLength);
            $encrypted = substr($data, $ivLength);

            $decrypted = openssl_decrypt($encrypted, $this->method, $this->key, OPENSSL_RAW_DATA, $iv);
            if ($decrypted === false) {
                return PSFS_ERR_FATAL;
            }

            $this->writeToBrigade($out, $decrypted);
        }

        return PSFS_PASS_ON;
    }

    /**
     * Hilfsmethode zum Lesen von Daten aus der Input-Brigade
     */
    private function readFromBrigade($brigade): string
    {
        $data = '';
        while ($bucket = stream_bucket_make_writeable($brigade)) {
            $data .= $bucket->data;
        }
        return $data;
    }

    /**
     * Hilfsmethode zum Schreiben von Daten in die Output-Brigade
     */
    private function writeToBrigade($brigade, string $data): void
    {
        if (!empty($data)) {
            $bucket = stream_bucket_new($this->stream, $data);
            stream_bucket_append($brigade, $bucket);
        }
    }

    /**
     * Gibt einen Parameter zurück
     */
    private function getParam(string $key, $default = null)
    {
        return $this->params[$key] ?? $default;
    }
}
