<?php
/**
 * Booking admin
 *
 * @package WooCommerce Bookings
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * WC_Bookings_Admin class.
 *
 * Handles booking admin functionality.
 *
 * @version 3.9.0
 */
class WC_Bookings_Admin {
	private static $_this;

	/**
	 * Constructor.
	 *
	 * @since 1.13.0
	 */
	public function __construct() {
		self::$_this = $this;

		add_action( 'init', array( $this, 'init' ) );

		add_filter( 'post_updated_messages', array( $this, 'post_updated_messages' ) );
		add_action( 'admin_init', array( $this, 'init_tabs' ) );
		add_action( 'admin_init', array( $this, 'include_post_type_handlers' ) );
		add_action( 'admin_init', array( $this, 'include_meta_box_handlers' ) );
		add_action( 'admin_init', array( $this, 'redirect_new_add_booking_url' ) );
		add_filter( 'product_type_options', array( $this, 'product_type_options' ) );
		add_action( 'admin_enqueue_scripts', array( $this, 'styles_and_scripts' ) );
		add_action( 'woocommerce_product_options_general_product_data', array( $this, 'booking_data' ) );
		add_filter( 'product_type_options', array( $this, 'booking_product_type_options' ) );
		add_action( 'woocommerce_after_order_itemmeta', array( $this, 'booking_display' ), 10, 3 );
		add_action( 'woocommerce_debug_tools', array( $this, 'bookings_debug_tools' ) );

		// Saving data.
		add_action( 'woocommerce_admin_process_product_object', array( $this, 'set_props' ), 20 );

		add_filter( 'woocommerce_product_type_query', array( $this, 'maybe_override_product_type' ), 10, 2 );

		add_action( 'before_delete_post', array( $this, 'handle_deleted_bookable_product' ) );
		add_filter( 'get_booking_products_args', array( $this, 'modify_booking_products_query' ) );
	}

	public function init() {
		add_action( 'woocommerce_product_duplicate', array( $this, 'woocommerce_duplicate_product' ), 10, 2 );
	}

	/**
	 * Bookings debug tools in WooCommerce > Status > Tools.
	 *
	 * @param array $tools
	 */
	public function bookings_debug_tools( $tools ) {
		$bookings_tools = array(
			'clean_person_types'             => array(
				'name'     => __( 'Clean unused Person Types from DB', 'woocommerce-bookings' ),
				'button'   => __( 'Clean Person Types', 'woocommerce-bookings' ),
				'desc'     => __( 'This tool will clean the person types that are not used by any booking or a product.', 'woocommerce-bookings' ),
				'callback' => array( 'WC_Bookings_Tools', 'clean_person_types' ),
			),
			'clear_expired_in_cart_bookings' => array(
				'name'     => __( 'Clear expired In Cart bookings', 'woocommerce-bookings' ),
				'button'   => __( 'Clear', 'woocommerce-bookings' ),
				'desc'     => __( 'This tool will clear all expired In Cart bookings.', 'woocommerce-bookings' ),
				'callback' => array( 'WC_Bookings_Tools', 'remove_in_cart_bookings' ),
			),
			'add_missing_attendance_status'  => array(
				'name'     => __( 'Add missing attendance status', 'woocommerce-bookings' ),
				'button'   => __( 'Schedule task', 'woocommerce-bookings' ),
				'desc'     => __( 'This tool will schedule a background task to fill in the Attendance status field on all existing bookings that don\'t have one. Completed bookings will be marked as Attended, cancelled bookings as Unattended, and all other statuses as Unattended.', 'woocommerce-bookings' ),
				'callback' => array( 'WC_Bookings_Tools', 'schedule_add_missing_attendance_status' ),
			),
		);

		return array_merge( $tools, $bookings_tools );
	}

	/**
	 * Get posted availability fields and format.
	 *
	 * @return array
	 */
	private function get_posted_availability() {
		$availability = array();
		$row_size     = isset( $_POST['wc_booking_availability_type'] ) ? sizeof( $_POST['wc_booking_availability_type'] ) : 0;
		for ( $i = 0; $i < $row_size; $i ++ ) {
			$availability[ $i ]['type']     = wc_clean( wp_unslash( $_POST['wc_booking_availability_type'][ $i ] ) );
			$availability[ $i ]['bookable'] = wc_clean( wp_unslash( $_POST['wc_booking_availability_bookable'][ $i ] ) );
			$availability[ $i ]['priority'] = intval( $_POST['wc_booking_availability_priority'][ $i ] );

			switch ( $availability[ $i ]['type'] ) {
				case 'custom':
					$availability[ $i ]['from'] = wc_clean( wp_unslash( $_POST['wc_booking_availability_from_date'][ $i ] ) );
					$availability[ $i ]['to']   = wc_clean( wp_unslash( $_POST['wc_booking_availability_to_date'][ $i ] ) );
					break;
				case 'months':
					$availability[ $i ]['from'] = wc_clean( wp_unslash( $_POST['wc_booking_availability_from_month'][ $i ] ) );
					$availability[ $i ]['to']   = wc_clean( wp_unslash( $_POST['wc_booking_availability_to_month'][ $i ] ) );
					break;
				case 'weeks':
					$availability[ $i ]['from'] = wc_clean( wp_unslash( $_POST['wc_booking_availability_from_week'][ $i ] ) );
					$availability[ $i ]['to']   = wc_clean( wp_unslash( $_POST['wc_booking_availability_to_week'][ $i ] ) );
					break;
				case 'days':
					$availability[ $i ]['from'] = wc_clean( wp_unslash( $_POST['wc_booking_availability_from_day_of_week'][ $i ] ) );
					$availability[ $i ]['to']   = wc_clean( wp_unslash( $_POST['wc_booking_availability_to_day_of_week'][ $i ] ) );
					break;
				case 'time':
				case 'time:1':
				case 'time:2':
				case 'time:3':
				case 'time:4':
				case 'time:5':
				case 'time:6':
				case 'time:7':
					$availability[ $i ]['from'] = wc_booking_sanitize_time( wp_unslash( $_POST['wc_booking_availability_from_time'][ $i ] ) );
					$availability[ $i ]['to']   = wc_booking_sanitize_time( wp_unslash( $_POST['wc_booking_availability_to_time'][ $i ] ) );
					break;
				case 'time:range':
				case 'custom:daterange':
					$availability[ $i ]['from']      = wc_booking_sanitize_time( wp_unslash( $_POST['wc_booking_availability_from_time'][ $i ] ) );
					$availability[ $i ]['to']        = wc_booking_sanitize_time( wp_unslash( $_POST['wc_booking_availability_to_time'][ $i ] ) );
					$availability[ $i ]['from_date'] = wc_clean( wp_unslash( $_POST['wc_booking_availability_from_date'][ $i ] ) );
					$availability[ $i ]['to_date']   = wc_clean( wp_unslash( $_POST['wc_booking_availability_to_date'][ $i ] ) );
					break;
			}
		}
		return $availability;
	}

	/**
	 * Get posted pricing fields and format.
	 *
	 * @return array
	 */
	private function get_posted_pricing() {
		$pricing = array();

		if ( empty( $_POST['wc_booking_pricing_type'] ) || ! is_array( $_POST['wc_booking_pricing_type'] ) ) {
			return $pricing;
		}

		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Array keys can not be unslashed/sanitized as it lead to errors in the foreach loop.
		foreach ( array_keys( $_POST['wc_booking_pricing_type'] ) as $i ) {
			$pricing[ $i ]['type']          = wc_clean( wp_unslash( $_POST['wc_booking_pricing_type'][ $i ] ) );
			$pricing[ $i ]['cost']          = wc_clean( wp_unslash( $_POST['wc_booking_pricing_cost'][ $i ] ) );
			$pricing[ $i ]['modifier']      = wc_clean( wp_unslash( $_POST['wc_booking_pricing_cost_modifier'][ $i ] ) );
			$pricing[ $i ]['base_cost']     = wc_clean( wp_unslash( $_POST['wc_booking_pricing_base_cost'][ $i ] ) );
			$pricing[ $i ]['base_modifier'] = wc_clean( wp_unslash( $_POST['wc_booking_pricing_base_cost_modifier'][ $i ] ) );

			switch ( $pricing[ $i ]['type'] ) {
				case 'custom':
					$pricing[ $i ]['from'] = wc_clean( wp_unslash( $_POST['wc_booking_pricing_from_date'][ $i ] ) );
					$pricing[ $i ]['to']   = wc_clean( wp_unslash( $_POST['wc_booking_pricing_to_date'][ $i ] ) );
					break;
				case 'months':
					$pricing[ $i ]['from'] = wc_clean( wp_unslash( $_POST['wc_booking_pricing_from_month'][ $i ] ) );
					$pricing[ $i ]['to']   = wc_clean( wp_unslash( $_POST['wc_booking_pricing_to_month'][ $i ] ) );
					break;
				case 'weeks':
					$pricing[ $i ]['from'] = wc_clean( wp_unslash( $_POST['wc_booking_pricing_from_week'][ $i ] ) );
					$pricing[ $i ]['to']   = wc_clean( wp_unslash( $_POST['wc_booking_pricing_to_week'][ $i ] ) );
					break;
				case 'days':
					$pricing[ $i ]['from'] = wc_clean( wp_unslash( $_POST['wc_booking_pricing_from_day_of_week'][ $i ] ) );
					$pricing[ $i ]['to']   = wc_clean( wp_unslash( $_POST['wc_booking_pricing_to_day_of_week'][ $i ] ) );
					break;
				case 'time':
				case 'time:1':
				case 'time:2':
				case 'time:3':
				case 'time:4':
				case 'time:5':
				case 'time:6':
				case 'time:7':
					$pricing[ $i ]['from'] = wc_booking_sanitize_time( wp_unslash( $_POST['wc_booking_pricing_from_time'][ $i ] ) );
					$pricing[ $i ]['to']   = wc_booking_sanitize_time( wp_unslash( $_POST['wc_booking_pricing_to_time'][ $i ] ) );
					break;
				case 'time:range':
					$pricing[ $i ]['from'] = wc_booking_sanitize_time( wp_unslash( $_POST['wc_booking_pricing_from_time'][ $i ] ) );
					$pricing[ $i ]['to']   = wc_booking_sanitize_time( wp_unslash( $_POST['wc_booking_pricing_to_time'][ $i ] ) );

					$pricing[ $i ]['from_date'] = wc_clean( wp_unslash( $_POST['wc_booking_pricing_from_date'][ $i ] ) );
					$pricing[ $i ]['to_date']   = wc_clean( wp_unslash( $_POST['wc_booking_pricing_to_date'][ $i ] ) );
					break;
				default:
					$pricing[ $i ]['from'] = wc_clean( wp_unslash( $_POST['wc_booking_pricing_from'][ $i ] ) );
					$pricing[ $i ]['to']   = wc_clean( wp_unslash( $_POST['wc_booking_pricing_to'][ $i ] ) );
					break;
			}
		}
		return array_values( $pricing );
	}

	/**
	 * Get posted person types.
	 *
	 * @return array
	 */
	private function get_posted_person_types( $product ) {
		$person_types = array();

		if ( isset( $_POST['person_id'] ) && isset( $_POST['_wc_booking_has_persons'] ) ) {
			$person_ids         = array_map( 'absint', $_POST['person_id'] );
			$person_menu_order  = array_map( 'absint', $_POST['person_menu_order'] );
			$person_name        = wc_clean( wp_unslash( $_POST['person_name'] ) );
			$person_cost        = wc_clean( wp_unslash( $_POST['person_cost'] ) );
			$person_block_cost  = wc_clean( wp_unslash( $_POST['person_block_cost'] ) );
			$person_description = wc_clean( wp_unslash( $_POST['person_description'] ) );
			$person_min         = wc_clean( wp_unslash( $_POST['person_min'] ) );
			$person_max         = wc_clean( wp_unslash( $_POST['person_max'] ) );
			$max_loop           = max( array_keys( $person_ids ) );

			for ( $i = 0; $i <= $max_loop; $i ++ ) {
				if ( ! isset( $person_ids[ $i ] ) ) {
					continue;
				}
				$person_id   = $person_ids[ $i ];
				$person_type = new WC_Product_Booking_Person_Type( $person_id );
				$person_type->set_props(
					array(
						'name'        => $person_name[ $i ],
						'description' => $person_description[ $i ],
						'sort_order'  => $person_menu_order[ $i ],
						'cost'        => $person_cost[ $i ],
						'block_cost'  => $person_block_cost[ $i ],
						'min'         => $person_min[ $i ],
						'max'         => $person_max[ $i ],
						'parent_id'   => $product->get_id(),
					)
				);
				$person_types[] = $person_type;
			}
		}
		return $person_types;
	}

	/**
	 * Get posted resources. Resources are global, but booking products store information about the relationship.
	 *
	 * @return array
	 */
	private function get_posted_resources( $product ) {
		$resources = array();

		if ( isset( $_POST['resource_id'] ) && isset( $_POST['_wc_booking_has_resources'] ) ) {
			$resource_ids         = array_map( 'absint', $_POST['resource_id'] );
			$resource_menu_order  = array_map( 'absint', $_POST['resource_menu_order'] );
			$resource_base_cost   = wc_clean( wp_unslash( $_POST['resource_cost'] ) );
			$resource_block_cost  = wc_clean( wp_unslash( $_POST['resource_block_cost'] ) );

			foreach ( array_keys( $resource_menu_order ) as $key ) {
				$resources[ absint( $resource_ids[ $key ] ) ] = array(
					'base_cost'  => $resource_base_cost[ $key ],
					'block_cost' => $resource_block_cost[ $key ],
				);
			}
		}

		return $resources;
	}

	/**
	 * Set data in 3.0.x
	 *
	 * @version  3.6.0
	 * @param    WC_Product $product
	 */
	public function set_props( $product ) {
		// Only set props if the product is a bookable product.
		if ( ! is_a( $product, 'WC_Product_Booking' ) ) {
			return;
		}

		// Limit hourly duration up to 24.
		$default_date_availability = filter_input( INPUT_POST, '_wc_booking_default_date_availability', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$duration_unit             = filter_input( INPUT_POST, '_wc_booking_duration_unit', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
		$duration                  = filter_input( INPUT_POST, '_wc_booking_duration', FILTER_SANITIZE_NUMBER_INT );
		$duration                  = 'hour' === $duration_unit && 24 < $duration && 'non-available' === $default_date_availability ? 24 : $duration;

		$resources = $this->get_posted_resources( $product );
		$product->set_props(
			array(
				'apply_adjacent_buffer'      => isset( $_POST['_wc_booking_apply_adjacent_buffer'] ),
				'availability'               => $this->get_posted_availability(),
				'block_cost'                 => wc_clean( wp_unslash( $_POST['_wc_booking_block_cost'] ) ),
				'buffer_period'              => wc_clean( wp_unslash( $_POST['_wc_booking_buffer_period'] ) ),
				'calendar_display_mode'      => wc_clean( wp_unslash( $_POST['_wc_booking_calendar_display_mode'] ) ),
				'cancel_limit_unit'          => wc_clean( wp_unslash( $_POST['_wc_booking_cancel_limit_unit'] ) ),
				'cancel_limit'               => wc_clean( wp_unslash( $_POST['_wc_booking_cancel_limit'] ) ),
				'check_start_block_only'     => 'start' === $_POST['_wc_booking_check_availability_against'],
				'cost'                       => wc_clean( wp_unslash( $_POST['_wc_booking_cost'] ) ),
				'default_date_availability'  => wc_clean( $default_date_availability ),
				'display_cost'               => wc_clean( wp_unslash( $_POST['_wc_display_cost'] ) ),
				'duration_type'              => wc_clean( wp_unslash( $_POST['_wc_booking_duration_type'] ) ),
				'duration_unit'              => wc_clean( $duration_unit ),
				'duration'                   => wc_clean( $duration ),
				'enable_range_picker'        => isset( $_POST['_wc_booking_enable_range_picker'] ),
				'first_block_time'           => wc_clean( wp_unslash( $_POST['_wc_booking_first_block_time'] ) ),
				'has_person_cost_multiplier' => isset( $_POST['_wc_booking_person_cost_multiplier'] ),
				'has_person_qty_multiplier'  => isset( $_POST['_wc_booking_person_qty_multiplier'] ),
				'has_person_types'           => isset( $_POST['_wc_booking_has_person_types'] ),
				'has_persons'                => isset( $_POST['_wc_booking_has_persons'] ),
				'has_resources'              => isset( $_POST['_wc_booking_has_resources'] ),
				'has_restricted_days'        => isset( $_POST['_wc_booking_has_restricted_days'] ) && ! empty( $_POST['_wc_booking_restricted_days'] ), // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified by WooCommerce before product meta save.
				'max_date_unit'              => wc_clean( wp_unslash( $_POST['_wc_booking_max_date_unit'] ) ),
				'max_date_value'             => wc_clean( wp_unslash( $_POST['_wc_booking_max_date'] ) ),
				'max_duration'               => wc_clean( wp_unslash( $_POST['_wc_booking_max_duration'] ) ),
				'max_persons'                => wc_clean( wp_unslash( $_POST['_wc_booking_max_persons_group'] ) ),
				'min_date_unit'              => wc_clean( wp_unslash( $_POST['_wc_booking_min_date_unit'] ) ),
				'min_date_value'             => wc_clean( wp_unslash( $_POST['_wc_booking_min_date'] ) ),
				'min_duration'               => wc_clean( wp_unslash( $_POST['_wc_booking_min_duration'] ) ),
				'min_persons'                => wc_clean( wp_unslash( $_POST['_wc_booking_min_persons_group'] ) ),
				'person_types'               => $this->get_posted_person_types( $product ),
				'pricing'                    => $this->get_posted_pricing(),
				'qty'                        => wc_clean( wp_unslash( $_POST['_wc_booking_qty'] ) ),
				'requires_confirmation'      => isset( $_POST['_wc_booking_requires_confirmation'] ),
				'resource_label'             => wc_clean( wp_unslash( $_POST['_wc_booking_resource_label'] ) ),
				'resource_base_costs'        => wp_list_pluck( $resources, 'base_cost' ),
				'resource_block_costs'       => wp_list_pluck( $resources, 'block_cost' ),
				'resource_ids'               => array_keys( $resources ),
				'resources_assignment'       => wc_clean( wp_unslash( $_POST['_wc_booking_resources_assignment'] ) ),
				'restricted_days'            => isset( $_POST['_wc_booking_restricted_days'] ) ? wc_clean( wp_unslash( $_POST['_wc_booking_restricted_days'] ) ) : '',
				'user_can_cancel'            => isset( $_POST['_wc_booking_user_can_cancel'] ),
			)
		);
	}

	/**
	 * Init product edit tabs.
	 */
	public function init_tabs() {
		add_filter( 'woocommerce_product_data_tabs', array( $this, 'register_tab' ), 9 );
		add_action( 'woocommerce_product_data_panels', array( $this, 'booking_panels' ) );
	}

	/**
	 * Add tabs to WC 2.6+
	 *
	 * @param  array $tabs
	 * @return array
	 */
	public function register_tab( $tabs ) {
		global $post;

		// Check if the post status is not 'publish'
		if ( $post->post_status !== 'publish' ) {
			$tabs['bookings_templates']    = array(
				'label'  => __( 'Templates', 'woocommerce-bookings' ),
				'target' => 'bookings_templates',
				'class'  => array(
					'show_if_booking',
				),
				'priority' => 1,
			);
		}

		$tabs['bookings_resources']    = array(
			'label'  => __( 'Resources', 'woocommerce-bookings' ),
			'target' => 'bookings_resources',
			'class'  => array(
				'show_if_booking',
			),
		);
		$tabs['bookings_availability'] = array(
			'label'  => __( 'Availability', 'woocommerce-bookings' ),
			'target' => 'bookings_availability',
			'class'  => array(
				'show_if_booking',
			),
		);
		$tabs['bookings_pricing']      = array(
			'label'  => __( 'Costs', 'woocommerce-bookings' ),
			'target' => 'bookings_pricing',
			'class'  => array(
				'show_if_booking',
			),
		);
		$tabs['bookings_persons']      = array(
			'label'  => __( 'Persons', 'woocommerce-bookings' ),
			'target' => 'bookings_persons',
			'class'  => array(
				'show_if_booking',
			),
		);
		$tabs['bookings_export']       = array(
			'label'  => __( 'Export', 'woocommerce-bookings' ),
			'target' => 'bookings_export',
			'class'  => array(
				'show_if_booking',
			),
		);
		return $tabs;
	}

	/**
	 * Public access to instance object
	 *
	 * @return object
	 */
	public static function get_instance() {
		return self::$_this;
	}

	/**
	 * Duplicate a post.
	 *
	 * @param  int     $new_post_id Duplicated product ID.
	 * @param  WP_Post $post        Original product post.
	 */
	public function woocommerce_duplicate_product_pre_wc_30( $new_post_id, $post ) {
		$product = wc_get_product( $post->ID );

		if ( is_wc_booking_product( $product ) ) {
			global $wpdb;
			// Duplicate relationships
			$relationships = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}wc_booking_relationships WHERE product_id = %d;", $post->ID ), ARRAY_A );
			foreach ( $relationships as $relationship ) {
				$relationship['product_id'] = $new_post_id;
				unset( $relationship['ID'] );
				$wpdb->insert( "{$wpdb->prefix}wc_booking_relationships", $relationship );
			}

			// Clone and re-save person types.
			foreach ( $product->get_person_types() as $person_type ) {
				$dupe_person_type = clone $person_type;
				$dupe_person_type->set_id( 0 );
				$dupe_person_type->set_parent_id( $new_post_id );
				$dupe_person_type->save();
			}
		}
	}

	/**
	 * Duplicate a post.
	 *
	 * @param  WC_Product $new_product Duplicated product.
	 * @param  WC_Product $product     Original product.
	 */
	public function woocommerce_duplicate_product( $new_product, $product ) {
		if ( is_wc_booking_product( $product ) ) {
			// Clone and re-save person types.
			foreach ( $product->get_person_types() as $person_type ) {
				$dupe_person_type = clone $person_type;
				$dupe_person_type->set_id( 0 );
				$dupe_person_type->set_parent_id( $new_product->get_id() );
				$dupe_person_type->save();
			}
		}
	}

	/**
	 * Change messages when a post type is updated.
	 *
	 * @param  array $messages
	 * @return array
	 */
	public function post_updated_messages( $messages ) {
		$messages['wc_booking'] = array(
			0  => '', // Unused. Messages start at index 1.
			1  => __( 'Booking updated.', 'woocommerce-bookings' ),
			2  => __( 'Custom field updated.', 'woocommerce-bookings' ),
			3  => __( 'Custom field deleted.', 'woocommerce-bookings' ),
			4  => __( 'Booking updated.', 'woocommerce-bookings' ),
			5  => '',
			6  => __( 'Booking updated.', 'woocommerce-bookings' ),
			7  => __( 'Booking saved.', 'woocommerce-bookings' ),
			8  => __( 'Booking submitted.', 'woocommerce-bookings' ),
			9  => '',
			10 => '',
		);
		return $messages;
	}

	/**
	 * Show booking data if a line item is linked to a booking ID.
	 */
	public function booking_display( $item_id, $item, $product ) {
		$booking_ids = WC_Booking_Data_Store::get_booking_ids_from_order_item_id( $item_id );

		wc_get_template(
			'order/admin/booking-display.php',
			array(
				'booking_ids'       => $booking_ids,
				'hide_item_details' => true,
			),
			'woocommerce-bookings',
			WC_BOOKINGS_TEMPLATE_PATH
		);
	}

	/**
	 * Include CPT handlers
	 */
	public function include_post_type_handlers() {
		new WC_Bookings_CPT();
		new WC_Bookable_Resource_CPT();
	}

	/**
	 * Include meta box handlers
	 */
	public function include_meta_box_handlers() {
		new WC_Bookings_Meta_Boxes();
	}

	/**
	 * Redirect the default add booking url to the custom one
	 */
	public function redirect_new_add_booking_url() {
		global $pagenow;

		if ( 'post-new.php' == $pagenow && isset( $_GET['post_type'] ) && 'wc_booking' == $_GET['post_type'] ) {
			wp_safe_redirect( admin_url( 'edit.php?post_type=wc_booking&page=create_booking' ), '301' );
		}
	}

	/**
	 * Get booking products.
	 *
	 * @return array
	 */
	public static function get_booking_products() {
		$ids               = WC_Data_Store::load( 'product-booking' )->get_bookable_product_ids();
		$bookable_products = array();

		foreach ( $ids as $id ) {
			$bookable_products[] = get_wc_product_booking( $id );
		}
		return $bookable_products;
	}

	/**
	 * Get booking product resources.
	 *
	 * @return array
	 */
	public static function get_booking_resources() {
		$ids       = WC_Data_Store::load( 'product-booking-resource' )->get_bookable_product_resource_ids();
		$resources = array();

		foreach ( $ids as $id ) {
			$resources[] = get_wc_product_booking_resource( $id );
		}
		return $resources;
	}

	/**
	 * Tweak product type options
	 *
	 * @param  array $options
	 * @return array
	 */
	public function product_type_options( $options ) {
		$options['virtual']['wrapper_class'] .= ' show_if_booking';
		return $options;
	}

	/**
	 * Show the booking tab
	 */
	public function add_tab() {
		include 'views/html-booking-tab.php';
	}

	/**
	 * Show the booking data view
	 */
	public function booking_data() {
		global $post, $bookable_product;

		if ( empty( $bookable_product ) || $bookable_product->get_id() !== $post->ID ) {
			$bookable_product = get_wc_product_booking( $post->ID );
		}

		include 'views/html-booking-data.php';
	}

	/**
	 * Show the booking panels views
	 */
	public function booking_panels() {
		global $post, $bookable_product;

		if ( empty( $bookable_product ) || $bookable_product->get_id() !== $post->ID ) {
			$bookable_product = get_wc_product_booking( $post->ID );
		}

		$restricted_meta = $bookable_product->get_restricted_days();

		for ( $i = 0; $i < 7; $i++ ) {

			if ( $restricted_meta && in_array( $i, $restricted_meta ) ) {
				$restricted_days[ $i ] = $i;
			} else {
				$restricted_days[ $i ] = false;
			}
		}

		wp_enqueue_script( 'wc_bookings_admin_js' );

		include 'views/html-booking-templates.php';
		include 'views/html-booking-resources.php';
		include 'views/html-booking-availability.php';
		include 'views/html-booking-pricing.php';
		include 'views/html-booking-persons.php';
		include 'views/html-booking-export.php';
	}

	/**
	 * Add admin styles
	 */
	public function styles_and_scripts() {
		global $post;

		$screen    = get_current_screen();
		$screen_id = $screen ? $screen->id : '';
		// Don't enqueue styles and JS on non-WC screens.
		if ( ! in_array( $screen_id, wc_get_screen_ids(), true ) ) {
			return;
		}

		wp_enqueue_style( 'wc_bookings_admin_styles', WC_BOOKINGS_PLUGIN_URL . '/dist/admin.css', array( 'wp-components' ), WC_BOOKINGS_VERSION );
		$admin_script_dependencies = wc_booking_get_script_dependencies( 'admin', array( 'jquery-ui-datepicker', 'jquery-ui-sortable' ) );
		wp_register_script( 'wc_bookings_admin_js', WC_BOOKINGS_PLUGIN_URL . '/dist/admin.js', $admin_script_dependencies, WC_BOOKINGS_VERSION, true );

		if ( 'wc_booking_page_create_booking' === $screen->id ) {
			wp_enqueue_script( 'wc-bookings-date' );
			wp_register_script( 'wc_bookings_admin_time_picker_js', WC_BOOKINGS_PLUGIN_URL . '/dist/admin-time-picker.js', wc_booking_get_script_dependencies( 'admin-time-picker' ), WC_BOOKINGS_VERSION, true );
		}

		if ( 'wc_booking_page_booking_calendar' === $screen->id ) {
			if ( WC_BOOKINGS_GUTENBERG_EXISTS ) {
				$admin_calendar_gutenberg_deps = wc_booking_get_script_dependencies( 'admin-calendar-gutenberg', array( 'wc_bookings_admin_js' ) );
				wp_register_script( 'wc_bookings_admin_calendar_gutenberg_js', WC_BOOKINGS_PLUGIN_URL . '/dist/admin-calendar-gutenberg.js', $admin_calendar_gutenberg_deps, WC_BOOKINGS_VERSION, true );
				wp_enqueue_style( 'wc_bookings_admin_calendar_css', WC_BOOKINGS_PLUGIN_URL . '/dist/admin-calendar-gutenberg.css', null, WC_BOOKINGS_VERSION );
			}
			wp_register_script( 'wc_bookings_admin_calendar_js', WC_BOOKINGS_PLUGIN_URL . '/dist/admin-calendar.js', wc_booking_get_script_dependencies( 'admin-calendar' ), WC_BOOKINGS_VERSION, true );
		}
		if ( 'wc_booking_page_wc_bookings_settings' === $screen->id ) {
			if ( WC_BOOKINGS_GUTENBERG_EXISTS ) {
				$admin_store_availability_deps = wc_booking_get_script_dependencies( 'admin-store-availability.js', array( 'wc_bookings_admin_js' ) );
				wp_register_script( 'wc_bookings_admin_store_availability_js', WC_BOOKINGS_PLUGIN_URL . '/dist/admin-store-availability.js', $admin_store_availability_deps, WC_BOOKINGS_VERSION, true );
				wp_enqueue_style( 'wc_bookings_admin_store_availability_css', WC_BOOKINGS_PLUGIN_URL . '/dist/admin-store-availability.css', null, WC_BOOKINGS_VERSION );
			}
		}

		if ( 'wc_booking' === $screen->id ) {
			wp_enqueue_script( 'wc_bookings_admin_edit_booking_js', WC_BOOKINGS_PLUGIN_URL . '/dist/admin-edit-booking.js', wc_booking_get_script_dependencies( 'admin-edit-booking' ), WC_BOOKINGS_VERSION, true );
		}

		if ( 'product' === $screen->id ) {
			wp_enqueue_script( 'wc_bookings_admin_edit_bookable_product_js', WC_BOOKINGS_PLUGIN_URL . '/dist/admin-edit-bookable-product.js', wc_booking_get_script_dependencies( 'admin-edit-bookable-product' ), WC_BOOKINGS_VERSION, true );
		}

		$params = array(
			'i18n_remove_person'            => esc_js( __( 'Are you sure you want to remove this person type?', 'woocommerce-bookings' ) ),
			'nonce_unlink_person'           => wp_create_nonce( 'unlink-bookable-person' ),
			'nonce_add_person'              => wp_create_nonce( 'add-bookable-person' ),
			'i18n_remove_resource'          => esc_js( __( 'Are you sure you want to remove this resource?', 'woocommerce-bookings' ) ),
			'nonce_delete_resource'         => wp_create_nonce( 'delete-bookable-resource' ),
			'nonce_add_resource'            => wp_create_nonce( 'add-bookable-resource' ),
			'i18n_minutes'                  => esc_js( __( 'minutes', 'woocommerce-bookings' ) ),
			'i18n_hours'                    => esc_js( __( 'hours', 'woocommerce-bookings' ) ),
			'i18n_days'                     => esc_js( __( 'days', 'woocommerce-bookings' ) ),
			'i18n_new_resource_name'        => esc_js( __( 'Enter a name for the new resource', 'woocommerce-bookings' ) ),
			'post'                          => isset( $post->ID ) ? $post->ID : '',
			'plugin_url'                    => WC()->plugin_url(),
			'ajax_url'                      => admin_url( 'admin-ajax.php' ),
			'nonce'                         => array(
				'wc_bookings_get_product_template' => wp_create_nonce( 'wc_bookings_get_product_template' ),
			),
			'i18n_template_error'           => esc_js( __( 'Something went wrong, please try again.', 'woocommerce-bookings' ) ),
			'calendar_image'                => WC_BOOKINGS_PLUGIN_URL . '/dist/images/calendar.png',
			'i18n_view_details'             => esc_js( __( 'View details', 'woocommerce-bookings' ) ),
			'i18n_customer'                 => esc_js( __( 'Customer', 'woocommerce-bookings' ) ),
			'i18n_resource'                 => esc_js( __( 'Resource', 'woocommerce-bookings' ) ),
			'i18n_persons'                  => esc_js( __( 'Persons', 'woocommerce-bookings' ) ),
			'i18n_max_booking_overwridden'  => esc_js( __( 'This setting is being overridden at the resource level.', 'woocommerce-bookings' ) ),
			'i18n_max_persons_warning'      => esc_js( __( 'Max allowed is set to', 'woocommerce-bookings' ) ),
			'i18n_limited_hours'            => esc_js( __( 'A duration greater than 24 hours is not allowed when Availability is "not-available by default".', 'woocommerce-bookings' ) ),
			'i18n_limited_hours_in_gen_tab' => esc_js( __( 'The booking duration has been set to 24 as a duration greater than 24 hours is not allowed when Availability is "not-available by default".', 'woocommerce-bookings' ) ),
			'bookings_version'              => WC_BOOKINGS_VERSION,
			'bookings_db_version'           => WC_BOOKINGS_DB_VERSION,
			'start_of_week'                 => get_option( 'start_of_week' ),
			'time_in_12hours'               => ! preg_match( '/(?<!\\\\)(\\\\{2})*(H|G)/', get_option( 'time_format' ) ),
		);

		wp_localize_script( 'wc_bookings_admin_js', 'wc_bookings_admin_js_params', $params );

		$params = array(
			'nonce_add_store_availability_rule'     => wp_create_nonce( 'add-store-availability-rule' ),
			'nonce_get_store_availability_rules'    => wp_create_nonce( 'get-store-availability-rules' ),
			'nonce_update_store_availability_rule'  => wp_create_nonce( 'update-store-availability-rule' ),
			'nonce_delete_store_availability_rules' => wp_create_nonce( 'delete-store-availability-rules' ),
			'ajax_url'                              => WC()->ajax_url(),
		);

		wp_localize_script( 'wc_bookings_admin_store_availability_js', 'wc_bookings_admin_store_availability_js_params', $params );

		$params = array(
			'invalid_start_end_date' => __( '"Start and end date" should be of the format yyyy-mm-dd and cannot be empty.', 'woocommerce-bookings' ),
			'date_range_invalid'     => __( 'Start date cannot be greater than end date.', 'woocommerce-bookings' ),
		);

		wp_localize_script( 'wc_bookings_admin_edit_booking_js', 'wc_bookings_admin_edit_booking_params', $params );

		$params = array(
			'wc_bookings_invalid_min_duration' => esc_html__( 'Minimum duration needs to be less than or equal to maximum duration.', 'woocommerce-bookings' ),
			'wc_bookings_invalid_max_duration' => esc_html__( 'Maximum duration needs to be greater than or equal to the minimum duration.', 'woocommerce-bookings' ),
		);

		wp_localize_script( 'wc_bookings_admin_edit_bookable_product_js', 'wc_bookings_admin_edit_booking_params', $params );
	}

	/**
	 * Add extra product type options
	 *
	 * @param  array $options
	 * @return array
	 */
	public function booking_product_type_options( $options ) {
		return array_merge(
			$options,
			array(
				'wc_booking_has_persons'   => array(
					'id'            => '_wc_booking_has_persons',
					'wrapper_class' => 'show_if_booking',
					'label'         => __( 'Has persons', 'woocommerce-bookings' ),
					'description'   => __( 'Enable this if this bookable product can be booked by a customer defined number of persons.', 'woocommerce-bookings' ),
					'default'       => 'no',
				),
				'wc_booking_has_resources' => array(
					'id'            => '_wc_booking_has_resources',
					'wrapper_class' => 'show_if_booking',
					'label'         => __( 'Has resources', 'woocommerce-bookings' ),
					'description'   => __( 'Enable this if this bookable product has multiple bookable resources, for example room types or instructors.', 'woocommerce-bookings' ),
					'default'       => 'no',
				),
			)
		);
	}

	/**
	 * Reset the ICS exporter timezone string cache.
	 *
	 * Kept for backwards compatibility with third-party integrations
	 * that may call this public method directly.
	 *
	 * @return void
	 */
	public function reset_ics_exporter_timezone_cache() {
		wc_booking_clear_timezone_string_cache();
	}

	/**
	 * Override product type for New Product screen, if a request parameter is set.
	 *
	 * @param string $override Product Type
	 * @param int    $product_id
	 *
	 * @return string
	 */
	public function maybe_override_product_type( $override, $product_id ) {
		if ( ! empty( $_REQUEST['bookable_product'] ) ) {
			return 'booking';
		}

		return $override;
	}

	/**
	 * Perform clean up when a bookable product is deleted.
	 *
	 * @since 1.14.0
	 * @param int $post_id The post ID.
	 */
	public function handle_deleted_bookable_product( $post_id ) {
		$product = wc_get_product( absint( $post_id ) );

		if ( ! is_a( $product, 'WC_Product' ) || ! is_wc_booking_product( $product ) ) {
			return;
		}

		WC_Bookings_Tools::unlink_resource( $post_id );
	}

	/**
	 * Modifies query to retrieve Private products on the
	 * Create Booking, Edit Booking and Send Notification
	 * admin pages.
	 *
	 * @param array $args Query args for products
	 * @see https://github.com/woocommerce/woocommerce-bookings/issues/3090
	 * @return array
	 */
	public function modify_booking_products_query( $args ) {
		$screen = get_current_screen();

		if ( is_null( $screen ) ) {
			return $args;
		}

		$is_booking_create_page    = 'wc_booking_page_create_booking' === $screen->id;
		$is_send_notification_page = 'wc_booking_page_booking_notification' === $screen->id;
		$is_booking_edit_page      = 'wc_booking' === $screen->post_type && 'edit' === $screen->parent_base;

		if ( $is_booking_create_page || $is_send_notification_page || $is_booking_edit_page ) {
			return array_merge(
				$args,
				array(
					'post_status' => array( 'publish', 'private' ),
				)
			);
		}

		return $args;
	}
}
