<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Tests;

/**
 * Custom PHP stream wrapper that replaces php://input in tests.
 *
 * Usage:
 *   TestInputStream::register();
 *   TestInputStream::setData('{"key":"value"}');
 *   // ... call controller method that reads php://input ...
 *   TestInputStream::unregister();
 *
 * This allows testing controllers whose getRequestData() method
 * uses file_get_contents('php://input').
 */
class TestInputStream {

    /** @var resource|null */
    public $context;

    private static string $data = '';
    private int $position = 0;
    private static bool $registered = false;

    /**
     * Set the data that php://input will return.
     */
    public static function setData(string $data): void {
        self::$data = $data;
    }

    /**
     * Register this stream wrapper, replacing the built-in php:// handler.
     */
    public static function register(): void {
        if (self::$registered) {
            return;
        }
        stream_wrapper_unregister('php');
        stream_wrapper_register('php', self::class);
        self::$registered = true;
    }

    /**
     * Restore the built-in php:// handler.
     */
    public static function unregister(): void {
        if (!self::$registered) {
            return;
        }
        stream_wrapper_unregister('php');
        stream_wrapper_restore('php');
        self::$registered = false;
        self::$data = '';
    }

    // ── Stream wrapper methods ──────────────────────────────────────

    public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool {
        $this->position = 0;
        return true;
    }

    public function stream_read(int $count): string|false {
        $chunk = substr(self::$data, $this->position, $count);
        $this->position += strlen($chunk);
        return $chunk;
    }

    public function stream_eof(): bool {
        return $this->position >= strlen(self::$data);
    }

    public function stream_stat(): array|false {
        return [
            'size' => strlen(self::$data),
        ];
    }

    public function stream_seek(int $offset, int $whence = SEEK_SET): bool {
        switch ($whence) {
            case SEEK_SET:
                $this->position = $offset;
                return true;
            case SEEK_CUR:
                $this->position += $offset;
                return true;
            case SEEK_END:
                $this->position = strlen(self::$data) + $offset;
                return true;
        }
        return false;
    }

    public function stream_tell(): int {
        return $this->position;
    }

    public function url_stat(string $path, int $flags): array|false {
        return [
            'size' => strlen(self::$data),
        ];
    }
}
