<?php

/**
 * Sets up and manages subscription gifting functionality.
 */
class WCS_Gifting {
	/**
	 * Plugin's current version.
	 *
	 * @var string
	 */
	public static $version = '2.9.0'; // WRCS: DEFINED_VERSION.

	/**
	 * Minimum WooCommerce version required.
	 *
	 * @var string
	 */
	public static $wc_minimum_supported_version = '3.0';

	/**
	 * Minimum WooCommerce Subscription version required.
	 *
	 * @var string
	 */
	public static $wcs_minimum_supported_version = '2.2';

	/**
	 * Minimum WooCommerce Memberships version required for integration.
	 *
	 * @var string
	 */
	public static $wcm_minimum_supported_version = '1.4';

	/**
	 * Setup hooks & filters, when the class is initialised.
	 */
	public static function init() {
		add_action( 'wp_enqueue_scripts', __CLASS__ . '::gifting_scripts' );

		// Needs to run after Subscriptions has loaded its dependant classes.
		self::load_dependant_classes();

		add_action( 'woocommerce_subscription_before_actions', __CLASS__ . '::add_billing_period_table_row' );

		add_filter( 'woocommerce_get_formatted_subscription_total', __CLASS__ . '::get_formatted_recipient_total', 10, 2 );

		if ( ! class_exists( 'WC_Subscriptions_Data_Copier' ) ) {
			add_filter( 'wcs_renewal_order_meta_query', __CLASS__ . '::remove_renewal_order_meta_query', 11 );
		} else {
			add_filter( 'wc_subscriptions_renewal_order_data', __CLASS__ . '::remove_renewal_order_meta', 11 );
		}

		// Handle "_is_gifted_subscription" argument in wc_get_orders().
		add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', array( __CLASS__, 'handle_is_gifted_subscription_query_var' ), 10, 2 );

		add_action( 'woocommerce_blocks_loaded', __CLASS__ . '::setup_blocks_integration' );
	}

	/**
	 * Don't carry the _recipient_user meta data to renewal orders.
	 *
	 * @param array $order_meta Renewal order meta.
	 *
	 * @return array
	 */
	public static function remove_renewal_order_meta( $order_meta ) {
		unset( $order_meta['_recipient_user'] );
		return $order_meta;
	}

	/**
	 * Don't carry recipient meta data to renewal orders.
	 *
	 * @param string $order_meta_query Renewal order meta-query.
	 */
	public static function remove_renewal_order_meta_query( $order_meta_query ) {
		$order_meta_query .= " AND `meta_key` NOT IN ('_recipient_user')";
		return $order_meta_query;
	}

	/**
	 * Loads classes after plugins for classes dependant on other plugin files.
	 */
	public static function load_dependant_classes() {
		require_once 'class-wcsg-query.php';

		if ( function_exists( 'wc_memberships' ) ) {
			if ( version_compare( get_option( 'wc_memberships_version' ), self::$wcm_minimum_supported_version, '>=' ) ) {
				require_once 'class-wcsg-memberships-integration.php';
			} else {
				add_action( 'admin_notices', 'WCS_Gifting::plugin_dependency_notices' );
			}
		}
	}

	/**
	 * Register/queue frontend scripts.
	 */
	public static function gifting_scripts() {
		global $post;

		if ( ! WCSG_Admin::is_gifting_enabled() ) {
			return;
		}

		// We load this on all pages because of the mini-cart related styles.
		wp_enqueue_style(
			'woocommerce_subscriptions_gifting',
			plugins_url( '/assets/css/gifting/shortcode-checkout.css', WC_Subscriptions::$plugin_file ),
			array( 'wp-components' ),
			WC_VERSION,
			'all'
		);

		if ( ! is_cart() && ! is_checkout() && ! is_product() ) {
			return;
		}

		// We don't need to load the scripts on blocks pages.
		if ( WC_Blocks_Utils::has_block_in_page( $post->ID, 'woocommerce/cart' ) || WC_Blocks_Utils::has_block_in_page( $post->ID, 'woocommerce/checkout' ) ) {
			return;
		}

		// We don't need to load the script on product pages that are not giftable.
		if ( is_product() && ! WCSG_Product::is_giftable( $post->ID ) ) {
			return;
		}

		wp_register_script( 'woocommerce_subscriptions_gifting', plugins_url( '/assets/js/gifting/wcs-gifting.js', WC_Subscriptions::$plugin_file ), array( 'jquery' ), WC_Subscriptions::$version, true );
		wp_enqueue_script( 'woocommerce_subscriptions_gifting' );
	}

	/**
	 * Determines if an email address belongs to the current user.
	 *
	 * @param string $email Email address.
	 * @return bool Returns whether the email address belongs to the current user.
	 */
	public static function email_belongs_to_current_user( $email ) {
		$emails_to_try = array();

		if ( is_user_logged_in() ) {
			/** @var WP_User $current_user */
			$current_user    = wp_get_current_user();
			$emails_to_try[] = $current_user->user_email;
		}

		if ( is_checkout() && ! empty( $_POST['billing_email'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.CSRF.NonceVerification.NoNonceVerification
			$emails_to_try[] = sanitize_email( wp_unslash( $_POST['billing_email'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.CSRF.NonceVerification.NoNonceVerification
		}

		return in_array( $email, $emails_to_try, true );
	}

	/**
	 * Validates an array of recipient emails scheduling error notices if an error is found.
	 *
	 * @param array $recipients An array of recipient email addresses.
	 * @return bool Returns whether any errors have occurred.
	 */
	public static function validate_recipient_emails( $recipients ) {
		$invalid_email_found = false;
		$self_gifting_found  = false;

		if ( is_array( $recipients ) ) {
			foreach ( $recipients as $key => $recipient ) {
				$cleaned_recipient = sanitize_email( $recipient );
				if ( $recipient === $cleaned_recipient && is_email( $cleaned_recipient ) ) {
					if ( ! $self_gifting_found && self::email_belongs_to_current_user( $cleaned_recipient ) ) {
						wc_add_notice( __( 'Please enter someone else\'s email address.', 'woocommerce-subscriptions' ), 'error' );
						$self_gifting_found = true;
					}
				} elseif ( ! empty( $recipient ) && ! $invalid_email_found ) {
					wc_add_notice( __( ' Invalid email address.', 'woocommerce-subscriptions' ), 'error' );
					$invalid_email_found = true;
				}
			}
		}
		return ! ( $invalid_email_found || $self_gifting_found );
	}

	/**
	 * Attaches recipient information to a subscription cart item.
	 *
	 * @param object $item The item in the cart to be updated.
	 * @param string $key  Cart item key.
	 * @param array  $new_recipient_data The new recipient information for the item.
	 */
	public static function update_cart_item_recipient( $item, $key, $new_recipient_data ) {
		if ( empty( $item['wcsg_gift_recipients_email'] ) || $item['wcsg_gift_recipients_email'] !== $new_recipient_data ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
			WC()->cart->cart_contents[ $key ]['wcsg_gift_recipients_email'] = $new_recipient_data;
		}
	}

	/**
	 * Populates the cart item data that will be used by WooCommerce to generate a unique ID for the cart item. That is to
	 * avoid merging different products when they aren't the same. Previously the resubscribe status was ignored.
	 *
	 * @param array  $item               A cart item with all its data.
	 * @param string $key                A cart item key.
	 * @param array  $new_recipient_data Email address of the new recipient.
	 * @return array New cart item data.
	 */
	private static function add_cart_item_data( $item, $key, $new_recipient_data ) {
		// start with a clean slate.
		$cart_item_data = array();

		// Add the recipient email.
		if ( ! empty( $new_recipient_data ) ) {
			$cart_item_data = array( 'wcsg_gift_recipients_email' => $new_recipient_data );
		}

		// Add resubscribe data.
		if ( array_key_exists( 'subscription_resubscribe', $item ) ) {
			$cart_item_data = array_merge( $cart_item_data, array( 'subscription_resubscribe' => $item['subscription_resubscribe'] ) );
		}

		$cart_item_data = apply_filters( 'wcsg_cart_item_data', $cart_item_data, $item, $key, $new_recipient_data );

		return $cart_item_data;
	}

	/**
	 * Checks on each admin page load if Gifting plugin is activated.
	 *
	 * Apparently the official WP API is "lame" and it's far better to use an upgrade routine fired on admin_init: https://core.trac.wordpress.org/ticket/14170#comment:68
	 *
	 * @deprecated This is a hangover from the time when Subscriptions Gifting was a separate plugin.
	 * @since 7.8.0 - Originally implemented in WooCommerce Subscriptions Gifting 1.1.
	 */
	public static function maybe_activate() {
		wcs_deprecated_function( __METHOD__, '7.8.0' );
	}

	/**
	 * Called when the plugin is deactivated. Deletes the is active flag and fires an action.
	 *
	 * @deprecated This is a hangover from the time when Subscriptions Gifting was a separate plugin.
	 * @since 7.8.0 - Originally implemented in WooCommerce Subscriptions Gifting 2.0.0.
	 */
	public static function deactivate() {
		wcs_deprecated_function( __METHOD__, '7.8.0' );
	}

	/**
	 * Renders the add recipient fields (including the checkbox and e-mail input).
	 *
	 * @param string $email           E-mail address.
	 * @param string $id              ID, for uniqueness on page.
	 * @param string $print_or_return Wether to print or return the HTML content. Optional. Default behaviour is to print the string. Pass 'return' to return the HTML content instead.
	 * @param bool   $hidden          Whether the container should be initially hidden (display:none). Used for products with subscription plans where the gifting UI is shown/hidden by JS based on plan selection.
	 * @return string Returns the HTML string if $print_or_return is set to 'return', otherwise prints the HTML and nothing is returned.
	 * @since 7.8.0 - Originally implemented in WooCommerce Subscriptions Gifting 2.1.
	 */
	public static function render_add_recipient_fields( $email = '', $id = '', $print_or_return = 'print', $hidden = false ) {
		$args           = self::get_add_recipient_template_args( $email, $id );
		$args['hidden'] = $hidden;

		$output = wc_get_template_html(
			'html-add-recipient.php',
			$args,
			'',
			plugin_dir_path( WC_Subscriptions::$plugin_file ) . 'templates/gifting/'
		);

		if ( 'return' === $print_or_return ) {
			return $output;
		} else {
			echo wp_kses(
				$output,
				array(
					'fieldset' => array(),
					'input'    => array(
						'type'           => array(),
						'id'             => array(),
						'class'          => array(),
						'style'          => array(),
						'value'          => array(),
						'checked'        => array(),
						'disabled'       => array(),
						'data-recipient' => array(),
						'name'           => array(),
						'placeholder'    => array(),
						'aria-label'     => array(),
					),
					'label'    => array(
						'for' => array(),
					),
					'div'      => array(
						'class' => array(),
						'style' => array(),
					),
					'p'        => array(
						'class' => array(),
						'style' => array(),
						'id'    => array(),
					),
					'svg'      => array(
						'xmlns'       => array(),
						'viewbox'     => array(),
						'width'       => array(),
						'height'      => array(),
						'aria-hidden' => array(),
						'focusable'   => array(),
					),
					'path'     => array(
						'd' => array(),
					),
					'span'     => array(),
				)
			);
		}

		return '';
	}

	/**
	 * Build the set of arguments to be passed to the "Add Recipient" template.
	 *
	 * @param string $email E-mail address.
	 * @param string $id    ID, for CSS uniqueness on page.
	 * @since 7.8.0 - Originally implemented in WooCommerce Subscriptions Gifting 2.1.
	 */
	public static function get_add_recipient_template_args( $email = '', $id = '' ) {
		$id = $id ? esc_attr( $id ) : '0';

		// E-mail field.
		$email_field_args = array(
			'placeholder'      => __( 'Recipient\'s Email Address', 'woocommerce-subscriptions' ),
			'class'            => array( 'woocommerce_subscriptions_gifting_recipient_email' ),
			'style_attributes' => array(),
		);

		if ( ! empty( $email ) && ( self::email_belongs_to_current_user( $email ) || ! is_email( $email ) ) ) {
			array_push( $email_field_args['class'], 'woocommerce-invalid' );
		}

		// "This is a gift" checkbox.
		$checkbox_field_args = array(
			'class'            => apply_filters( 'wcsg_recipient_checkbox_class', array() ),
			'style_attributes' => apply_filters( 'wcsg_recipient_checkbox_style_attributes', array() ),
			'disabled'         => apply_filters( 'wcsg_recipient_checkbox_disabled', false ),
			'checked'          => empty( $email ) ? apply_filters( 'wcsg_recipient_checkbox_checked', false ) : true,
		);

		$nonce_field  = '<input type="hidden" id="_wcsgnonce_' . $id . '" name="_wcsgnonce" value="' . wp_create_nonce( 'wcsg_add_recipient' ) . '" />';
		$nonce_field .= wp_referer_field( false );

		$args = array(
			'email'                      => $email,
			'id'                         => $id,
			'container_style_attributes' => apply_filters( 'wcsg_recipient_fields_style_attributes', array(), $email ),
			'container_css_class'        => apply_filters( 'wcsg_recipient_fields_css_class', empty( $email ) ? array( 'hidden' ) : array(), $email ),
			'email_field_args'           => apply_filters( 'wcsg_recipient_email_field_args', $email_field_args, $email ),
			'checkbox_field_args'        => apply_filters( 'wcsg_recipient_checkbox_field_args', $checkbox_field_args, $email ),
			'nonce_field'                => $nonce_field,
		);

		return $args;
	}

	/**
	 * Adds row to subscription details table that displays subscription period for recipients.
	 *
	 * @param WC_Subscription $subscription Subscription object.
	 * @since 7.8.0 - Originally implemented in WooCommerce Subscriptions Gifting 2.0.0.
	 */
	public static function add_billing_period_table_row( $subscription ) {
		if ( ! wcsg_is_wc_subscriptions_pre( '2.2.19' ) && self::is_gifted_subscription( $subscription ) && get_current_user_id() === self::get_recipient_user( $subscription ) ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
			$subscription_details  = array(
				'recurring_amount'      => '',
				'subscription_period'   => $subscription->get_billing_period(),
				'subscription_interval' => $subscription->get_billing_interval(),
				'initial_amount'        => '',
				'use_per_slash'         => false,
			);
			$billing_period_string = apply_filters( 'woocommerce_subscription_price_string_details', $subscription_details, $subscription );
			?>
			<tr>
				<td><?php echo esc_html_x( 'Renewing', 'table heading', 'woocommerce-subscriptions' ); ?></td>
				<td><?php echo esc_html( wcs_price_string( $billing_period_string ) ); ?></td>
			</tr>
			<?php
		}
	}

	/**
	 * Reformats the price of the subscription to hide it if the user is the recipient.
	 *
	 * @param string          $formatted_order_total The order total formatted.
	 * @param WC_Subscription $subscription          Subscription object.
	 * @since 7.8.0 - Originally implemented in WooCommerce Subscriptions Gifting 2.0.0.
	 */
	public static function get_formatted_recipient_total( $formatted_order_total, $subscription ) {
		global $wp;

		if ( ! wcsg_is_wc_subscriptions_pre( '2.2.19' ) && is_account_page() && isset( $wp->query_vars['subscriptions'] ) && self::is_gifted_subscription( $subscription ) && get_current_user_id() === self::get_recipient_user( $subscription ) ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
			$formatted_order_total = '-';
		}
		return $formatted_order_total;
	}

	/**
	 * Returns a combination of the customer's first name, last name and email depending on what the customer has set.
	 *
	 * @param int  $user_id    The ID of the customer user.
	 * @param bool $strip_tags Whether to strip HTML tags in user name (defaulted to false).
	 */
	public static function get_user_display_name( $user_id, $strip_tags = false ) {

		$user = get_user_by( 'id', $user_id );
		$name = '';

		if ( ! empty( $user->first_name ) ) {
			$name = $user->first_name . ( ( ! empty( $user->last_name ) ) ? ' ' . $user->last_name : '' ) . ' (' . make_clickable( $user->user_email ) . ')';
		} else {
			$name = make_clickable( $user->user_email );
		}

		if ( $strip_tags ) {
			$name = wp_strip_all_tags( $name );
		}

		return $name;
	}

	/**
	 * Displays plugin dependency notices if required plugins are inactive or the installed version is less than a
	 * supported version.
	 */
	public static function plugin_dependency_notices() {
		if ( ! class_exists( 'WooCommerce' ) ) {
			self::output_plugin_dependency_notice( 'WooCommerce' );

			return;
		}

		if ( ! class_exists( 'WC_Subscription' ) ) {
			self::output_plugin_dependency_notice( 'WooCommerce Subscriptions / WooCommerce Payments' );

			return;
		}

		if ( version_compare( get_option( 'woocommerce_subscriptions_active_version' ), self::$wcs_minimum_supported_version, '<' ) ) {
			self::output_plugin_dependency_notice( 'WooCommerce Subscriptions', self::$wcs_minimum_supported_version );
		}

		if ( version_compare( get_option( 'woocommerce_db_version' ), self::$wc_minimum_supported_version, '<' ) ) {
			self::output_plugin_dependency_notice( 'WooCommerce', self::$wc_minimum_supported_version );
		}

		if ( class_exists( 'WC_Memberships' ) && version_compare( get_option( 'wc_memberships_version' ), self::$wcm_minimum_supported_version, '<' ) ) {
			self::output_plugin_dependency_notice( 'WooCommerce Memberships', self::$wcm_minimum_supported_version );
		}
	}

	/**
	 * Prints a plugin dependency admin notice. If a required version is supplied an invalid version notice is printed,
	 * otherwise an inactive plugin notice is printed.
	 *
	 * @param string $plugin_name The plugin name.
	 * @param string|bool $required_version The minimum supported version of the plugin.
	 */
	public static function output_plugin_dependency_notice( $plugin_name, $required_version = false ) {

		if ( current_user_can( 'activate_plugins' ) ) {
			if ( $required_version ) {
				?>
				<div id="message" class="error">
					<p>
						<?php
						if ( 'WooCommerce Memberships' === $plugin_name ) {
							// translators: 1$-2$: opening and closing <strong> tags, 3$ plugin name, 4$ required plugin version, 5$-6$: opening and closing link tags, leads to plugins.php in admin, 7$: line break, 8$-9$ Opening and closing small tags.
							printf( esc_html__( '%1$sWooCommerce Subscriptions Gifting Membership integration is inactive.%2$s In order to integrate with WooCommerce Memberships, WooCommerce Subscriptions Gifting requires %3$s %4$s or newer. %5$sPlease update &raquo;%6$s %7$s%8$sNote: All other WooCommerce Subscriptions Gifting features will remain available, however purchasing membership plans for recipients will fail to grant the membership to the gift recipient.%9$s', 'woocommerce-subscriptions' ), '<strong>', '</strong>', esc_html( $plugin_name ), esc_html( $required_version ), '<a href="' . esc_url( admin_url( 'plugins.php' ) ) . '">', '</a>', '<br>', '<small>', '</small>' );
						} else {
							// translators: 1$-2$: opening and closing <strong> tags, 3$ plugin name, 4$ required plugin version, 5$-6$: opening and closing link tags, leads to plugins.php in admin.
							printf( esc_html__( '%1$sWooCommerce Subscriptions Gifting is inactive.%2$s This version of WooCommerce Subscriptions Gifting requires %3$s %4$s or newer. %5$sPlease update &raquo;%6$s', 'woocommerce-subscriptions' ), '<strong>', '</strong>', esc_html( $plugin_name ), esc_html( $required_version ), '<a href="' . esc_url( admin_url( 'plugins.php' ) ) . '">', '</a>' );
						}
						?>
					</p>
				</div>
				<?php
			} else {
				$message = null;

				if ( 'WooCommerce Subscriptions / WooCommerce Payments' === $plugin_name ) {
					$wcs_plugin_url   = 'http://www.woocommerce.com/products/woocommerce-subscriptions/';
					$wcpay_plugin_url = 'http://www.woocommerce.com/products/woocommerce-payments/';

					// translators: 1$-2$: opening and closing <strong> tags, 3$:opening link tag, leads to WooCommerce Payments plugin product page, 4$:opening link tag, leads to WooCommerce Subscriptions plugin product page, 5$-6$: opening and closing link tags, leads to plugins.php in admin.
					$message = sprintf( esc_html__( '%1$sWooCommerce Subscriptions Gifting is inactive.%2$s WooCommerce Subscriptions Gifting requires either the %3$sWooCommerce Payments%6$s or %4$sWooCommerce Subscriptions%6$s plugin to be active to work correctly. Please %5$sinstall & activate either one &raquo;%6$s', 'woocommerce-subscriptions' ), '<strong>', '</strong>', '<a href="' . esc_url( $wcpay_plugin_url ) . '">', '<a href="' . esc_url( $wcs_plugin_url ) . '">', '<a href="' . esc_url( admin_url( 'plugins.php' ) ) . '">', '</a>' );
				} elseif ( 'WooCommerce' === $plugin_name ) {
					$plugin_url = 'http://wordpress.org/extend/plugins/woocommerce/';

					// translators: 1$-2$: opening and closing <strong> tags, 3$ plugin name, 4$:opening link tag, leads to plugin product page, 5$-6$: opening and closing link tags, leads to plugins.php in admin.
					$message = sprintf( esc_html__( '%1$sWooCommerce Subscriptions Gifting is inactive.%2$s WooCommerce Subscriptions Gifting requires the %4$s%3$s%6$s plugin to be active to work correctly. Please %5$sinstall & activate %3$s &raquo;%6$s', 'woocommerce-subscriptions' ), '<strong>', '</strong>', esc_html( $plugin_name ), '<a href="' . esc_url( $plugin_url ) . '">', '<a href="' . esc_url( admin_url( 'plugins.php' ) ) . '">', '</a>' );
				}

				if ( $message ) {
					?>
					<div id="message" class="error">
						<p>
							<?php
							echo $message; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
							?>
						</p>
					</div>
					<?php
				}
			}
		}
	}

	/**
	 * Checks whether a subscription is a gifted subscription.
	 *
	 * @param int|WC_Subscription $subscription either a subscription object or subscription's ID.
	 * @return bool
	 */
	public static function is_gifted_subscription( $subscription ) {
		$is_gifted_subscription = false;

		if ( is_int( $subscription ) ) {
			$subscription = wcs_get_subscription( $subscription );
		}

		if ( ! $subscription ) {
			return false;
		}

		if ( wcs_is_subscription( $subscription ) ) {
			$recipient_user_id      = self::get_recipient_user( $subscription );
			$has_recipient_email    = $subscription->get_meta( '_recipient_user_email_address' );
			$is_gifted_subscription = ( ! empty( $recipient_user_id ) && is_numeric( $recipient_user_id ) ) || $has_recipient_email;
		}

		return $is_gifted_subscription;
	}

	/**
	 * Returns a list of all order item ids and their containing order ids that have been purchased for a recipient.
	 *
	 * @param int $recipient_user_id User ID.
	 * @return array
	 */
	public static function get_recipient_order_items( $recipient_user_id ) {
		global $wpdb;

		return $wpdb->get_results(
			$wpdb->prepare(
				"SELECT o.order_id, i.order_item_id
					FROM {$wpdb->prefix}woocommerce_order_itemmeta AS i
					INNER JOIN {$wpdb->prefix}woocommerce_order_items as o
					ON i.order_item_id=o.order_item_id
					WHERE meta_key = 'wcsg_recipient'
					AND meta_value = %s",
				'wcsg_recipient_id_' . $recipient_user_id
			),
			ARRAY_A
		);
	}

	/**
	 * Returns the user's shipping address.
	 *
	 * @param int $user_id User ID.
	 * @return array
	 */
	public static function get_users_shipping_address( $user_id ) {
		return array(
			'first_name' => get_user_meta( $user_id, 'shipping_first_name', true ),
			'last_name'  => get_user_meta( $user_id, 'shipping_last_name', true ),
			'company'    => get_user_meta( $user_id, 'shipping_company', true ),
			'address_1'  => get_user_meta( $user_id, 'shipping_address_1', true ),
			'address_2'  => get_user_meta( $user_id, 'shipping_address_2', true ),
			'city'       => get_user_meta( $user_id, 'shipping_city', true ),
			'state'      => get_user_meta( $user_id, 'shipping_state', true ),
			'postcode'   => get_user_meta( $user_id, 'shipping_postcode', true ),
			'country'    => get_user_meta( $user_id, 'shipping_country', true ),
		);
	}

	/**
	 * Determines if an order contains a gifted subscription.
	 *
	 * @param mixed $order the order id or order object to check.
	 * @return bool
	 */
	public static function order_contains_gifted_subscription( $order ) {

		if ( ! is_object( $order ) ) {
			$order = wc_get_order( $order );
		}

		if ( ! is_a( $order, 'WC_Order' ) ) {
			return false;
		}

		$contains_gifted_subscription = false;

		foreach ( wcs_get_subscriptions_for_order( $order ) as $subscription_id => $subscription ) {

			if ( self::is_gifted_subscription( $subscription ) ) {
				$contains_gifted_subscription = true;
				break;
			}
		}

		return $contains_gifted_subscription;
	}

	/**
	 * Retrieves the user id of the recipient stored in order item meta.
	 *
	 * @param mixed $order_item the order item to check.
	 * @return mixed bool|int The recipient user id or false if the order item is not gifted.
	 */
	public static function get_order_item_recipient_user_id( $order_item ) {

		if ( is_a( $order_item, 'WC_Order_Item' ) && $order_item->meta_exists( 'wcsg_recipient' ) ) {
			$raw_recipient_meta = $order_item->get_meta( 'wcsg_recipient' );
		} elseif ( isset( $order_item['item_meta']['wcsg_recipient'] ) ) {
			$raw_recipient_meta = $order_item['item_meta']['wcsg_recipient'][0];
		}

		return isset( $raw_recipient_meta ) ? substr( $raw_recipient_meta, strlen( 'wcsg_recipient_id_' ) ) : false;
	}

	/**
	 * Create a recipient user account.
	 *
	 * @param string $recipient_email Recipient's e-mail address.
	 * @return int ID for newly created user.
	 */
	public static function create_recipient_user( $recipient_email ) {
		$username = explode( '@', $recipient_email );
		$username = sanitize_user( $username[0], true );
		$counter  = 1;

		$original_username = $username;

		while ( username_exists( $username ) ) {
			$username = $original_username . $counter;
			++$counter;
		}

		$password = wp_generate_password();

		$recipient_user_id = wc_create_new_customer( $recipient_email, $username, $password );

		// set a flag to force the user to update/set account information on login.
		update_user_meta( $recipient_user_id, 'wcsg_update_account', 'true' );
		return $recipient_user_id;
	}

	/**
	 * Retrieve the recipient user ID from a subscription.
	 *
	 * @param WC_Subscription $subscription Subscription object.
	 *
	 * @return string The recipient's user ID. Returns an empty string if there is no recipient set.
	 */
	public static function get_recipient_user( $subscription ) {
		// There may be cases, especially when emails are being previewed in the customizer, where we receive something
		// other than a WC_Subscription object.
		if ( $subscription instanceof WC_Subscription && $subscription->meta_exists( '_recipient_user' ) ) {
			return $subscription->get_meta( '_recipient_user' );
		}

		return '';
	}

	/**
	 * Set the recipient user ID on a subscription.
	 *
	 * @param WC_Subscription $subscription Subscription object.
	 * @param int             $user_id      The user ID of the user to set as the recipient on the subscription.
	 * @param string          $save         Whether to save the data or not, 'save' to save the data, otherwise it won't be saved.
	 * @param int             $meta_id      The meta ID of existing meta data if you wish to overwrite an existing recipient meta value.
	 * @param WC_Order        $order        Order object.
	 */
	public static function set_recipient_user( &$subscription, $user_id, $save = 'save', $meta_id = 0, ?WC_Order $order = null ) {
		$current_user_id = absint( self::get_recipient_user( $subscription ) );

		if ( 'save' === $save ) {
			$subscription->update_meta_data( '_recipient_user', $user_id, $meta_id );
			$subscription->save();

			$gifting_subscription_items = $subscription->get_items();
			$gifting_subcription_item   = reset( $gifting_subscription_items );

			if ( ! empty( $gifting_subcription_item ) ) {
				if ( ! $order ) {
					$order = wc_get_order( $subscription->get_parent_id() );
				}

				if ( $order ) {
					foreach ( $order->get_items() as $order_item ) {
						if ( $order_item->get_meta( '_wcsg_cart_key' ) === $gifting_subcription_item->get_meta( '_wcsg_cart_key' ) ) {
							$order_item->add_meta_data( 'wcsg_recipient', 'wcsg_recipient_id_' . $user_id, true );
							$order_item->save();
						}
					}
				}
			}

			do_action( 'woocommerce_subscriptions_gifting_recipient_changed', $subscription, $user_id, $current_user_id );
		}
	}

	/**
	 * Delete the recipient user ID on a subscription
	 *
	 * @param WC_Subscription $subscription Subscription object.
	 * @param string          $save         Whether to save the data or not, 'save' to save the data, otherwise it won't be saved.
	 * @param int             $meta_id      The meta ID of existing recipient meta data if you wish to only delete a field specified by ID.
	 */
	public static function delete_recipient_user( &$subscription, $save = 'save', $meta_id = 0 ) {
		// Save the data.
		if ( 'save' === $save ) {
			if ( ! empty( $meta_id ) ) {
				$subscription->delete_meta_data_by_mid( $meta_id );
			} else {
				$subscription->delete_meta_data( '_recipient_user' );
			}

			$subscription->save();
		}
	}

	/**
	 * Retrieves a set of gifted subscriptions based on certain parameters.
	 *
	 * @see wc_get_orders()
	 *
	 * @param array $args Custom args for query, excluding 'type' and custom var 'is_gifted_subscription'.
	 *                    Passing `'paginate' => true` switches the return shape to the standard
	 *                    `wc_get_orders()` paginated stdClass envelope ({orders, total, max_num_pages});
	 *                    callers that need a count and a page in one shot should use that form.
	 *
	 * @return WC_Order[]|\stdClass `WC_Order[]` by default, or the paginated envelope
	 *                              when `$args['paginate']` is true.
	 *
	 * @since 7.8.0 - Originally implemented in WooCommerce Subscriptions Gifting 2.0.3.
	 */
	public static function get_gifted_subscriptions( $args = array() ) {
		$query_args = wp_parse_args(
			$args,
			array(
				'limit'   => -1,
				'status'  => 'any',
				'orderby' => 'date',
				'order'   => 'desc',
			)
		);

		$query_args['type'] = 'shop_subscription';

		if ( function_exists( 'wcs_get_orders_with_meta_query' ) ) {
			$query_args['meta_query'] = array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query
				array(
					'key'     => '_recipient_user',
					'compare' => 'EXISTS',
				),
			);

			return wcs_get_orders_with_meta_query( $query_args );
		}

		$query_args['is_gifted_subscription'] = true;

		return wc_get_orders( $query_args );
	}

	/**
	 * Handle custom WCS Gifting query vars to get subscriptions with 'WCS Gifting' meta.
	 *
	 * @param array $query      Args for WP_Query.
	 * @param array $query_vars Query vars from WC_Order_Query.
	 *
	 * @return array modified $query
	 * @since 7.8.0 - Originally implemented in WooCommerce Subscriptions Gifting 2.0.3.
	 */
	public static function handle_is_gifted_subscription_query_var( $query, $query_vars ) {
		if ( ! empty( $query_vars['is_gifted_subscription'] ) && true === $query_vars['is_gifted_subscription'] ) {
			$query['meta_query'][] = array(
				array(
					'key'     => '_recipient_user',
					'compare' => 'EXISTS',
				),
			);
		}

		return $query;
	}

	/**
	 * Does the site requires shipping address data for non-virtual products. Default: true
	 *
	 * @return bool
	 */
	public static function require_shipping_address_for_virtual_products() {
		return apply_filters( 'wcsg_require_shipping_address_for_virtual_products', true );
	}

	/**
	 * Counts the number of Gifted Subscriptions.
	 *
	 * @return int
	 * @since 7.8.0 - Originally implemented in WooCommerce Subscriptions Gifting 2.1.0.
	 */
	public static function get_gifted_subscriptions_count() {
		if ( ! class_exists( \Automattic\WooCommerce_Subscriptions\Internal\Telemetry\Subscriptions::class ) ) {
			return 0;
		}

		$telemetry = new \Automattic\WooCommerce_Subscriptions\Internal\Telemetry\Subscriptions();
		return $telemetry->get_gifted_subscriptions_count();
	}

	/**
	 * Register/queue admin scripts.
	 *
	 * @deprecated 2.0.0 Use WCSG_Admin::enqueue_scripts() instead.
	 */
	public static function admin_scripts() {
		_deprecated_function( __METHOD__, '2.0.0', 'WCSG_Admin::enqueue_scripts()' );
	}

	/**
	 * Install wcsg
	 *
	 * @deprecated 2.0.0 Use WCS_Gifting::maybe_activate() instead.
	 */
	public static function wcsg_install() {
		_deprecated_function( __METHOD__, '2.0.0', 'WCS_Gifting::maybe_activate()' );
	}

	/**
	 * Flush rewrite rules if they haven't been flushed since plugin activation
	 *
	 * @deprecated 2.0.0 Use flush_rewrite_rules() instead.
	 */
	public static function maybe_flush_rewrite_rules() {
		_deprecated_function( __METHOD__, '2.0.0', 'flush_rewrite_rules()' );
	}

	/**
	 * Overrides the default recent order template for gifted subscriptions
	 *
	 * @deprecated 2.0.0 Use WCSG_Template_Loader::get_recent_orders_template() instead.
	 *
	 * @param string $located       Path to template.
	 * @param string $template_name Template name.
	 * @param array  $args          Arguments.
	 */
	public static function get_recent_orders_template( $located, $template_name, $args ) {
		_deprecated_function( __FUNCTION__, '2.0.0', 'WCSG_Template_Loader::get_recent_orders_template()' );
		WCSG_Template_Loader::get_recent_orders_template( $located, $template_name, $args );
	}

	/**
	 * Generates an array of arguments used to create the recipient email html fields.
	 *
	 * @param string $email E-mail address.
	 * @return array email_field_args A set of html attributes
	 * @deprecated 2.1
	 */
	public static function get_recipient_email_field_args( $email ) {
		_deprecated_function( __METHOD__, '2.1', 'WCS_Gifting::get_add_recipient_template_args()' );
		$args = self::get_add_recipient_template_args( $email );
		return $args['email_field_args'];
	}

	/**
	 * Generates an array of arguments used to create the recipient checkbox html fields
	 *
	 * @param string $email The email of the gift recipient.
	 * @return array checkbox_field_args A set of html attributes
	 * @since 7.8.0 - Originally implemented in WooCommerce Subscriptions Gifting 2.0.2.
	 * @deprecated 2.1
	 */
	public static function get_recipient_checkbox_field_args( $email ) {
		_deprecated_function( __METHOD__, '2.1', 'WCS_Gifting::get_add_recipient_template_args()' );
		$args = self::get_add_recipient_template_args( $email );
		return $args['checkbox_field_args'];
	}

	public static function setup_blocks_integration() {
		if ( ! class_exists( 'Automattic\WooCommerce\Blocks\Package' ) || ! version_compare( \Automattic\WooCommerce\Blocks\Package::get_version(), '4.4.0', '>' ) ) {
			return;
		}

		if ( ! WCSG_Admin::is_gifting_enabled() ) {
			return;
		}

		/**
		 * Filter the compatible blocks for WooCommerce Subscriptions.
		 * @since 7.8.2
		 */
		$compatible_blocks = apply_filters(
			'wcsg_compatible_blocks',
			[ 'cart', 'checkout', 'mini-cart' ]
		);

		foreach ( $compatible_blocks as $block_name ) {
			add_action(
				"woocommerce_blocks_{$block_name}_block_registration",
				function ( $integration_registry ) {
					$integration_registry->register( new WCSG_Blocks_Integration() );
				}
			);
		}
	}
}
