<?php

namespace GeminiLabs\SiteReviews;

trait HookProxy
{
    /**
     * Proxy for WordPress defined filter/action callbacks.
     *
     * Since we cannot ensure third-party code will pass the correct data declared
     * by WordPress, this function allows us to maintain parameter type hints and
     * prevents fatal errors without introducing complexity. If something goes wrong,
     * the error is logged to the Site Reviews console.
     *
     * Catching Throwable causes some PHPUnit tests to get flagged as risky with,
     * "Test code or tested code did not (only) close its own output buffers".
     * So if PHPUNIT_TESTING is defined then just skip the catch.
     */
    public function proxy(string $method): callable
    {
        $reflection = new \ReflectionMethod($this, $method);
        if (!$reflection->isPublic()) {
            throw new \BadMethodCallException("Method [{$method}] is either not public or does not exist.");
        }
        $callback = [$this, $method];
        return static function (...$args) use ($callback, $method) {
            if (defined('PHPUNIT_TESTING')) {
                return call_user_func_array($callback, $args);
            }
            try {
                return call_user_func_array($callback, $args);
            } catch (\Throwable $error) {
                glsr_log()->error($error->getMessage())->debug($error);
            }
            if (str_starts_with($method, 'filter')) {
                // A throwable error was caught so just return the unfiltered first argument.
                return array_shift($args);
            }
        };
    }
}
