<?php
/**
 * Course bundle addon.
 *
 * @since 2.12.0
 */

namespace Masteriyo\Addons\CourseBundle;

use Masteriyo\Addons\CourseBundle\Controllers\CourseBundlesController;
use Masteriyo\Addons\CourseBundle\Models\CourseBundle;
use Masteriyo\Addons\CourseBundle\Models\OrderItemCourseBundle;
use Masteriyo\PostType\PostType;
use Masteriyo\Addons\CourseBundle\PostType\CourseBundle as PostTypeCourseBundle;
use Masteriyo\Addons\CourseBundle\Query\CourseBundleQuery;
use Masteriyo\Addons\CourseBundle\Widgets\BundleCarouselWidget;
use Masteriyo\Addons\CourseBundle\Widgets\BundleListPaginationWidget;
use Masteriyo\Addons\CourseBundle\Widgets\BundleListWidget;
use Masteriyo\Addons\ElementorIntegration\Helper as ElementorHelper;
use Masteriyo\Constants;
use Masteriyo\Enums\CourseAccessMode;
use Masteriyo\Enums\OrderStatus;
use Masteriyo\Enums\PostStatus;
use Masteriyo\Enums\UserCourseStatus;
use Masteriyo\Models\Course;
use Masteriyo\Models\User;
use Masteriyo\Query\UserCourseQuery;

defined( 'ABSPATH' ) || exit;

/**
 * Course bundle addon class.
 */
class CourseBundleAddon {

	/**
	 * Init addon.
	 *
	 * @since 2.12.0
	 */
	public function init() {
		$this->init_hooks();
	}

	/**
	 * Init hooks.
	 *
	 * @since 2.12.0
	 */
	public function init_hooks() {
		add_filter( 'masteriyo_rest_api_get_rest_namespaces', array( $this, 'register_rest_namespaces' ) );
		add_filter( 'masteriyo_register_post_types', array( $this, 'register_post_types' ) );
		add_filter( 'masteriyo_admin_submenus', array( $this, 'add_course_bundle_submenu' ) );
		add_filter( 'template_include', array( $this, 'template_loader' ), PHP_INT_MAX );
		add_filter( 'masteriyo_rest_response_course_data', array( $this, 'add_featured_image_url_to_response' ), 10, 3 );
		add_filter( 'masteriyo_add_to_cart_get_item_callable', array( $this, 'change_add_to_cart_item_callable' ), 10, 2 );
		add_filter( 'masteriyo_enqueue_scripts', array( $this, 'change_single_course_script_callback' ) );
		add_filter( 'masteriyo_rest_response_order_data', array( $this, 'modify_rest_response_order_data' ), 10, 3 );
		add_filter( 'masteriyo_shortcodes', array( $this, 'register_course_bundle_shortcode' ) );
		add_action( 'masteriyo_order_status_changed', array( $this, 'enroll_courses' ), 10, 4 );
		add_filter( 'masteriyo_get_items_key', array( $this, 'change_order_items_key' ), 10, 2 );
		add_filter( 'masteriyo_order_type_to_group', array( $this, 'add_order_type_group' ), 10 );
		add_filter( 'masteriyo_checkout_summary_your_order_item_title', array( $this, 'modify_order_summary_title' ), 10, 2 );
		add_filter( 'display_post_states', array( $this, 'add_course_bundles_page_state' ), 10, 2 );
		add_filter( 'masteriyo_can_start_course', array( $this, 'update_can_start_course' ), 10, 3 );
		add_filter( 'masteriyo_get_template', array( $this, 'change_template_for_course_bundle' ), 10, 5 );
		add_filter( 'masteriyo_sales_data', array( $this, 'add_bundles_sales_data' ), 10, 5 );
		add_filter( 'masteriyo_invoice_data', array( $this, 'modify_invoice_data' ), 10, 2 );
		add_filter( 'elementor_course_widgets', array( $this, 'elementor_widgets' ) );
		add_filter( 'masteriyo_elementor_widgets_svg_icons_data', array( $this, 'widget_svg_icons' ) );
		add_filter( 'print_inline_scripts_for_tab_product', array( $this, 'print_inline_scripts' ) );
		add_filter( 'masteriyo_order_contains_recurring_courses', array( $this, 'masteriyo_order_contains_recurring_courses_bundle' ), 10, 2 );

		add_action( 'masteriyo_new_order', array( $this, 'delete_sales_related_cache_keys' ), 10, 2 );
		add_action( 'masteriyo_update_order', array( $this, 'delete_sales_related_cache_keys' ), 10, 2 );
		add_action( 'masteriyo_trash_order', array( $this, 'delete_sales_related_cache_keys' ), 10, 2 );
	}

	/**
	 * Check if an order contains any recurring course bundles.
	 *
	 * This function determines whether an order contains any course bundles with recurring access mode.
	 * It checks course bundle item in the order and returns true if any of them have a recurring access mode.
	 *
	 * @since 2.18.1
	 *
	 * @param bool $contains_recurring_courses Whether the order contains recurring courses.
	 * @param \Masteriyo\Models\Order\Order $order The order object to check.
	 * @return bool True if the order contains any recurring course bundles, false otherwise.
	 */
	public function masteriyo_order_contains_recurring_courses_bundle( $contains_recurring_courses, $order ) {
		$is_recurring_product = $contains_recurring_courses;
		foreach ( $order->get_items( 'course-bundle' ) as $order_item ) {
			if ( $order_item instanceof OrderItemCourseBundle ) {
				$course_bundle        = masteriyo_get_course_bundle( $order_item->get_course_bundle_id() );
				$is_recurring_product = $course_bundle && CourseAccessMode::RECURRING === $course_bundle->get_access_mode();
				break;
			}
		}
		return $is_recurring_product;
	}

	/**
	 * Deletes sales-related cache keys when an order is created, updated or trashed.
	 *
	 * @since 2.14.0
	 *
	 * @param int $id The ID of the order.
	 * @param \Masteriyo\Models\Order\Order $order The order object.
	 *
	 * @return void
	 */
	public function delete_sales_related_cache_keys( $id, $order ) {
		if ( ! $id || ! ( $order instanceof \Masteriyo\Models\Order\Order ) ) {
			return;
		}

		$items = $order->get_items( 'course-bundle' );

		if ( ! empty( $items ) ) {
			masteriyo_transient_cache()->clear_caches( 'bundle_analytics_sales_group' );

			if ( masteriyo_is_current_user_admin() || masteriyo_is_current_user_manager() ) {
				masteriyo_transient_cache()->clear_caches( 'analytics_sales_group' );
			}
		}
	}

	/**
	 * Generates cache keys for analytics data.
	 *
	 * @since 2.14.0
	 *
	 * @param int|null $user_id The ID of the user. Defaults to null.
	 * @param string|null $start_date The start date in 'Y-m-d' format. Defaults to null.
	 * @param string|null $end_date The end date in 'Y-m-d' format. Defaults to null.
	 *
	 * @return array An array of cache keys for analytics data.
	 */
	private function analytics_cache_keys( $user_id = null, $start_date = null, $end_date = null ) {
		$is_admin_or_manager  = masteriyo_is_current_user_admin() || masteriyo_is_current_user_manager();
		$current_user_id      = $user_id ? $user_id : get_current_user_id();
		$start_date_formatted = $start_date ? gmdate( 'Y-m-d', strtotime( $start_date ) ) : 'no_start_date';
		$end_date_formatted   = $end_date ? gmdate( 'Y-m-d', strtotime( $end_date ) ) : 'no_end_date';

		return array(
			'sales_data'      => 'bundle_analytics_sales_data_' . ( $is_admin_or_manager ? 'all_' : $current_user_id . '_' ) . $start_date_formatted . '_' . $end_date_formatted,
			'total_earnings'  => 'bundle_analytics_total_earnings_' . ( $is_admin_or_manager ? 'all' : $current_user_id ),
			'total_refunds'   => 'bundle_analytics_total_refunds_' . ( $is_admin_or_manager ? 'all' : $current_user_id ),
			'total_discounts' => 'bundle_analytics_total_discounts_' . ( $is_admin_or_manager ? 'all' : $current_user_id ),
		);
	}

	/**
	 * Adds SVG icon data for the Elementor course bundle list widget.
	 *
	 * This method is used to add the SVG icon data for the Elementor course bundle list widget.
	 * The icon data is added to the `$icons` array, which is used by the Elementor plugin to
	 * display the widget's icon in the Elementor editor.
	 *
	 * @since 2.14.0
	 *
	 * @param array $icons The existing SVG icon data.
	 *
	 * @return array The updated SVG icon data.
	 */
	public function widget_svg_icons( $icons ) {
		$icons [] = array_merge(
			array(
				'class' => 'masteriyo-bundle-list-widget-icon',
			),
			ElementorHelper::get_widget_icon_urls( 'course-bundle-list-widget-icon' )
		);

		$icons [] = array_merge(
			array(
				'class' => 'masteriyo-bundle-carousel-widget-icon',
			),
			ElementorHelper::get_widget_icon_urls( 'course-bundle-carousel-widget-icon' )
		);

		$icons [] = array_merge(
			array(
				'class' => 'masteriyo-course-bundle-list-pagination-widget-icon',
			),
			ElementorHelper::get_widget_icon_urls( 'course-bundle-pagination-widget-icon' )
		);

		return $icons;
	}

	/**
	 * Adds Elementor widgets for the Course Bundle addon.
	 *
	 * This method is used to add the Elementor widgets for the Course Bundle addon to the
	 * list of available widgets in the Elementor plugin. The `$widgets` array is modified
	 * to include the `BundleListWidget` class, which provides a widget for displaying a
	 * list of course bundles in the Elementor editor.
	 *
	 * @since 2.14.0
	 *
	 * @param array $widgets The existing list of Elementor widgets.
	 * @return array The updated list of Elementor widgets.
	 */
	public function elementor_widgets( $widgets ) {
		$widgets[] = new BundleListWidget();
		$widgets[] = new BundleCarouselWidget();
		$widgets[] = new BundleListPaginationWidget();

		return $widgets;
	}



	/**
	 * Print inline scripts for woocommerce product.
	 *
	 * @since 2.14.0
	 */
	public function print_inline_scripts() {

		$scripts = '
		(function($) {
			$( "div.downloadable_files" ).parent().addClass( "hide_if_mto_course hide_if_mto_course_recurring hide_if_mto_course_bundle hide_if_mto_course_bundle_recurring" ).hide();
			$( ".options_group.pricing" ).addClass( "show_if_mto_course show_if_mto_course_bundle" );
			$( ".options_group.pricing, ._subscription_sign_up_fee_field, ._subscription_trial_length_field" ).addClass( "hide_if_mto_course_recurring hide_if_mto_course_bundle_recurring" );
			$( ".options_group.subscription_pricing" ).addClass( "show_if_mto_course_recurring show_if_mto_course_bundle_recurring" );
			$( ".options_group.show_if_simple.show_if_external.show_if_variable" ).addClass( "show_if_mto_course show_if_mto_course_recurring show_if_mto_course_bundle show_if_mto_course_bundle_recurring" );
			if ( $( \'#product-type\' ).val() === \'mto_course_recurring\' ) {
				$(\'option[value="mto_course_recurring"]\').show();
				$(\'option[value="mto_course"]\').hide();
				$(\'option[value="mto_course_bundle"]\').hide();
				$(\'option[value="mto_course_bundle_recurring"]\').show();
			} else if ( $( \'#product-type\' ).val() === \'mto_course_bundle_recurring\' ) {
				$(\'option[value="mto_course_recurring"]\').show();
				$(\'option[value="mto_course"]\').hide();
				$(\'option[value="mto_course_bundle"]\').hide();
				$(\'option[value="mto_course_bundle_recurring"]\').show();
			} else if ( $( \'#product-type\' ).val() === \'mto_course_bundle\' ) {
				$(\'option[value="mto_course_recurring"]\').hide();
				$(\'option[value="mto_course"]\').show();
				$(\'option[value="mto_course_bundle"]\').show();
				$(\'option[value="mto_course_bundle_recurring"]\').hide();
			}else {
				$(\'option[value="mto_course_recurring"]\').hide();
				$(\'option[value="mto_course"]\').show();
				$(\'option[value="mto_course_bundle"]\').show();
				$(\'option[value="mto_course_bundle_recurring"]\').hide();
			}
		})(jQuery);
		';

		return $scripts;
	}

	/**
	 * Modifies the invoice data for an order.
	 *
	 * This method is used to add course bundle data to the invoice data for an order.
	 * If the order contains course bundle items, the course bundle data is retrieved
	 * and added to the `course_data` key in the invoice data array.
	 *
	 * @since 2.12.0
	 *
	 * @param array $data The invoice data.
	 * @param \Masteriyo\Models\Order $order The order object.
	 *
	 * @return array The modified invoice data.
	 */
	public function modify_invoice_data( $data, $order ) {
		$items = $order->get_items( 'course-bundle' );

		if ( ! empty( $items ) && isset( $data['course_data'] ) && empty( $data['course_data'] ) ) {
			$bundle_data         = $this->get_order_item_course_bundle( $items );
			$data['course_data'] = $bundle_data;
		}

		return $data;
	}

	/**
	 * Adds sales data for course bundles within the given date range.
	 *
	 * @since 2.12.0
	 *
	 * @param array $old_data The existing sales data.
	 * @param string $start_date The start date of the date range.
	 * @param string $end_date The end date of the date range.
	 * @param array
	 * @param \Masteriyo\RestApi\Controllers\Version1\AnalyticsController $analytics_controller
	 *
	 * @return array The updated sales data.
	 */
	public function add_bundles_sales_data( $old_data, $start_date, $end_date, $course_ids, $analytics_controller ) {
		$bundles_data = $this->get_course_bundles_data();
		$bundle_ids   = $bundles_data['ids'];

		if ( empty( $bundle_ids ) ) {
			return $old_data;
		}

		if ( ! ( masteriyo_is_current_user_admin() || masteriyo_is_current_user_manager() ) ) {
			$total_earnings  = $analytics_controller->get_total_amount( $bundle_ids, OrderStatus::COMPLETED, '_conversion_total', '_total', 'total_earnings', 'bundle_analytics_sales_group', 'course-bundle', 'course_bundle_id', false );
			$total_refunds   = $analytics_controller->get_total_amount( $bundle_ids, OrderStatus::REFUNDED, '_conversion_total', '_total', 'total_refunds', 'bundle_analytics_sales_group', 'course-bundle', 'course_bundle_id', false );
			$total_discounts = $analytics_controller->get_total_amount( $bundle_ids, OrderStatus::COMPLETED, '_conversion_discount_total', '_discount_total', 'total_discounts', 'bundle_analytics_sales_group', 'course-bundle', 'course_bundle_id', false );

			$old_data['total_earnings']  = floatval( $old_data['total_earnings'] ) + $total_earnings;
			$old_data['total_refunds']   = floatval( $old_data['total_refunds'] ) + $total_refunds;
			$old_data['total_discounts'] = floatval( $old_data['total_discounts'] ) + $total_discounts;
		}

		$cache     = masteriyo_transient_cache();
		$cache_key = $this->analytics_cache_keys( null, $start_date, $end_date )['sales_data'];
		$data      = $cache->get_cache( $cache_key, 'bundle_analytics_sales_group' );

		if ( is_null( $data ) ) {

			global $wpdb;

			$data = array();
			if ( $bundle_ids ) {
				// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
				$course_ids_placeholder = implode( ',', array_fill( 0, count( $bundle_ids ), '%d' ) );

				$query_params = array_merge(
					$bundle_ids,
					array(
						PostType::ORDER,
						OrderStatus::COMPLETED,
						OrderStatus::REFUNDED,
						$start_date,
						$end_date,
					)
				);

				// phpcs:disable
				$orders_query = $wpdb->prepare(
					"
					SELECT
							DATE(p.post_modified) AS date,
							p.post_status AS status,
							COUNT(*) AS count,
							SUM(
									CASE
											WHEN meta_conversion.meta_value IS NOT NULL AND meta_conversion.meta_value != ''
											THEN CAST(meta_conversion.meta_value AS DECIMAL(10, 2))
											ELSE CAST(pm.meta_value AS DECIMAL(10, 2))
									END
							) AS amount
					FROM {$wpdb->posts} AS p
					LEFT JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id AND pm.meta_key = '_total'
					LEFT JOIN {$wpdb->postmeta} AS meta_conversion ON p.ID = meta_conversion.post_id AND meta_conversion.meta_key = '_conversion_total'
					INNER JOIN {$wpdb->prefix}masteriyo_order_items oi ON p.ID = oi.order_id
					INNER JOIN {$wpdb->prefix}masteriyo_order_itemmeta oim ON oi.order_item_id = oim.order_item_id
					WHERE oim.meta_key = 'course_bundle_id'
							AND oim.meta_value IN ($course_ids_placeholder)
							AND p.post_type = %s
							AND p.post_status IN (%s, %s)
							AND p.post_modified >= %s
							AND p.post_modified <= %s
					GROUP BY DATE(p.post_modified), p.post_status
					ORDER BY DATE(p.post_modified) ASC, p.post_status ASC
					",
					...$query_params
				);

				$orders_results = $wpdb->get_results($orders_query, ARRAY_A);
				// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared

				if ( ! $orders_results ) {
					return $old_data;
				}

				$data['earnings']['data'] = array_filter(
				$orders_results,
				function( $result ) {
					return OrderStatus::COMPLETED === $result['status'];
					}
				);

				$data['refunds']['data'] = array_filter(
					$orders_results,
					function( $result ) {
						return OrderStatus::REFUNDED === $result['status'];
					}
				);
			}
			$cache->set_cache( $cache_key, $data, DAY_IN_SECONDS, 'bundle_analytics_sales_group' );
	}

	if ( ! empty( $data ) ) {
			$data['earnings']['data'] = $this->format_series_data( array_values( $data['earnings']['data'] ?? array() ), $start_date, $end_date, '1 day' );
			$data['refunds']['data']  = $this->format_series_data( array_values( $data['refunds']['data'] ?? array() ), $start_date, $end_date, '1 day' );

			$old_data['bundles'] = $data;
	}

	return $old_data;
	}

	/**
	 * Get course bundles data.
	 *
	 * @since 2.12.0
	 *
	 * @return array
	 */
	protected function get_course_bundles_data() {
		$query = new \WP_Query(
			array(
				'post_status'    => PostStatus::PUBLISH,
				'post_type'      => PostType::COURSE_BUNDLE,
				'posts_per_page' => -1,
				'author'         => masteriyo_is_current_user_admin() || masteriyo_is_current_user_manager() ? null : get_current_user_id(),
				'fields'         => 'ids',
			)
		);

		return array(
			'ids'   => $query->posts,
			'total' => $query->post_count,
		);
	}

	/**
	 * Format series data.
	 *
	 * Prefills empty data with 0.
	 *
	 * @since 2.12.0
	 *
	 * @param array $data Table name.
	 * @param string $start Start date.
	 * @param string $end End date.
	 *
	 * @param string $interval Interval.
	 */
	protected function format_series_data( $data, $start, $end, $interval ) {
		$start = new \DateTime( $start );
		$end   = new \DateTime( $end );

		$end->modify( '+1 day' );

		$interval       = \DateInterval::createFromDateString( $interval );
		$period         = new \DatePeriod( $start, $interval, $end );
		$formatted_data = array();

		foreach ( $period as $date ) {
			$date    = $date->format( 'Y-m-d' );
			$found   = array_search( $date, array_column( $data, 'date' ), true );
			$current = array();

			if ( false !== $found ) {
				$current = isset( $data[ $found ] ) ? $data[ $found ] : array();
			}

			$formatted_data[] = array(
				'date'   => $date,
				'count'  => $current['count'] ?? 0,
				'status' => $current['status'] ?? null,
				'amount' => $current['amount'] ?? null,
			);
		}

		return $formatted_data;
	}

	/**
	 * Changes the template path for specific course bundle related templates.
	 *
	 * @since 2.12.0
	 *
	 * @param string $template Template path.
	 * @param string $template_name Template name.
	 * @param array $args Template arguments.
	 * @param string $template_path Template path from function parameter.
	 * @param string $default_path Default templates directory path.
	 *
	 * @return string
	 */
	public function change_template_for_course_bundle( $template, $template_name, $args, $template_path, $default_path ) {
		$template_map = array(
			'course-bundle/course-bundle-searchform.php' => 'course-bundle-searchform.php',
			'course-bundle/loop/bundle-loop-start.php'   => 'loop/bundle-loop-start.php',
			'course-bundle/loop/bundle-loop-end.php'     => 'loop/bundle-loop-end.php',
			'course-bundle/content-single-course-bundle.php' => 'content-single-course-bundle.php',
			'course-bundle/content-course-bundle.php'    => 'content-course-bundle.php',
		);

		if ( isset( $template_map[ $template_name ] ) ) {
			$new_template = trailingslashit( Constants::get( 'MASTERIYO_COURSE_BUNDLE_TEMPLATES' ) ) . $template_map[ $template_name ];

			return file_exists( $new_template ) ? $new_template : $template;
		}

		return $template;
	}

	/**
	 * Add a post display state for masteriyo course bundle page in the page list table.
	 *
	 * @since 2.12.0
	 *
	 * @param array $post_states An array of post display states.
	 * @param \WP_Post $post The current post object.
	 */
	public function add_course_bundles_page_state( $post_states, $post ) {
		if ( masteriyo_get_page_id( 'course-bundles' ) === $post->ID ) {
			$post_states['masteriyo_course_bundles_page'] = __( 'Masteriyo Course Bundles Page', 'learning-management-system' );
		}
		return $post_states;
	}

	/**
	 * Modify order summary title.
	 *
	 * @since 2.12.0
	 * @param string $title Summary title.
	 * @param int $id Object ID.
	 * @return string
	 */
	public function modify_order_summary_title( $title, $id ) {
		if ( PostType::COURSE_BUNDLE === get_post_type( $id ) ) {
			$title = __( 'Bundles', 'learning-management-system' );
		}
		return $title;
	}

	/**
	 * Add order type group.
	 *
	 * @since 2.12.0
	 * @param array $group Group of order item type.
	 * @return array
	 */
	public function add_order_type_group( $group ) {
		$group['course-bundle'] = 'course_bundle_lines';
		return $group;
	}

	/**
	 * Change order items key.
	 *
	 * @since 2.12.0
	 * @param string $key Order item key.
	 * @param mixed $item Order item object.
	 * @return string
	 */
	public function change_order_items_key( $key, $item ) {
		if ( is_a( $item, OrderItemCourseBundle::class ) ) {
			$key = 'course_bundle_lines';
		}
		return $key;
	}

	/**
	 * Register course bundle shortcode.
	 *
	 * @since 2.12.0
	 * @param array $shortcodes Shortcodes
	 * @return array
	 */
	public function register_course_bundle_shortcode( $shortcodes ) {
		$shortcodes['course_bundles'] = \Masteriyo\Addons\CourseBundle\Shortcodes\CourseBundlesShortcode::class;
		return $shortcodes;
	}

	/**
	 * Modify rest response order data.
	 *
	 * @since 2.12.0
	 * @param array $data Response data.
	 * @param \Masteriyo\Models\Order\Order $order Order object.
	 * @param string $context Context.
	 * @return array
	 */
	public function modify_rest_response_order_data( $data, $order, $context ) {
		$items                       = $order->get_items( 'course-bundle' );
		$data['course_bundle_lines'] = $this->get_order_item_course_bundle( $items, $context );
		return $data;
	}

	/**
	 * Get order item course bundle.
	 *
	 * @since 2.12.0
	 * @param array $items Order items.
	 * @param string $context Context. Default is 'view';
	 * @return void
	 */
	protected function get_order_item_course_bundle( $items, $context = 'view' ) {
		$course_bundle_items = array_filter(
			$items,
			function( $item ) {
				return 'course-bundle' === $item->get_type();
			}
		);

		$data = array();

		foreach ( $course_bundle_items as $course_item ) {
			$data[] = array(
				'id'                 => $course_item->get_id(),
				'name'               => wp_specialchars_decode( $course_item->get_name( $context ) ),
				'type'               => $course_item->get_type( $context ),
				'course_id'          => $course_item->get_course_bundle_id( $context ),
				'quantity'           => $course_item->get_quantity( $context ),
				'subtotal'           => $course_item->get_subtotal( $context ),
				'total'              => $course_item->get_total( $context ),
				'formatted_subtotal' => $course_item->get_rest_formatted_subtotal( $context ),
				'formatted_total'    => $course_item->get_rest_formatted_total( $context ),
			);
		}

		return $data;
	}


	/**
	 * Enroll courses from bundle.
	 *
	 * @since 2.12.0
	 * @param int $order_id Order id.
	 * @param string $old_status Old order status.
	 * @param string $new_status New order status.
	 * @param \Masteriyo\Models\Order\Order $order Order object.
	 * @return void
	 */
	public function enroll_courses( $order_id, $old_status, $new_status, $order ) {
		if ( ! empty( $order->get_items( 'course-bundle' ) ) ) {
			foreach ( $order->get_items( 'course-bundle' ) as $item ) {
				$course_bundle = masteriyo_get_course_bundle( $item->get_course_bundle_id() );
				if ( ! $course_bundle ) {
					continue;
				}
				$courses = $course_bundle->get_bundled_courses();

				foreach ( $courses as $course ) {
					$query = new UserCourseQuery(
						array(
							'course_id' => $course->get_id(),
							'user_id'   => $order->get_user_id(),
						)
					);

					$user_courses = $query->get_user_courses();
					$user_course  = empty( $user_courses ) ? masteriyo( 'user-course' ) : current( $user_courses );

					$user_course->set_course_id( $course->get_id() );
					$user_course->set_user_id( $order->get_user_id() );
					$user_course->set_price( $course->get_price() );
					$user_course->set_status( OrderStatus::COMPLETED === $order->get_status() ? UserCourseStatus::ACTIVE : UserCourseStatus::INACTIVE );
					$user_course->set_date_start( current_time( 'mysql', true ) );
					$user_course->save();
				}

				$purchased_bundles   = get_user_meta( $order->get_user_id(), '_mas_bundles', true );
				$purchased_bundles   = ! empty( $purchased_bundles ) ? $purchased_bundles : array();
				$purchased_bundles[] = $course_bundle->get_id();

				update_user_meta( $order->get_user_id(), '_mas_bundles', $purchased_bundles );
			}
		}
	}

	/**
	 * Update can start course.
	 *
	 * @param boolean $can_start_course
	 * @param Course $course
	 * @param User $user
	 * @return boolean
	 */
	public function update_can_start_course( $can_start_course, $course, $user ) {
		if ( $user || ! $course ) {
			return $can_start_course;
		}

		$course_bundle = current(
			( new CourseBundleQuery(
				array(
					'course' => $course->get_id(),
				)
			) )->get_course_bundles()
		);

		if ( empty( $course_bundle ) || ! $course_bundle instanceof CourseBundle ) {
			return $can_start_course;
		}

		$user_course_bundles = get_user_meta( $user->get_id(), '_mas_bundles', true );
		$user_course_bundles = ! empty( $user_course_bundles ) ? $user_course_bundles : array();

		if ( in_array( $course_bundle->get_id(), $user_course_bundles, true ) ) {
			$can_start_course = true;
		}

		return $can_start_course;
	}

	/**
	 * Change add to cart item callable.
	 *
	 * @since 2.12.0
	 * @param mixed $func Callable to get cart item.
	 * @param int $id Course/Bundle Id.
	 * @return string
	 */
	public function change_add_to_cart_item_callable( $func, $id ) {
		if ( PostType::COURSE_BUNDLE === get_post_type( $id ) ) {
			$func = 'masteriyo_get_course_bundle';
		}
		return $func;
	}

	/**
	 * Change single course script callback.
	 *
	 * @since 2.12.0
	 * @param array $scripts Enqueue scripts data.
	 * @return array
	 */
	public function change_single_course_script_callback( $scripts ) {
		$scripts['single-course']['callback'] = function () {
			return masteriyo_is_single_course_page() || masteriyo_is_single_course_bundle_page() || isset( $_GET['masteriyo-load-single-course-js'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		};

		masteriyo_parse_args(
			$scripts,
			array(
				'course-bundles' => array(
					'src'      => plugin_dir_url( Constants::get( 'MASTERIYO_COURSE_BUNDLE_ADDON_FILE' ) ) . '/frontend/course-bundles.js',
					'context'  => 'public',
					'callback' => function() {
						return true;
					},
					'deps'     => array( 'jquery' ),
				),
				'single-course-bundle' => array(
					'src'      => plugin_dir_url( Constants::get( 'MASTERIYO_COURSE_BUNDLE_ADDON_FILE' ) ) . '/frontend/single-course-bundle.js',
					'context'  => 'public',
					'callback' => function() {
						return true;
					},
					'deps'     => array( 'jquery' ),
				),
			)
		);

		return $scripts;
	}

	/**
	 * Add featured image url to response.
	 *
	 * @since 2.12.0
	 * @param mixed $data Response data.
	 * @param \Masteriyo\Addons\CourseBundle\Models\CourseBundle $course_bundle
	 * @param string $context Context.
	 * @return array
	 */
	public function add_featured_image_url_to_response( $data, $course_bundle, $context ) {
		if ( 'edit' === $context ) {
			return $data;
		}

		$featured = $course_bundle->get_featured_image_url();

		$data['featured_image_url'] = $featured;
		return $data;
	}

	/**
	 * Register post types.
	 *
	 * @since 2.12.0
	 * @param array $post_types
	 * @return array
	 */
	public function register_post_types( $post_types ) {
		$post_types['course-bundle'] = PostTypeCourseBundle::class;
		return $post_types;
	}

	/**
	 * Add course bundle submenu.
	 *
	 * @since 2.12.0
	 * @param array $submenus Submenus.
	 */
	public function add_course_bundle_submenu( $submenus ) {
		$submenus['course-bundles'] = array(
			'page_title' => __( 'Course Bundles', 'learning-management-system' ),
			'menu_title' => __( 'Course Bundles', 'learning-management-system' ),
			'capability' => 'edit_courses',
			'position'   => 70,
		);

		return $submenus;
	}

	/**
	 * Register namespaces.
	 *
	 * @since 2.12.0
	 *
	 * @param array $namespaces Rest namespaces.
	 * @return array
	 */
	public function register_rest_namespaces( $namespaces ) {
		$namespaces['masteriyo/v1']['course-bundle'] = CourseBundlesController::class;
		return $namespaces;
	}

	/**
	 * Template loader.
	 *
	 * @since 2.12.0
	 * @param string $template Template path.
	 * @return string
	 */
	public function template_loader( $template ) {
		global $post;

		if ( masteriyo_is_single_course_bundle_page() ) {
			masteriyo_setup_course_bundle_data( $post );
			$template = masteriyo( 'template' )->locate( 'single-course-bundle.php', trailingslashit( Constants::get( 'MASTERIYO_COURSE_BUNDLE_TEMPLATES' ) ), trailingslashit( Constants::get( 'MASTERIYO_COURSE_BUNDLE_TEMPLATES' ) ) );
		} elseif ( masteriyo_is_archive_course_bundle_page() ) {
			$template = masteriyo( 'template' )->locate( 'archive-course-bundle.php', trailingslashit( Constants::get( 'MASTERIYO_COURSE_BUNDLE_TEMPLATES' ) ), trailingslashit( Constants::get( 'MASTERIYO_COURSE_BUNDLE_TEMPLATES' ) ) );
		}

		return $template;
	}
}
