<?php
/**
 * Adds items to track to the action scheduler.
 *
 * @package Ealab\Deployer
 */

namespace EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking;

use EaLab\WP_Deployer\vendor\Dakwamine\Component\ComponentBasedObject;
use EaLab\WP_Deployer\vendor\Dakwamine\Component\Exception\UnmetDependencyException;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Exception\WpDeployException;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Tracking\ItemTracker\ItemTrackerBase;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\CommonDataStorage;
use EaLab\WP_Deployer\vendor\Ealab\Deployer\Utility\Date;
use Exception;

/**
 * Adds items to track to the action scheduler.
 *
 * This is useful for processes that require to track a large number of items,
 * usually when enabling a tracker on a large table.
 */
class TrackScheduler extends ComponentBasedObject {
	/**
	 * Action scheduler callback to track an item.
	 */
	public static function registerTrackItemActionCallback(): void {
		add_action(
			'deploy_sync_content/track_item',
			/**
			 * Tracks an item using the action scheduler.
			 *
			 * @param string              $tracker_id The tracker ID to use.
			 * @param array               $local_ids The local IDs of the items to track.
			 * @param array<string,mixed> $stats The stats of the items to track. Only for informational purposes.
			 *     Contains the following keys:
			 *     - "count" (int): The number of items to track of this action.
			 * @param string              $batch_id Groups actions together.
			 * @param bool                $no_update Whether to update the tracking data or not. Default is false.
			 *
			 * @throws WpDeployException If the tracker is not found.
			 * @throws UnmetDependencyException Missing code dependencies.
			 * @throws Exception Unrecoverable error.
			 */
			function ( string $tracker_id, array $local_ids, array $stats, string $batch_id, bool $no_update = false ) {
				$tracking_manager = ComponentBasedObject::getRootComponentByClassName( TrackingManager::class, true );

				$tracker = $tracking_manager->getItemTrackerByTrackerId( $tracker_id );

				if ( empty( $tracker ) || empty( $local_ids ) ) {
					return;
				}

				// track_mode will tell the ItemTrackerBase object to track using
				// the suitable track date initializer.
				$result = $tracker->trackItemByLocalId(
					$local_ids,
					null,
					array(
						'no_update'  => $no_update,
						'track_mode' => ItemTrackerBase::TRACK_MODE__TRACKER_INIT,
					)
				);

				$current_date = Date::getCurrentDateTime()->format( Date::DB_DATETIME_FORMAT_MILLISECONDS );

				$result = array_map(
					function ( $uuid ) use ( $current_date, $tracker_id ) {
						// Prefix the UUIDs with the tracker ID and date.
						return $current_date . ': Tracked ' . $tracker_id . '-' . $uuid;
					},
					$result
				);

				$common_data_storage = ComponentBasedObject::getRootComponentByClassName( CommonDataStorage::class, true );

				$common_data_storage->store( $batch_id, json_encode( $result ), 'deploy_sync_content/track_item' );
			},
			10,
			5
		);
	}

	/**
	 * Tracks an item using the action scheduler.
	 *
	 * @param string     $tracker_id The tracker ID to use.
	 * @param array      $local_ids The local IDs of the item to track. Supports composite local IDs.
	 * @param array|null $options Options for the tracking. Available options:
	 *     - "no_update" (bool): If true, the already tracked items will not get their dates updated.
	 *
	 * @return int The action ID of the tracking action.
	 */
	public function trackItems( string $tracker_id, array $local_ids, ?array $options = array() ): int {
		$batch_id = 'tracking-queue';

		require_once DEPLOY_SYNC_CONTENT__PLUGIN_DIR . 'vendor/woocommerce/action-scheduler/action-scheduler.php';
		! did_action( 'action_scheduler_init' ) && do_action( 'action_scheduler_init' );

		return as_schedule_single_action(
			// Delay the tracking by 10 seconds to leave enough time for the plugin to be ready.
			time() + 10,
			'deploy_sync_content/track_item',
			array(
				$tracker_id,
				$local_ids,
				array(
					'count' => count( $local_ids ),
				),
				$batch_id,
				$options['no_update'] ?? false,
			),
			$batch_id,
			false
		);
	}
}
