<?php

/**
 * Handles shared global functionality.
 *
 * @package northmill-online-shared
 */
declare (strict_types=1);
namespace Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Modules;

use Northmill_Online_Shared_Checkout\Northmill_Online_Shared\Abstract_Shared_Base;
defined('ABSPATH') || exit;
/**
 * Class Logger
 */
class Globals_Module extends Abstract_Module
{
    private const GLOBAL_KEY = 'northmill-online-shared';
    /**
     * Class constructor.
     *
     * @param Abstract_Shared_Base $shared Shared instance.
     *
     * @return void
     */
    public function __construct(Abstract_Shared_Base $shared)
    {
        parent::__construct($shared);
        add_action('init', array($this, 'maybe_process_unique_hooks'));
        if (!isset($GLOBALS[self::GLOBAL_KEY])) {
            $GLOBALS[self::GLOBAL_KEY] = array('unique_hooks' => array());
        }
    }
    /**
     * Get a global value.
     *
     * @param string $key The key for the global value.
     *
     * @return mixed|null The value if found, null otherwise.
     */
    public function get(string $key): mixed
    {
        return $GLOBALS[self::GLOBAL_KEY][$key] ?? null;
    }
    /**
     * Set a global value.
     *
     * @param string $key The key for the global value.
     * @param mixed  $value The value to set.
     *
     * @return void
     */
    public function set(string $key, mixed $value): void
    {
        $GLOBALS[self::GLOBAL_KEY][$key] = $value;
    }
    /**
     * Append a value to a global array.
     * Note that this assumes the global value is an array.
     * If the global value is not an array, it will be converted to an array.
     *
     * @param string      $key The key for the global value.
     * @param mixed       $value The value to append.
     * @param string|null $sub_key The key in the sub-array to append the value to. If null, the value will be appended without a key.
     *
     * @return void
     */
    public function append(string $key, mixed $value, ?string $sub_key = null): void
    {
        if (!is_array($this->get($key))) {
            $this->set($key, array());
        }
        if ($sub_key) {
            $GLOBALS[self::GLOBAL_KEY][$key][$sub_key] = $value;
        } else {
            $GLOBALS[self::GLOBAL_KEY][$key][] = $value;
        }
    }
    /**
     * Check if a global value exists.
     *
     * @param string $key The key for the global value.
     *
     * @return bool True if the global value exists, false otherwise.
     */
    public function exists(string $key): bool
    {
        return isset($GLOBALS[self::GLOBAL_KEY][$key]);
    }
    /**
     * Remove a global value.
     *
     * @param string $key The key for the global value.
     *
     * @return void
     */
    public function remove(string $key): void
    {
        unset($GLOBALS[self::GLOBAL_KEY][$key]);
    }
    /**
     * Add an action that should only be added once even if there are multiple shared instances on a site.
     *
     * @param string   $hook          The hook to add the action to.
     * @param callable $callback      The function to call.
     * @param int      $priority      The priority of the action.
     * @param int      $accepted_args The number of arguments the function accepts.
     *
     * @return void
     */
    public function add_unique_action(string $hook, callable $callback, int $priority = 10, int $accepted_args = 1): void
    {
        $this->add_unique_filter($hook, $callback, $priority, $accepted_args);
    }
    /**
     * Add a filter that should only be added once even if there are multiple shared instances on a site.
     *
     * @param string   $hook          The hook to add the filter to.
     * @param callable $callback      The function to call.
     * @param int      $priority      The priority of the filter.
     * @param int      $accepted_args The number of arguments the function accepts.
     *
     * @return void
     */
    public function add_unique_filter(string $hook, callable $callback, int $priority = 10, int $accepted_args = 1): void
    {
        $unique_hooks = $this->get('unique_hooks');
        if (!isset($unique_hooks[$hook])) {
            $unique_hooks[$hook] = array();
        }
        $unique_hooks[$hook][] = array('function' => $callback, 'priority' => $priority, 'accepted_args' => $accepted_args, 'shared_version' => $this->shared->package_info->get_shared_version());
        $this->set('unique_hooks', $unique_hooks);
    }
    /**
     * Process unique hooks.
     *
     * @return void
     */
    public function maybe_process_unique_hooks(): void
    {
        if ($this->exists('unique_hooks_processed')) {
            return;
        }
        $unique_hooks = $this->get('unique_hooks');
        if (!empty($unique_hooks)) {
            foreach ($unique_hooks as $hook => $hook_data) {
                // Add the hook only once, the one with the highest shared_version.
                // Determine the hook with the highest shared_version.
                usort($hook_data, function ($a, $b) {
                    return version_compare($b['shared_version'], $a['shared_version']);
                });
                // Use the first entry (highest shared_version) after sorting.
                $highest_version_hook = $hook_data[0];
                // Add the action or filter with the appropriate parameters.
                add_filter($hook, $highest_version_hook['function'], $highest_version_hook['priority'], $highest_version_hook['accepted_args']);
            }
        }
        $this->set('unique_hooks_processed', \true);
    }
}
