<?php
declare( strict_types=1 );

namespace FiboFilters\Indexer\Filters;

use FiboFilters\Indexer\Config;
use FiboFilters\Indexer\BackgroundProcessing\BackgroundProcess;
use FiboFilters\Indexer\BuildInfo;
use FiboFilters\Indexer\Logger;
use FiboFilters\WP\Helper as WPHelper;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class AsyncProcess
 */
class AsyncProcess extends BackgroundProcess {

	/**
	 * @var string
	 */
	protected $prefix = 'fibofilters';

	/**
	 * @var string
	 */
	protected $action = 'build_filters_index';

	/**
	 * @var string
	 */
	protected $name = '[Filters index]';

	/** @var BuildInfo */
	protected $build_info;

	/** @var Config */
	protected $config;

	/** @var Indexer */
	private $indexer;

	/** @var Logger */
	protected $logger;

	/** @var WPHelper */
	protected $wp_helper;

	public function __construct(
		BuildInfo $build_info,
		Config $config,
		Indexer $indexer,
		Logger $logger,
		WPHelper $wp_helper
	) {
		$this->build_info = $build_info;
		$this->config     = $config;
		$this->indexer    = $indexer;
		$this->logger     = $logger;
		$this->wp_helper  = $wp_helper;

		parent::__construct( $this->build_info, $this->config, $this->logger, $this->wp_helper );
	}

	/**
	 * Task
	 *
	 * Override this method to perform any actions required on each
	 * queue item. Return the modified item for further processing
	 * in the next pass through. Or, return false to remove the
	 * item from the queue.
	 *
	 * @param mixed $items_set Queue item to iterate over.
	 *
	 * @return mixed
	 */
	public function task( $items_set ) {
		$this->build_info->set_is_indexing( true );

		$status = $this->build_info->get_info( 'status', $this->config->get_index_role() );
		if ( $status !== 'building' ) {
			$this->logger->log( '[Filters index] Breaking async task due to indexer status: ' . $status, 'debug', 'file', 'filters' );
			exit();
		}

		$this->logger->log( '[Filters index] Starting async task. Items set count: ' . count( $items_set ), 'debug', 'file', 'filters' );

		do_action( 'fibofilters/indexer/bg_processing/before_task' );

		$this->indexer->set_index_role( $this->config->get_index_role() );
		$this->indexer->set_call_source( $this->call_source );
		$result = $this->indexer->index_set( $items_set, [ $this, 'update_lock' ] );

		if ( $result ) {
			$this->logger->log( '[Filters index] Finished processing items set', 'debug', 'file', 'filters' );
		}

		return false;
	}

	/**
	 * Cancel Process
	 *
	 * Stop processing queue items, clear cronjob and delete batch.
	 */
	public function cancel_process(): void {
		if ( ! $this->is_queue_empty() ) {
			$batch = $this->get_batch();

			$this->delete( $batch->key );

			wp_clear_scheduled_hook( $this->cron_hook_identifier );
		}
	}

	/**
	 * Delete queue
	 *
	 * @param string $key Queue key.
	 *
	 * @return $this
	 */
	public function delete( string $key ): AsyncProcess {
		if ( delete_site_option( $key ) ) {
			$this->logger->log( sprintf( '[Filters index] The queue <code>%s</code> was deleted ', $key ), 'debug', 'file' );
		}

		return $this;
	}

	/**
	 * Schedule event
	 */
	protected function schedule_event(): void {
		if ( $this->indexing_error_occurred() ) {
			return;
		}

		if ( ! wp_next_scheduled( $this->cron_hook_identifier ) ) {
			if ( wp_schedule_event( time(), $this->cron_interval_identifier, $this->cron_hook_identifier ) !== false ) {
				$this->logger->log( sprintf( '[Filters index] Schedule <code>%s</code> was created ', $this->cron_hook_identifier ), 'debug', 'file' );
			}
		}
	}

	/**
	 * Save queue
	 *
	 * @return $this
	 */
	public function save() {
		$key = $this->generate_key();

		if ( ! empty( $this->data ) ) {
			$this->save_result = update_site_option( $key, $this->data );
			$this->logger->log( sprintf( '[Filters index] The queue <code>%s</code> was created', $key ), 'debug', 'file' );
		}

		return $this;
	}

	/**
	 * Dispatch job is queue is not empty
	 */
	public function maybe_dispatch(): void {
		if ( $this->is_queue_empty() ) {
			$this->complete();
		} else {
			$this->data( [] );
			$this->dispatch();
		}
	}

	/**
	 * Complete
	 *
	 * Override if applicable, but ensure that the below actions are
	 * performed, or, call parent::complete().
	 */
	public function complete(): void {
		parent::complete();

		$this->build_info->add_info( 'end_filters_ts', time() );
		$this->logger->log( '[Filters index] Completed' );

		/** Wp-documentor:ignore */
		do_action( 'fibofilters/indexer/maybe_completed' );
	}
}
