<?php

/**
 * Mock functions for WordPress core hooks.
 *
 * These functions mock the hooks API so that hooks can be used in packages
 * that don't explicitly depend on WordPress for testing.
 *
 * To use the mocked hooks, include this file in your test suite's `bootstrap.php` file:
 * ```php
 *   require_once 'vendor/caseproof/ground-level-php-tests/src/mocks/wp-hooks.php';
 * ```
 */

declare(strict_types=1);

global $mockedWpHooks;
$mockedWpHooks = [];

/**
 * Adds an action hook.
 *
 * @param  string   $hookName     The hook name.
 * @param  callable $callback     The callback function.
 * @param  integer  $priority     The priority.
 * @param  integer  $acceptedArgs The number of arguments the callback will accept.
 * @return boolean
 *
 * @link https://developer.wordpress.org/reference/functions/add_action/
 */
function add_action(string $hookName, callable $callback, int $priority = 10, int $acceptedArgs = 1): bool
{
    return add_filter($hookName, $callback, $priority, $acceptedArgs);
}

/**
 * Adds a filter hook.
 *
 * @param  string   $hookName     The hook name.
 * @param  callable $callback     The callback function.
 * @param  integer  $priority     The priority.
 * @param  integer  $acceptedArgs The number of arguments the callback will accept.
 * @return boolean
 *
 * @link https://developer.wordpress.org/reference/functions/add_filter/
 */
function add_filter(string $hookName, callable $callback, int $priority = 10, int $acceptedArgs = 1): bool
{
    global $mockedWpHooks;
    if (! isset($mockedWpHooks[$hookName])) {
        $mockedWpHooks[$hookName] = [];
    }
    $mockedWpHooks[$hookName][] = compact('callback', 'priority', 'acceptedArgs');
    return true;
}

/**
 * Determines whether an action has been registered for a hook.
 *
 * @param  string           $hookname The hook name.
 * @param  boolean|callable $callback The callback function or to check if the hook has any callbacks.
 * @return boolean|integer
 *
 * @link https://developer.wordpress.org/reference/functions/has_action/
 */
function has_action(string $hookname, $callback = false)
{
    return has_filter($hookname, $callback);
}

/**
 * Determines whether a filter has been registered for a hook.
 *
 * @param  string           $hookname The hook name.
 * @param  boolean|callable $callback The callback function or to check if the hook has any callbacks.
 * @return boolean|integer
 *
 * @link https://developer.wordpress.org/reference/functions/has_filter/
 */
function has_filter(string $hookname, $callback = false)
{
    global $mockedWpHooks;
    if (! isset($mockedWpHooks[$hookname])) {
        return false;
    }

    if (! $callback) {
        return true;
    }

    foreach ($mockedWpHooks[$hookname] as $hookArgs) {
        if ($hookArgs['callback'] === $callback) {
            return $hookArgs['priority'];
        }
    }

    return false;
}

/**
 * Removes an action hook.
 *
 * @param  string   $hookName The hook name.
 * @param  callable $callback The callback function.
 * @param  integer  $priority The priority.
 * @return boolean
 *
 * @link https://developer.wordpress.org/reference/functions/remove_action/
 */
function remove_action(string $hookName, callable $callback, int $priority = 10): bool
{
    return remove_filter($hookName, $callback, $priority);
}

/**
 * Removes a filter hook.
 *
 * @param  string   $hookName The hook name.
 * @param  callable $callback The callback function.
 * @param  integer  $priority The priority.
 * @return boolean
 *
 * @link https://developer.wordpress.org/reference/functions/remove_filter/
 */
function remove_filter(string $hookName, callable $callback, int $priority = 10): bool
{
    global $mockedWpHooks;
    $res = false;
    if (isset($mockedWpHooks[$hookName])) {
        $origLength               = count($mockedWpHooks[$hookName]);
        $mockedWpHooks[$hookName] = array_filter(
            $mockedWpHooks[$hookName],
            function ($hookArgs) use ($callback, $priority): bool {
                return $hookArgs['callback'] !== $callback && $hookArgs['priority'] !== $priority;
            }
        );
        $res                      = $origLength !== count($mockedWpHooks[$hookName]);
    }
    return $res;
}
