<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Jobs;

use Automattic\WooCommerce\GoogleListingsAndAds\ActionScheduler\ActionSchedulerInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Admin\Exports\Services\YouTubeOrders;
use Automattic\WooCommerce\GoogleListingsAndAds\Jobs\AbstractBatchedActionSchedulerJob;
use Automattic\WooCommerce\GoogleListingsAndAds\Jobs\ActionSchedulerJobMonitor;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareInterface;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsAwareTrait;
use Automattic\WooCommerce\GoogleListingsAndAds\Options\OptionsInterface;

defined( 'ABSPATH' ) || exit;

/**
 * Class CreateYouTubeOrderIdsCache
 *
 * Create a cache of Order IDs for a specific day that have a YouTube attribution source.
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Jobs
 * @since 2.2.0
 */
class CreateYouTubeOrderIdsCache extends AbstractBatchedActionSchedulerJob implements RecurringJobInterface, OptionsAwareInterface {
	use OptionsAwareTrait;

	/**
	 * @var YouTubeOrders
	 */
	protected $youtube_orders;

	/**
	 * @var JobRepository
	 */
	protected $job_repository;

	/**
	 * CreateYouTubeOrderIdsCache constructor.
	 *
	 * @param ActionSchedulerInterface  $action_scheduler
	 * @param ActionSchedulerJobMonitor $monitor
	 * @param YouTubeOrders             $youtube_orders
	 * @param JobRepository             $job_repository
	 */
	public function __construct( ActionSchedulerInterface $action_scheduler, ActionSchedulerJobMonitor $monitor, YouTubeOrders $youtube_orders, JobRepository $job_repository ) {
		parent::__construct( $action_scheduler, $monitor );
		$this->youtube_orders = $youtube_orders;
		$this->job_repository = $job_repository;
	}

	/**
	 * Get the name of the job.
	 *
	 * @return string
	 */
	public function get_name(): string {
		return 'create_youtube_order_ids_cache';
	}

	/**
	 * Get job batch size.
	 *
	 * @return int
	 */
	protected function get_batch_size(): int {
		/**
		 * Filters the batch size for the job.
		 *
		 * @param string Job's name
		 */
		return apply_filters( 'woocommerce_gla_batched_job_size', 100, $this->get_name() );
	}

	/**
	 * Get the date to capture orders for.
	 *
	 * @return string
	 */
	protected function get_date(): string {
		/**
		 * Filters the YouTube orders query date value.
		 *
		 * @param string Date string formatted YYYY-MM-DD
		 */
		return apply_filters( 'woocommerce_gla_youtube_order_ids_job_date', gmdate( 'Y-m-d', strtotime( '-1 day' ) ) );
	}

	/**
	 * Get a single batch of items.
	 *
	 * If no items are returned the job will stop.
	 *
	 * @param int $batch_number The batch number increments for each new batch in the job cycle.
	 *
	 * @return int[]
	 */
	public function get_batch( int $batch_number ): array {
		return $this->youtube_orders->find_orders( $this->get_date(), $this->get_batch_size(), $this->get_query_offset( $batch_number ) );
	}

	/**
	 * Process batch items.
	 *
	 * @param int[] $items A single batch of WooCommerce Order IDs from the get_batch() method.
	 *
	 * @throws \Exception If an error occurs during caching.
	 */
	protected function process_items( array $items ) {
		try {
			// Get the date for the orders.
			$date = $this->get_date();

			// Get the existing order IDs cache.
			$youtube_cache = $this->options->get( OptionsInterface::YOUTUBE_ORDER_IDS_CACHE, [] );

			// Create the date key if not already set.
			if ( ! isset( $youtube_cache[ $date ] ) || ! is_array( $youtube_cache[ $date ] ) ) {
				$youtube_cache[ $date ] = [];
			}

			// Update the order IDs in the option cache.
			$youtube_cache[ $date ] = array_unique( array_merge( $youtube_cache[ $date ], $items ) );

			$this->options->update( OptionsInterface::YOUTUBE_ORDER_IDS_CACHE, $youtube_cache );
		} catch ( \Exception $e ) {
			// Log error to WooCommerce logs before re-throwing.
			do_action(
				'woocommerce_gla_error',
				sprintf(
					'YouTube order IDs cache update failed for %s: %s',
					$date,
					$e->getMessage()
				),
				__METHOD__
			);

			// Re-throw so Action Scheduler marks the job as failed.
			throw $e;
		}
	}

	/**
	 * Called when the job is completed.
	 *
	 * @param int $final_batch_number The final batch number when the job was completed.
	 *                                If equal to 1 then no items were processed by the job.
	 */
	protected function handle_complete( int $final_batch_number ) {
		/**
		 * @var CreateMerchantReportedConversionReport
		*/
		$job = $this->job_repository->get( CreateMerchantReportedConversionReport::class );
		$job->schedule();
	}

	/**
	 * Get the name of an action hook to attach the job's start method to.
	 *
	 * @return StartHook
	 */
	public function get_start_hook(): StartHook {
		return new StartHook( "{$this->get_hook_base_name()}start" );
	}

	/**
	 * Return the recurring job's interval in seconds.
	 *
	 * @return int
	 */
	public function get_interval(): int {
		return 24 * 60 * 60; // 24 hours
	}
}