<?php
/**
 * Handles the scheduled task for updating AI Style Assistant recommendations.
 *
 * @package    Ai_Try_N_Buy
 * @subpackage Ai_Try_N_Buy/includes
 * @author     Edu Brazeal <contact@edubrazeal.com>
 */

if ( ! defined( 'WPINC' ) ) {
	die;
}

class Aitnb_Style_Assistant_Cron {

	/**
	 * The hook name for the main cron event.
	 *
	 * @since    1.0.0
	 * @access   public
	 * @var      string
	 */
	const CRON_HOOK = 'aitnb_update_style_recommendations';

	/**
	 * The hook name for processing a single product.
	 *
	 * @since    1.0.0
	 * @access   public
	 * @var      string
	 */
	const SINGLE_PRODUCT_HOOK = 'aitnb_process_single_product_update';


	/**
	 * Constructor. Adds the action hook for the cron job.
	 *
	 * @since    1.0.0
	 */
	public function __construct() {
		add_filter( 'cron_schedules', array( $this, 'add_custom_cron_schedules' ) );
		add_action( self::CRON_HOOK, array( $this, 'run_update_task' ) );
		add_action( self::SINGLE_PRODUCT_HOOK, array( $this, 'process_single_product_update' ), 10, 1 );
	}

	/**
	 * Adds custom time intervals to the WP-Cron schedules.
	 *
	 * @since    1.0.0
	 * @param    array    $schedules    The existing array of cron schedules.
	 * @return   array    The modified array of cron schedules.
	 */
	public function add_custom_cron_schedules( $schedules ) {
		$schedules['twoweekly'] = array(
			'interval' => 1209600,
			'display'  => __( 'Every Two Weeks', 'virtual-ai-fit-and-style-assistant' ),
		);
		$schedules['monthly'] = array(
			'interval' => 2592000,
			'display'  => __( 'Monthly', 'virtual-ai-fit-and-style-assistant' ),
		);
		return $schedules;
	}

	/**
	 * Clears any existing schedule and sets a new one based on plugin settings.
	 *
	 * @since    1.0.0
	 */
	public static function reschedule_event() {
		self::unschedule_event();

		$options     = get_option( 'aitnb_settings', array() );
		$frequency   = isset( $options['style_assistant_frequency'] ) ? $options['style_assistant_frequency'] : 'daily';
		$api_key     = isset( $options['gemini_api_key'] ) ? $options['gemini_api_key'] : '';
		$style_model = isset( $options['gemini_style_model'] ) ? $options['gemini_style_model'] : '';

		if ( 'manual' === $frequency ) {
			return;
		}

		if ( ! empty( $api_key ) && ! empty( $style_model ) && ! wp_next_scheduled( self::CRON_HOOK ) ) {
			wp_schedule_event( time(), $frequency, self::CRON_HOOK );
		}
	}

	/**
	 * Unschedules the cron event.
	 *
	 * @since    1.0.0
	 */
	public static function unschedule_event() {
		wp_clear_scheduled_hook( self::CRON_HOOK );
		wp_clear_scheduled_hook( self::SINGLE_PRODUCT_HOOK );
	}

	/**
	 * The main function executed by the cron job.
	 *
	 * This function now dispatches individual background tasks for each product,
	 * preventing server timeouts on sites with many products.
	 *
	 * @since    1.0.0
	 */
	public function run_update_task() {
		$all_products = get_posts(
			array(
				'post_type'      => 'product',
				'posts_per_page' => -1,
				'fields'         => 'ids',
			)
		);

		if ( empty( $all_products ) ) {
			return;
		}

		$enabled_products = array();
		foreach ( $all_products as $product_id ) {
			$is_enabled = get_post_meta( $product_id, '_aitnb_enable_style_assistant', true );
			if ( 'yes' === $is_enabled ) {
				$enabled_products[] = $product_id;
			}
		}

		if ( empty( $enabled_products ) ) {
			return;
		}

		foreach ( $enabled_products as $product_id ) {
			if ( ! wp_next_scheduled( self::SINGLE_PRODUCT_HOOK, array( 'product_id' => $product_id ) ) ) {
				wp_schedule_single_event( time(), self::SINGLE_PRODUCT_HOOK, array( 'product_id' => $product_id ) );
			}
		}
	}

	/**
	 * Processes the recommendation update for a single product.
	 * This is executed as a separate background task.
	 *
	 * @since 1.0.0
	 * @param int $product_id The ID of the product to update.
	 */
	public function process_single_product_update( $product_id ) {
		$options     = get_option( 'aitnb_settings' );
		$api_key     = isset( $options['gemini_api_key'] ) ? $options['gemini_api_key'] : '';
		$style_model = isset( $options['gemini_style_model'] ) ? $options['gemini_style_model'] : '';

		if ( empty( $api_key ) || empty( $style_model ) ) {
			return;
		}

		require_once plugin_dir_path( dirname( __FILE__ ) ) . 'services/class-gemini-style-service.php';
		$service = new Aitnb_Gemini_Style_Service( $api_key, $style_model );

		$frequency  = isset( $options['style_assistant_frequency'] ) ? $options['style_assistant_frequency'] : 'daily';
		$expiration = $this->get_expiration_from_frequency( $frequency );

		$this->update_product_recommendations( $product_id, $service, $expiration );
	}

	/**
	 * Updates the recommendations for a single product.
	 *
	 * @since    1.0.0
	 * @param    int                       $product_id The ID of the product to update.
	 * @param    Aitnb_Gemini_Style_Service $service    The Gemini service instance.
	 * @param    int                       $expiration The expiration time in seconds for the transient.
	 */
	private function update_product_recommendations( $product_id, $service, $expiration ) {
		$current_product = wc_get_product( $product_id );
		if ( ! $current_product ) {
			return;
		}

		$selected_categories = get_post_meta( $product_id, '_aitnb_style_assistant_categories', true );
		if ( empty( $selected_categories ) || ! is_array( $selected_categories ) ) {
			return;
		}

		$args = array(
			'post_type'      => 'product',
			'posts_per_page' => 50,
			'fields'         => 'ids',
		);
		$all_category_products = new WP_Query( $args );
		$all_product_ids       = $all_category_products->posts;
		$product_catalog       = array();

		if ( ! empty( $all_product_ids ) ) {
			foreach ( $all_product_ids as $pid ) {
				if ( $pid === $product_id ) {
					continue;
				}

				$product_categories = wp_get_post_terms( $pid, 'product_cat', array( 'fields' => 'ids' ) );
				if ( is_wp_error( $product_categories ) ) {
					continue;
				}

				$has_matching_category = false;
				foreach ( $selected_categories as $selected_cat ) {
					if ( in_array( $selected_cat, $product_categories, true ) ) {
						$has_matching_category = true;
						break;
					}
				}

				if ( ! $has_matching_category ) {
					continue;
				}

				$product = wc_get_product( $pid );
				if ( $product ) {
					$product_catalog[] = array(
						'name' => $product->get_name(),
						'sku'  => $product->get_sku(),
					);
				}
			}
		}

		if ( empty( $product_catalog ) ) {
			return;
		}

		$product_data = array(
			'name' => $current_product->get_name(),
			'sku'  => $current_product->get_sku(),
		);

		$recommended_skus = $service->get_style_recommendations( $product_data, $product_catalog );

		if ( is_wp_error( $recommended_skus ) || empty( $recommended_skus ) ) {
			return;
		}

		$recommended_product_ids = array();
		foreach ( $recommended_skus as $sku ) {
			$rec_product_id = wc_get_product_id_by_sku( $sku );
			if ( $rec_product_id ) {
				$recommended_product_ids[] = $rec_product_id;
			}
		}

		if ( ! empty( $recommended_product_ids ) ) {
			set_transient( 'aitnb_style_recs_' . $product_id, $recommended_product_ids, $expiration );
		}
	}

	/**
	 * Converts a frequency string to an expiration time in seconds.
	 *
	 * @since    1.0.0
	 * @param    string    $frequency The frequency string (e.g., 'daily').
	 * @return   int       The expiration time in seconds.
	 */
	private function get_expiration_from_frequency( $frequency ) {
		switch ( $frequency ) {
			case 'twicedaily':
				return 12 * HOUR_IN_SECONDS;
			case 'weekly':
				return 7 * DAY_IN_SECONDS;
			case 'twoweekly':
				return 14 * DAY_IN_SECONDS;
			case 'monthly':
				return 30 * DAY_IN_SECONDS;
			case 'manual':
				return YEAR_IN_SECONDS;
			case 'daily':
			default:
				return DAY_IN_SECONDS;
		}
	}
}
