<?php
declare( strict_types=1 );

namespace FiboFilters\Indexer;

use FiboFilters\Indexer\Filters\Indexer;
use FiboFilters\Util\Helpers;
use FiboFilters\WooCommerce\Helper as WooCommerceHelper;

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

/**
 * BackgroundProductUpdater Class
 */
class BackgroundProductUpdater {

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

	/** @var DescriptorsCache */
	private $descriptors_cache;

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

	/** @var WooCommerceHelper */
	private $woocommerce_helper;

	public function __construct( BuildInfo $build_info, DescriptorsCache $descriptors_cache, Indexer $indexer, WooCommerceHelper $woocommerce_helper ) {
		$this->build_info         = $build_info;
		$this->descriptors_cache  = $descriptors_cache;
		$this->indexer            = $indexer;
		$this->woocommerce_helper = $woocommerce_helper;
	}

	public function init(): void {
		add_action( 'fibofilters/indexer/updater/background_product_update', [ $this, 'handle' ], 10, 2 );
	}

	/**
	 * Update product in index
	 *
	 * @param string $action Action.
	 * @param int    $post_id Product ID.
	 */
	public function handle( string $action, int $post_id ): void {
		if ( empty( $post_id ) ) {
			return;
		}
		if ( $post_id <= 0 ) {
			return;
		}

		// We always update individual products on the 'main' index.
		$this->indexer->set_index_role( 'main' );

		switch ( $action ) {
			case 'update':
				$this->indexer->update( $post_id, true, 'updater' );
				$this->descriptors_cache->process_descriptors_files();
				do_action( 'fibofilters/indexer/updater/product_update/after', $post_id );
				break;
			case 'delete':
				$this->indexer->delete( $post_id, true );
				do_action( 'fibofilters/indexer/updater/product_delete/after', $post_id );
				break;
		}
	}

	/**
	 * Schedule to update or delete product in background
	 *
	 * @param string $action Action.
	 * @param int    $post_id Product ID.
	 */
	public function schedule( string $action, int $post_id ): void {
		$queue = $this->woocommerce_helper->get_queue();
		if ( empty( $queue ) ) {
			return;
		}

		// Skip if index isn't yet completed.
		if ( $this->build_info->get_info( 'status', 'main' ) !== 'completed' ) {
			$queue->cancel_all( 'fibofilters/indexer/updater/background_product_update' );

			return;
		}

		// Skip if triggered from order.
		if ( Helpers::is_running_inside_class( 'WC_Order', 20 ) && $action !== 'delete' ) {
			return;
		}

		// Check if there is task scheduled for this product.
		$scheduled_updates = $queue->search(
			[
				'hook'   => 'fibofilters/indexer/updater/background_product_update',
				'args'   => [
					'action'  => $action,
					'post_id' => $post_id,
				],
				'status' => 'pending',
			]
		);

		// Break if action for this product is already scheduled.
		if ( ! empty( $scheduled_updates ) ) {
			return;
		}

		// Preventing creation of too large queue of products to update in the index.
		$all_scheduled_updates = $queue->search(
			[
				'hook'     => 'fibofilters/indexer/updater/background_product_update',
				'status'   => 'pending',
				'per_page' => - 1,
			]
		);

		$max_scheduled_updates = apply_filters( 'fibofilters/indexer/updater/max_scheduled_updates', 200 );

		if ( count( $all_scheduled_updates ) < $max_scheduled_updates ) {
			$queue->schedule_single(
				$action === 'update' ? time() + 10 : time() + 1,
				'fibofilters/indexer/updater/background_product_update',
				[
					'action'  => $action,
					'post_id' => $post_id,
				]
			);
		}
	}
}
