<?php
/**
 * Registers a cron component to get product recommendations.
 *
 * @package     EDD\Pro\Cron\Components
 * @copyright   Copyright (c) 2025, Sandhills Development, LLC
 * @license     https://opensource.org/licenses/gpl-2.0.php GNU Public License
 * @since       3.6.2
 */

namespace EDD\Pro\Cron\Components;

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore

use EDD\Cron\Components\Component;
use EDD\Pro\Recommendations\API;
use EDD\Pro\Recommendations\Products;
use EDD\Cron\Events\SingleEvent;

/**
 * Recommendations.
 *
 * @since 3.6.2
 */
class Recommendations extends Component {

	/**
	 * The component ID.
	 *
	 * @var string
	 */
	protected static $id = 'recommendations';

	/**
	 * The number of products to sync per step.
	 *
	 * @var int
	 */
	private $per_step = 30;

	/**
	 * Get the subscribed events.
	 *
	 * @since 3.6.2
	 * @return array
	 */
	public static function get_subscribed_events(): array {
		if ( ! edd_get_option( 'cart_recommendations', false ) ) {
			return array();
		}

		return array(
			'edd_ingest_products'              => 'ingest_products',
			'edd_sync_single_download'         => 'ingest_single_product',
			'edd_pro_weekly_scheduled_events'  => 'sync_all_products',
			'edd_sync_all_products'            => 'sync_all_products',
			'edd_sync_product_recommendations' => 'sync_single_product',
		);
	}

	/**
	 * Sync recommendations.
	 *
	 * @since 3.6.2
	 * @param int $step The step number.
	 * @return void
	 */
	public function ingest_products( $step = 0 ) {

		// For now, only sync the most recent 150 products.
		if ( $step >= 5 ) {
			return;
		}

		$api = new API();
		if ( ! $api->can_make_request() ) {
			return;
		}

		$products = Products::get_all_download_ids(
			array(
				'posts_per_page' => $this->per_step,
				'offset'         => $step * $this->per_step,
				'orderby'        => 'ID',
				'order'          => 'DESC',
			)
		);
		if ( empty( $products ) ) {
			if ( ! edd_has_upgrade_completed( 'cart_recommendations_initial_sync' ) ) {
				edd_set_upgrade_complete( 'cart_recommendations_initial_sync' );
				$weekly_next       = SingleEvent::next_scheduled( 'edd_weekly_scheduled_events' );
				$time_until_weekly = $weekly_next ? ( $weekly_next - time() ) : false;

				if ( ( ! $time_until_weekly || $time_until_weekly > 10 * MINUTE_IN_SECONDS ) && ! SingleEvent::next_scheduled( 'edd_sync_all_products' ) ) {
					// On the initial ingestion, we'll sync all products.
					SingleEvent::add(
						time() + ( 3 * MINUTE_IN_SECONDS ),
						'edd_sync_all_products'
					);
				}
			}
			return;
		}

		$response = $api->ingest( $products );
		if ( is_wp_error( $response ) ) {
			edd_debug_log( $response->get_error_message() );
			return;
		}

		if ( empty( $response['success'] ) ) {
			edd_debug_log( print_r( $response['errors'], true ) );
			return;
		}

		$number_synced = $response['processed'] ?? 0;
		edd_debug_log( 'Recommendations synced: ' . $number_synced );
		++$step;

		SingleEvent::add(
			time() + ( 2 * MINUTE_IN_SECONDS ),
			'edd_ingest_products',
			array(
				$step,
			)
		);
	}

	/**
	 * Sync a single download.
	 *
	 * @since 3.6.2
	 * @param int $download_id The download ID.
	 * @return void
	 */
	public function ingest_single_product( $download_id ) {
		$api = new API();
		if ( ! $api->can_make_request() ) {
			return;
		}

		$response = $api->ingest( array( $download_id ) );
		if ( is_wp_error( $response ) ) {
			edd_debug_log( $response->get_error_message() );
			return;
		}

		edd_debug_log( 'Download synced: ' . $download_id );

		delete_post_meta( $download_id, '_edd_recommendations_synced' );
		if ( SingleEvent::next_scheduled( 'edd_sync_product_recommendations', array( $download_id ) ) ) {
			return;
		}

		SingleEvent::add(
			time() + ( 2 * MINUTE_IN_SECONDS ),
			'edd_sync_product_recommendations',
			array(
				$download_id,
			)
		);
	}

	/**
	 * Sync product recommendations.
	 *
	 * @since 3.6.2
	 * @param int $product_id The product ID.
	 * @return void
	 */
	public function sync_single_product( $product_id ) {
		$api = new API();
		if ( ! $api->can_make_request() ) {
			return;
		}

		$last_synced = get_post_meta( $product_id, '_edd_recommendations_synced', true );
		// If the product has been synced in the last day, return.
		if ( $last_synced && $last_synced > time() - DAY_IN_SECONDS ) {
			return;
		}

		$recommendations = $api->get_recommendations(
			array(
				'product_ids' => array( $product_id ),
				'type'        => 'complementary',
				'limit'       => 20,
			)
		);

		if ( ! is_wp_error( $recommendations ) && ! empty( $recommendations['recommendations'] ) ) {
			update_post_meta( $product_id, '_edd_cached_recommendations', $recommendations['recommendations'] );
			update_post_meta( $product_id, '_edd_recommendations_synced', time() );
		}
	}

	/**
	 * Sync all products.
	 *
	 * @since 3.6.2
	 * @param int $step The step number.
	 * @return void
	 */
	public function sync_all_products( $step = 0 ) {
		// For now, only sync the most recent 150 products.
		if ( $step >= 5 ) {
			return;
		}

		$api = new API();
		if ( ! $api->can_make_request() ) {
			return;
		}

		$products = Products::get_all_download_ids(
			array(
				'posts_per_page' => $this->per_step,
				'offset'         => $step * $this->per_step,
				'orderby'        => 'ID',
				'order'          => 'DESC',
			)
		);
		if ( empty( $products ) ) {
			return;
		}

		$recommendations = $api->get_recommendations(
			array(
				'product_ids' => $products,
				'type'        => 'complementary',
				'limit'       => 20,
			)
		);
		if ( empty( $recommendations['recommendations'] ) ) {
			return;
		}

		foreach ( $recommendations['recommendations'] as $product_id => $recommendation ) {
			update_post_meta( $product_id, '_edd_cached_recommendations', $recommendation );
			update_post_meta( $product_id, '_edd_recommendations_synced', time() );
		}

		++$step;
		SingleEvent::add(
			time() + ( 2 * MINUTE_IN_SECONDS ),
			'edd_sync_all_products',
			array(
				$step,
			)
		);
	}
}
