<?php
namespace DevOwl\RealQueue;

use DevOwl\RealQueue\rest\Queue;
use DevOwl\RealQueue\queue\Executor;
use DevOwl\RealQueue\queue\Job;
use DevOwl\RealQueue\queue\Persist;
use DevOwl\RealQueue\queue\Query;
use MatthiasWeb\Utils\Activator;
use MatthiasWeb\Utils\Core as UtilsCore;
use MatthiasWeb\Utils\Service;
use MatthiasWeb\Utils\ServiceNoStore;

// @codeCoverageIgnoreStart
defined('ABSPATH') or die('No script kiddies please!'); // Avoid direct file request
// @codeCoverageIgnoreEnd

/**
 * Core for real-queue.
 */
class Core {
    use UtilsProvider;

    private $pluginCore;

    private $assets;

    private $persist;

    private $query;

    private $executor;

    private $restQuery;

    /**
     * `real-queue` allows to define multiple capabilities needed for fetching and working the queue.
     * For this, you can use `Core#addCapability()`.
     */
    private $capabilities = ['edit_posts'];

    /**
     * C'tor.
     *
     * @param UtilsCore $pluginCore
     */
    public function __construct($pluginCore) {
        $this->pluginCore = $pluginCore;
        $this->assets = new Assets($this);
        $this->persist = new Persist($this);
        $this->query = new Query($this);
        $this->executor = new Executor($this);
        $this->restQuery = new Queue($this);

        // Enable `no-store` for our relevant WP REST API endpoints
        ServiceNoStore::hook('/' . Service::getNamespace($this));

        //add_action('wp_enqueue_scripts', [$this->getAssets(), 'admin_enqueue_scripts'], 9);
        add_action('admin_enqueue_scripts', [$this->getAssets(), 'admin_enqueue_scripts'], 9);
        add_action('rest_api_init', [$this->restQuery, 'rest_api_init']);
    }

    /**
     * Make sure the database tables are created.
     *
     * @param Activator $activator
     */
    public function dbDelta($activator) {
        $charset_collate = $activator->getCharsetCollate();
        $table_name = $this->getTableName();

        $sql =
            "CREATE TABLE $table_name (
            id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            type varchar(50) NOT NULL,
            worker varchar (10) NOT NULL,
            group_uuid char(36),
            group_position int(11),
            group_total int(11),
            process int(11) NOT NULL,
            process_total int(11) NOT NULL,
            duration_ms int(11),
            data text NOT NULL,
            runs int NOT NULL DEFAULT 0,
            retries int NOT NULL,
            delay_ms int NOT NULL,
            created datetime NOT NULL,
            lock_until timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
            locked tinyint(1) DEFAULT 0,
            callable tinytext,
            exception text,
            capability varchar(50),
            priority tinyint UNSIGNED NOT NULL DEFAULT " .
            Job::DEFAULT_PRIORITY .
            ",
            PRIMARY KEY  (id),
            KEY priority (priority),
            KEY filters (process, process_total, runs, retries, lock_until, type, capability)
        ) $charset_collate;";
        dbDelta($sql);
    }

    /**
     * Check if the current user is allowed to query the queue. This only enables
     * frontend assets and UI.
     */
    public function currentUserAllowedToQuery() {
        foreach ($this->capabilities as $cap) {
            if (current_user_can($cap)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Add a capability which is allowed to query the queue.
     *
     * @param string $cap
     */
    public function addCapability($cap) {
        $this->capabilities[] = $cap;
    }

    /**
     * Getter.
     *
     * @codeCoverageIgnore
     */
    public function getAssets() {
        return $this->assets;
    }

    /**
     * Getter.
     *
     * @codeCoverageIgnore
     */
    public function getPluginCore() {
        return $this->pluginCore;
    }

    /**
     * Getter.
     *
     * @codeCoverageIgnore
     */
    public function getPersist() {
        return $this->persist;
    }

    /**
     * Getter.
     *
     * @codeCoverageIgnore
     */
    public function getQuery() {
        return $this->query;
    }

    /**
     * Getter.
     *
     * @codeCoverageIgnore
     */
    public function getRestQuery() {
        return $this->restQuery;
    }

    /**
     * Getter.
     *
     * @codeCoverageIgnore
     */
    public function getExecutor() {
        return $this->executor;
    }
}
