<?php

namespace ProudNerds\Video;

use ProudNerds\Video\Tasks\QueueTask;

class QueueManager
{
    private $queue = [];
    private $queueHash = '';

    /**
     * QueueManager constructor.
     *
     * Load the queue from the database
     */
    private function __construct()
    {
        // Load the queue from the database
        $this->queue = get_option('ffmpeg_queue', ['new' => [], 'processing' => []]);
        //$this->queue = ['new' => [], 'processing' => []];
        $this->queueHash = sha1(serialize($this->queue));
        //$this->saveQueue();

        // Process the queue using a cron job every minute
        add_action('ffmpeg_process_queue', [$this, 'processQueue']);
        add_filter('cron_schedules', function ($schedules) {
            $schedules['ffmpeg_queue'] = [
                'interval' => 60,
                'display' => __('FFMpeg minute handler', 'proudnerds'),
            ];
            return $schedules;
        });
        if (!wp_next_scheduled('ffmpeg_process_queue')) {
            wp_schedule_event(time(), 'ffmpeg_queue', 'ffmpeg_process_queue');
        }
    }

    /**
     * Get the instance of the QueueManager
     *
     * @return QueueManager
     */
    public static function getInstance(): QueueManager
    {
        static $instance = null;
        if ($instance === null) {
            $instance = new QueueManager();
        }
        return $instance;
    }

    /**
     * Add a task to the queue
     *
     * @param QueueTask $task
     */
    public function addToQueue(QueueTask $task)
    {
        $id = $task->getTaskId();

        // Check if task already exists, if so, skip
        if (isset($this->queue['new'][$id]) || isset($this->queue['processing'][$id])) {
            return;
        }
        $this->queue['new'][$id] = $task;
        $this->saveQueue();
    }

    /**
     * Process the queue
     * This function is called by the cron job
     *
     * @private
     */
    public function processQueue()
    {
        // Check if we are running in a cron
        if (!defined('DOING_CRON') || !DOING_CRON) {
            return;
        }

        // Set last run value
        update_option('ffmpeg_queue_last_run', time());

        // Check if there is no cron running by settting an option
        if (get_option('ffmpeg_queue_processing', false)) {
            echo 'A conversion task is already running, skipping this run.' . PHP_EOL;

            // If a task took longer than an hour, reset the option
            if (get_option('ffmpeg_queue_processing') < time() - 3600) {
                echo 'Resetting overdue conversiontask, since it probably failed.' . PHP_EOL;
                delete_option('ffmpeg_queue_processing');
            }
            return;
        }

        // Select the first new task and set it to processing
        $task = array_shift($this->queue['new']);
        if (!$task) {
            return;
        }

        // Set the option to indicate that a task is running
        update_option('ffmpeg_queue_processing', time());

        // Log to the cli console
        echo 'Processing task (may take a while): ' . $task->getTaskId() . PHP_EOL;

        // Add the task to the processing queue
        $this->queue['processing'][$task->getTaskId()] = $task;
        $this->saveQueue();

        // Execute the task
        set_time_limit(0);
        $task->execute(function () use ($task) {
            // Remove the task from the processing queue
            unset($this->queue['processing'][$task->getTaskId()]);
            $this->saveQueue();

            // Log to the cli console
            echo 'Task completed: ' . $task->getTaskId() . PHP_EOL;

            // Reset ffmpeg_queue_processing
            delete_option('ffmpeg_queue_processing');
        });
    }

    /**
     * Save the queue to the database if it has changed
     */
    private function saveQueue(): void
    {
        // Save the queue to the database if it has been modified
        if (sha1(serialize($this->queue)) !== $this->queueHash) {
            update_option('ffmpeg_queue', $this->queue);
            $this->queueHash = sha1(serialize($this->queue));
        }
    }
}
