<?php

/**
 * This class is responsible for importing products to OZON.
 *
 * @link       https://icopydoc.ru
 * @since      0.1.0
 * @version    0.8.3 (29-04-2026)
 *
 * @package    IP2OZ
 * @subpackage IP2OZ/includes/import
 */

/**
 * This class is responsible for importing products to OZON.
 * 
 * Depends on the classes:   `IP2OZ_Admin`
 *                           `IP2OZ_Import_XML_Helper`
 *                           `IP2OZ_Write_File`, 
 *                           `IP2OZ_Import_Create_Simple_Product`, 
 *                           `IP2OZ_Import_Create_Variable_Product`, 
 *                           `IP2OZ_Import_Create_External_Product`
 *                           `IP2OZ_Error_Log`.
 * 
 * Depends on the constants: `IP2OZ_PLUGIN_UPLOADS_DIR_PATH`
 *
 * @since      0.1.0
 * @package    IP2OZ
 * @subpackage IP2OZ/includes/import
 * @author     Maxim Glazunov <icopydoc@gmail.com>
 */
class IP2OZ_Generation_XML {

	/**
	 * Feed ID.
	 * @var string
	 */
	protected $feed_id;

	/**
	 * Status sborki.
	 * @var int
	 */
	protected $status_sborki;

	/**
	 * Step export.
	 * @var int
	 */
	protected $step_export;

	/**
	 * Constructor.
	 * 
	 * @param string|int $feed_id
	 */
	public function __construct( $feed_id ) {

		$this->feed_id = (string) $feed_id;
		$this->status_sborki = (int) common_option_get(
			'status_sborki',
			-1,
			$this->get_feed_id(),
			'ip2oz'
		);
		$this->step_export = (int) common_option_get(
			'step_export',
			25,
			$this->get_feed_id(),
			'ip2oz'
		);

	}

	/**
	 * Run the importing products to OZON.
	 * 
	 * @return void
	 */
	public function run() {

		$syncing_with_ozon = common_option_get(
			'syncing_with_ozon',
			'disabled',
			$this->get_feed_id(),
			'ip2oz'
		);
		if ( $syncing_with_ozon === 'disabled' ) {

			// включен глобальный запрет на импорт товаров
			new IP2OZ_Error_Log( sprintf( 'FEED #%1$s; `status_sborki` = -1. %2$s; %3$s: %4$s; %5$s: %6$s',
				$this->get_feed_id(),
				__(
					'A global ban on the import of products has been enabled. We disable the CRON task',
					'import-products-to-ozon'
				),
				__( 'File', 'import-products-to-ozon' ),
				'class-ip2oz-generation-xml.php',
				__( 'Line', 'import-products-to-ozon' ),
				__LINE__
			) );
			$this->stop();
			return;

		}

		switch ( $this->get_status_sborki() ) {
			case -1:

				// сборка завершена
				new IP2OZ_Error_Log( sprintf( 'FEED #%1$s; `status_sborki` = -1. %2$s; %3$s: %4$s; %5$s: %6$s',
					$this->get_feed_id(),
					__( 'Just in case, we disable the CRON task', 'import-products-to-ozon' ),
					__( 'File', 'import-products-to-ozon' ),
					'class-ip2oz-generation-xml.php',
					__( 'Line', 'import-products-to-ozon' ),
					__LINE__
				) );
				wp_clear_scheduled_hook( 'ip2oz_cron_sborki', [ $this->get_feed_id() ] );

				break;
			case 1:

				/**
				 * импорт по расписанию стартовал. 
				 * на этом шаге мы:
				 *    - обновляем дату начала импорта
				 *    - обнуляем счётчик обработанных товаров
				 */

				$date_sborki_start = current_time( 'Y-m-d H:i' );
				common_option_upd(
					'date_sborki_start',
					$date_sborki_start,
					'no',
					$this->get_feed_id(),
					'ip2oz'
				);

				// обнуляем счётчик обработанных элементов фида
				univ_option_upd(
					'ip2oz_last_element_feed_' . $this->get_feed_id(),
					0,
					'no'
				);

				$planning_result = self::cron_sborki_task_planning( $this->get_feed_id() );
				if ( false === $planning_result ) {
					new IP2OZ_Error_Log( sprintf(
						'FEED #%1$s; ERROR: %2$s `ip2oz_cron_sborki` %3$s 1; %4$s: %5$s; %6$s: %7$s',
						$this->get_feed_id(),
						__( 'Failed to schedule a CRON task', 'import-products-to-ozon' ),
						__( 'on step', 'import-products-to-ozon' ),
						__( 'File', 'import-products-to-ozon' ),
						'class-ip2oz-generation-xml.php',
						__( 'Line', 'import-products-to-ozon' ),
						__LINE__
					) );
				} else {
					$this->set_status_sborki( 2 );
				}

				break;
			case 2:

				// получаем полседний обработанный элемент
				$last_element_feed = (int) univ_option_get(
					'ip2oz_last_element_feed_' . $this->get_feed_id(),
					0
				);
				new IP2OZ_Error_Log(
					sprintf( 'FEED #%1$s; INFO: %2$s (status_sborki = %3$s, last_element_feed = %4$s); %5$s: %6$s; %7$s: %8$s',
						$this->get_feed_id(),
						__( 'We continue to import products', 'import-products-to-ozon' ),
						$this->get_status_sborki(),
						$last_element_feed,
						__( 'File', 'import-products-to-ozon' ),
						'class-ip2oz-generation-xml.php',
						__( 'Line', 'import-products-to-ozon' ),
						__LINE__
					)
				);

				// сразу запланируем задачу через 32 секунды
				$planning_result = self::cron_sborki_task_planning( $this->get_feed_id(), 32 );
				if ( false === $planning_result ) {
					new IP2OZ_Error_Log( sprintf(
						'FEED #%1$s; ERROR: %2$s `ip2oz_cron_sborki` %3$s 2; %4$s: %5$s; %6$s: %7$s',
						$this->get_feed_id(),
						__( 'Failed to schedule a CRON task', 'yml-for-yandex-market' ),
						__( 'on step', 'yml-for-yandex-market' ),
						__( 'File', 'yml-for-yandex-market' ),
						'class-ip2oz-generation-xml.php',
						__( 'Line', 'yml-for-yandex-market' ),
						__LINE__
					) );
				}

				$time_start = time();
				$step_export = (int) common_option_get(
					'step_export',
					500,
					$this->get_feed_id(),
					'ip2oz'
				);
				$args = apply_filters(
					'ip2oz_f_query_arg',
					[
						'post_type' => 'product',
						'post_status' => 'publish',
						'posts_per_page' => $step_export,
						'offset' => $last_element_feed,
						'relation' => 'AND',
						'orderby' => 'ID',
						'fields' => 'ids'
					],
					$this->get_feed_id()
				);
				new IP2OZ_Error_Log( sprintf(
					'FEED #%1$s; %2$s =>',
					$this->get_feed_id(),
					__( 'Sending a request to the database', 'yml-for-yandex-market' )
				) );
				new IP2OZ_Error_Log( $args );
				new IP2OZ_Error_Log( json_encode( $args ) );
				$products_query = new \WP_Query( $args );
				$query_time = (int) time() - $time_start;
				new IP2OZ_Error_Log( sprintf(
					'FEED #%1$s; %2$s: %3$s; %4$s: %5$s; %6$s: %7$s',
					$this->get_feed_id(),
					__( 'The query time to the database was', 'yml-for-yandex-market' ),
					$query_time,
					__( 'File', 'yml-for-yandex-market' ),
					'class-ip2oz-generation-xml.php',
					__( 'Line', 'yml-for-yandex-market' ),
					__LINE__
				) );
				$script_execution_time = (int) common_option_get(
					'script_execution_time',
					'26',
					$this->get_feed_id(),
					'ip2oz'
				);
				if ( $script_execution_time == 0 ) {
					// TODO: 18-09-2025 по мере перехода других плагинов на новое ядро в которых есть common_option_get эту проверку можно будет удалить
					$script_execution_time = 26;
				}
				if ( $query_time > $script_execution_time ) {
					new IP2OZ_Error_Log( sprintf(
						'FEED #%1$s; WARNING: %2$s: %3$s > %4$s. %5$s "%6$s" %7$s %8$s %9$s; %10$s: %11$s; %12$s: %13$s',
						$this->get_feed_id(),
						__( 'The query time to the database was', 'yml-for-yandex-market' ),
						$query_time,
						$script_execution_time,
						__(
							'If you experience freezes when creating the feed, try increasing the',
							'yml-for-yandex-market'
						),
						__( 'The maximum script execution time', 'yml-for-yandex-market' ),
						__( 'parameter to', 'yml-for-yandex-market' ),
						$query_time + 5,
						__( 'points', 'yml-for-yandex-market' ),
						__( 'File', 'yml-for-yandex-market' ),
						'class-ip2oz-generation-xml.php',
						__( 'Line', 'yml-for-yandex-market' ),
						__LINE__
					) );
				}
				if ( $products_query->have_posts() ) {
					new IP2OZ_Error_Log( sprintf(
						'FEED #%1$s; %2$s: %3$s; %4$s: %5$s; %6$s: %7$s',
						$this->get_feed_id(),
						__( 'The number of records returned by the server', 'yml-for-yandex-market' ),
						count( $products_query->posts ),
						__( 'File', 'yml-for-yandex-market' ),
						'class-ip2oz-generation-xml.php',
						__( 'Line', 'yml-for-yandex-market' ),
						__LINE__
					) );
					$api = new IP2OZ_Api();
					for ( $i = 0; $i < count( $products_query->posts ); $i++ ) {
						$product_id = $products_query->posts[ $i ];
						new IP2OZ_Error_Log( sprintf(
							'FEED #%1$s; INFO: %2$s ID = %3$s; %4$s: %5$s; %6$s: %7$s',
							$this->get_feed_id(),
							__( 'Getting started with the product', 'yml-for-yandex-market' ),
							$product_id,
							__( 'File', 'yml-for-yandex-market' ),
							'class-ip2oz-generation-xml.php',
							__( 'Line', 'yml-for-yandex-market' ),
							__LINE__
						) );

						$answer_arr = $api->product_sync( $product_id );
						if ( true === $answer_arr['status'] ) {
							new IP2OZ_Error_Log( sprintf(
								'FEED № %1$s; товара с $product_id = %2$s успешно импортирован; Файл: %3$s; Строка: %4$s',
								$this->get_feed_id(),
								$product_id,
								'class-ip2oz-generation-xml.php',
								__LINE__
							) );
						} else {
							new IP2OZ_Error_Log( sprintf(
								'FEED № %1$s; ошибка добавления товара с $product_id = %2$s; Файл: %3$s; Строка: %4$s',
								$this->get_feed_id(),
								$product_id,
								'class-ip2oz-generation-xml.php',
								__LINE__
							) );
							new IP2OZ_Error_Log( $answer_arr );
						}

						$time_end = time();
						$time = $time_end - $time_start;
						if ( $time > $script_execution_time ) {
							break;
						} else {
							$last_element_feed++;
						}
					}
					univ_option_upd(
						'ip2oz_last_element_feed_' . $this->get_feed_id(),
						$last_element_feed,
						'no'
					);

				} else {
					new IP2OZ_Error_Log( sprintf(
						'FEED #%1$s; %2$s: 0; %3$s: %4$s; %5$s: %6$s',
						$this->get_feed_id(),
						__( 'The number of records returned by the server', 'yml-for-yandex-market' ),
						__( 'File', 'yml-for-yandex-market' ),
						'class-ip2oz-generation-xml.php',
						__( 'Line', 'yml-for-yandex-market' ),
						__LINE__
					) );
					$this->set_status_sborki( 3 );
				}

				break;
			case 3:

				$this->stop();

				break;
			default:

				$this->stop();

			// end default
		} // end switch($status_sborki)
		return; // final return from public function phase()

	}

	/**
	 * Stops the import.
	 * 
	 * @return void
	 */
	public function stop() {

		if ( 'once' === common_option_get( 'run_cron', 'disabled', $this->get_feed_id(), 'ip2oz' ) ) {
			// если был одноразовый полный импорт - переводим переключатель в `отключено`
			common_option_upd( 'run_cron', 'disabled', 'no', $this->get_feed_id(), 'ip2oz' );
		}
		$this->set_status_sborki( -1 );
		wp_clear_scheduled_hook( 'ip2oz_cron_sborki', [ $this->get_feed_id() ] );
		do_action( 'ip2oz_after_import', $this->get_feed_id(), 'full' );

	}

	/**
	 * Проверим, нужно ли отправлять запрос к API при обновлении поста.
	 * 
	 * @param int $post_id
	 * 
	 * @return bool
	 */
	public function check_ufup( $post_id ) {

		$ip2oz_ufup = common_option_get(
			'syncing_with_ozon',
			'disabled',
			$this->get_feed_id(),
			'ip2oz'
		);
		if ( $ip2oz_ufup === 'enabled' ) {
			$status_sborki = (int) common_option_get(
				'status_sborki',
				false,
				$this->get_feed_id(),
				'ip2oz'
			);
			if ( $status_sborki > -1 ) {
				// если идет сборка фида - пропуск
				return false;
			} else {
				return true;
			}
		} else {
			return false;
		}

	}

	/**
	 * Set and save status sborki.
	 * 
	 * @param int $status_sborki
	 * 
	 * @return void
	 */
	private function set_status_sborki( $status_sborki ) {

		common_option_upd(
			'status_sborki',
			(string) $status_sborki,
			'no',
			$this->get_feed_id(),
			'ip2oz'
		);
		$this->status_sborki = (int) $status_sborki;

	}

	/**
	 * Get feed ID.
	 * 
	 * @return string
	 */
	protected function get_feed_id() {
		return $this->feed_id;
	}

	/**
	 * Get status sborki.
	 * 
	 * @return string
	 */
	protected function get_status_sborki() {
		return $this->status_sborki;
	}

	/**
	 * Get step export.
	 * 
	 * @return string
	 */
	protected function get_step_export() {
		return $this->step_export;
	}

	/**
	 * Cron sborki task planning.
	 * 
	 * @param string $feed_id
	 * @param int $delay_second Scheduling task CRON in N seconds.
	 * 
	 * @return bool|WP_Error
	 */
	public static function cron_sborki_task_planning( $feed_id, $delay_second = 5 ) {

		wp_clear_scheduled_hook( 'ip2oz_cron_sborki', [ $feed_id ] );
		if ( ! wp_next_scheduled( 'ip2oz_cron_sborki', [ $feed_id ] ) ) {
			$planning_result = wp_schedule_single_event(
				current_time( 'timestamp', 1 ) + $delay_second, // добавим 5 секунд
				'ip2oz_cron_sborki',
				[ $feed_id ]
			);
		} else {
			$planning_result = false;
		}

		return $planning_result;

	}

}