<?php
/**
 * This file belongs to the YIT Framework.
 *
 * This source file is subject to the GNU GENERAL PUBLIC LICENSE (GPL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl-3.0.txt
 *
 * @package YITH\PreOrder\Includes
 * @author YITH <plugins@yithemes.com>
 */

if ( ! defined( 'YITH_WCPO_VERSION' ) ) {
	exit( 'Direct access forbidden.' );
}

if ( ! class_exists( 'YITH_Pre_Order_Cron' ) ) {
	/**
	 * Class YITH_Pre_Order_Cron
	 *
	 */
	class YITH_Pre_Order_Cron {

		/**
		 * Main Instance
		 *
		 * @var YITH_Pre_Order_Cron
		 */
		protected static $instance;

		/**
		 * Returns single instance of the class
		 *
		 * @return YITH_Pre_Order_Cron
		 */
		public static function get_instance() {
			if ( is_null( self::$instance ) ) {
				self::$instance = new self();
			}

			return self::$instance;
		}

		/**
		 * Construct
		 */
		public function __construct() {
			add_filter( 'cron_schedules', array( $this, 'cron_schedule' ), 15 ); //phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
			add_action( 'init', array( $this, 'add_cron_jobs' ), 15 );

			// Cron hooks.
			add_action( 'ywpo_pre_order_completed_check', array( $this, 'pre_order_completed_check' ) );
			add_action( 'ywpo_release_date_reminder_check', array( $this, 'release_date_reminder_check' ) );
			add_action( 'ywpo_pay_reminder_check', array( $this, 'pay_reminder_check' ) );
		}

		/**
		 * Define the schedule for the Pre-Order cron event.
		 *
		 * @param array $schedules Schedules array.
		 * @return array
		 */
		public function cron_schedule( $schedules ) {
			$pre_order_completed_interval = apply_filters( 'ywpo_pre_order_completed_check_interval', 60, $schedules );
			$interval_in_minutes          = $pre_order_completed_interval / 60;

			$schedules['ywpo_pre_order_completed_check_recurrence'] = array(
				'interval' => $pre_order_completed_interval,
				'display'  => sprintf(
					// translators: %d: number of minutes.
					_n(
						'Every %d minute',
						'Every %d minutes',
						$interval_in_minutes,
						'yith-pre-order-for-woocommerce'
					),
					$interval_in_minutes
				),
			);

			return $schedules;
		}

		/**
		 * Add scheduled events to wp-cron if not already added
		 */
		public function add_cron_jobs() {
			if ( ! wp_next_scheduled( 'ywpo_pre_order_completed_check' ) ) {
				wp_schedule_event( time(), 'ywpo_pre_order_completed_check_recurrence', 'ywpo_pre_order_completed_check' );
			}
			if ( ! wp_next_scheduled( 'ywpo_release_date_reminder_check' ) ) {
				wp_schedule_event( time(), apply_filters( 'ywpo_release_date_reminder_check_recurrence', 'daily' ), 'ywpo_release_date_reminder_check' );
			}
			if ( ! wp_next_scheduled( 'ywpo_pay_reminder_check' ) ) {
				wp_schedule_event( time(), apply_filters( 'ywpo_pay_reminder_check_recurrence', 'daily' ), 'ywpo_pay_reminder_check' );
			}
		}

		/**
		 * Cron Job to check and complete the pre-orders that have reach their release dates.
		 */
		public function pre_order_completed_check() {
			do_action( 'ywpo_before_pre_order_completed_check' );

			$args = apply_filters(
				'ywpo_pre_order_completed_check_query_args',
				array(
					'post_type'    => 'shop_order',
					'return'       => 'ids',
					'limit'        => -1,
					'_ywpo_status' => 'waiting',
				)
			);

			$orders = wc_get_orders( $args );

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

			YITH_Pre_Order_Orders_Manager()::complete_pre_orders( $orders );

			do_action( 'ywpo_after_pre_order_completed_check', $orders );
		}

		/**
		 * Check if pre-order products are about to be released and notify the admin.
		 */
		public function release_date_reminder_check() {
			do_action( 'ywpo_before_release_date_reminder_check' );

			$args = array(
				'post_type'   => array( 'product', 'product_variation' ),
				'numberposts' => - 1,
				'fields'      => 'ids',
				'meta_query'  => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
					'relation' => 'AND',
					array(
						'key'     => '_ywpo_preorder',
						'value'   => 'yes',
						'compare' => '=',
					),
					array(
						'key'     => '_ywpo_preorder_notified',
						'compare' => 'NOT EXISTS',
					),
				),
			);

			$products = get_posts( $args );

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

			$products = apply_filters( 'ywpo_release_date_reminder_check_products', $products );

			WC()->mailer();

			foreach ( $products as $product_id ) {
				$product   = wc_get_product( $product_id );
				$timestamp = YITH_Pre_Order_Utils::get_for_sale_date_timestamp( $product );

				if ( ! $timestamp ) {
					continue;
				}

				$num_days    = get_option( 'yith_wcpo_notification_number_days', 1 );
				$notify_date = strtotime( sprintf( '-%d days', $num_days ), (int) $timestamp );

				if ( apply_filters( 'ywpo_release_date_reminder_check_time_condition', time() > $notify_date, $product, $timestamp, $num_days, $notify_date ) ) {
					do_action( 'ywpo_release_date_reminder', $product );
				}
			}

			do_action( 'ywpo_after_release_date_reminder_check', $products );
		}

		/**
		 * Check for completed pre-orders that are also pending payment and send notification to the customer.
		 */
		public function pay_reminder_check() {
			if ( 'yes' !== get_option( 'ywpo_payment_reminder_email_notification_enabled', 'yes' ) ) {
				return;
			}

			do_action( 'ywpo_before_pay_reminder_check' );

			if ( yith_plugin_fw_is_wc_custom_orders_table_usage_enabled() ) {
				$args = apply_filters(
					'ywpo_pay_reminder_check_query_args',
					array(
						'type'       => 'shop_order',
						'return'     => 'ids',
						'limit'      => -1,
						'meta_query' => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
							array(
								'key'   => '_ywpo_status',
								'value' => 'completed',
							),
							array(
								'key'   => '_ywpo_pending_payment',
								'value' => 'yes',
							),
						),
					)
				);
			} else {
				$args = apply_filters(
					'ywpo_pay_reminder_check_query_args',
					array(
						'post_type'             => 'shop_order',
						'return'                => 'ids',
						'limit'                 => -1,
						'_ywpo_status'          => 'completed',
						'_ywpo_pending_payment' => 'yes',
					)
				);
			}

			$orders = wc_get_orders( $args );

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

			$orders = apply_filters( 'ywpo_pay_reminder_check_orders', $orders );

			WC()->mailer();

			foreach ( $orders as $order_id ) {
				$order = wc_get_order( $order_id );

				// If the customer has been notified for this pre-order, skip.
				if ( apply_filters( 'ywpo_pay_reminder_email_sent', 'yes' === $order->get_meta( '_ywpo_pay_reminder_email_sent' ), $order_id ) ) {
					continue;
				}

				// Double-check the order is an upon release pre-order.
				if ( $order instanceof WC_Order && ywpo_is_upon_release_order( $order ) ) {
					// The pay reminder is only for upon release orders. Therefore, the first (and unique) entry
					// in the '_ywpo_pre_order_items' array is the only possible WC_Order_Item_Product ID.
					$item_id = key( $order->get_meta( '_ywpo_pre_order_items' ) );
					$item    = is_numeric( $item_id ) ? new WC_Order_Item_Product( $item_id ) : '';

					if ( $item instanceof WC_Order_Item_Product && $item->get_product() instanceof WC_Product ) {
						$release_date = $item->get_meta( '_ywpo_item_for_sale_date' );
						$num_days     = get_option( 'ywpo_payment_reminder_number_of_days', 1 );
						$notify_date  = strtotime( sprintf( '+%d days', $num_days ), (int) $release_date );

						// If the pay reminder timestamp has passed, send the email notification.
						if ( apply_filters( 'ywpo_pay_reminder_check_time_condition', time() > $notify_date, $item, $order, $notify_date, $release_date, $num_days ) ) {
							do_action( 'ywpo_payment_reminder_email', $order, $item->get_product(), $item->get_id() );
						}
					}
				}
			}

			do_action( 'ywpo_after_pay_reminder_check', $orders );
		}
	}
}

/**
 * Unique access to instance of YITH_Pre_Order_Cron class
 *
 * @return YITH_Pre_Order_Cron
 */
function YITH_Pre_Order_Cron() { // phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
	return YITH_Pre_Order_Cron::get_instance();
}
