<?php

namespace WpModules\Foundation;

use WpModules\Contracts\Foundation\Loader as LoaderContract;

/**
 * Class Loader
 *
 * @package WpModules\Foundation
 */
class Loader implements LoaderContract
{
    /**
     * Array of action hooks to attach.
     *
     * @var array
     */
    private $actions = [];

    /**
     * Array of filter hooks to attach.
     *
     * @var array
     */
    private $filters = [];

    /**
     * Register a service provider hooks.
     *
     * @param \WpModules\Support\ServiceProvider $provider
     *
     * @return void
     */
    public function register($provider): void
    {
        $provider->register();

        if (property_exists($provider, 'actions') && is_array($provider->actions)) {
            $this->addActions($provider);
        }

        if (property_exists($provider, 'filters') && is_array($provider->filters)) {
            $this->addFilters($provider);
        }
    }

    /**
     * Register all plugin actions.
     *
     * @param \WpModules\Support\ServiceProvider $provider
     *
     * @return void
     */
    private function addActions($provider): void
    {
        foreach ($provider->actions as $action) {
            if (!empty($action['hook']) && !empty($action['method'])) {
                $this->actions = $this->add(
                    $this->actions,
                    $action['hook'],
                    $provider,
                    $action['method'],
                    $action['priority'] ?? 10,
                    $action['args'] ?? 1
                );
            }
        }
    }

    /**
     * Utility to register the actions and hooks into a single collection.
     *
     * @param array  $hooks        The array that holds the hooks
     * @param string $hook         The name of the action to witch the callback function is hooked
     * @param object $provider     The service provider that needs to be registered
     * @param string $method       The callback function or method of the service provider
     * @param int    $priority     Used to specify the order in which the functions associated with a particular action
     *                             are executed. Lower numbers correspond with earlier execution, and functions with
     *                             the same priority are executed in the order in which they were added to the action
     * @param int    $acceptedArgs The number of arguments the hook function will accept
     *
     * @return array
     */
    private function add($hooks, $hook, $provider, $method, $priority, $acceptedArgs): array
    {
        $hooks[] = [
            'hook'     => $hook,
            'service'  => $provider,
            'method'   => $method,
            'priority' => $priority,
            'args'     => $acceptedArgs,
        ];

        return $hooks;
    }

    /**
     * Register all plugin filters.
     *
     * @param \WpModules\Support\ServiceProvider $provider
     *
     * @return void
     */
    private function addFilters($provider): void
    {
        foreach ($provider->filters as $filter) {
            if (!empty($filter['hook']) && !empty($filter['method'])) {
                $this->filters = $this->add(
                    $this->filters,
                    $filter['hook'],
                    $provider,
                    $filter['method'],
                    $filter['priority'] ?? 10,
                    $filter['args'] ?? 1
                );
            }
        }
    }

    /**
     * Run the loader and register all the hooks with WordPress.
     */
    public function run(): void
    {
        add_action('plugins_loaded', function () {
            foreach ($this->actions as $action) {
                add_action(
                    $action['hook'],
                    [
                        $action['service'],
                        $action['method'],
                    ],
                    $action['priority'],
                    $action['args']
                );
            }

            foreach ($this->filters as $filter) {
                add_filter(
                    $filter['hook'],
                    [
                        $filter['service'],
                        $filter['method'],
                    ],
                    $filter['priority'],
                    $filter['args']
                );
            }
        });
    }
}
