<?php
/**
 * Kkart Onboarding
 * NOTE: DO NOT edit this file in Kkart core, this is generated from kkart-admin.
 */

namespace Automattic\Kkart\Admin\Features;

use \Automattic\Kkart\Admin\Loader;
use \Automattic\Kkart\Admin\PluginsHelper;
use \Automattic\Kkart\Admin\Features\OnboardingSetUpShipping;
use \Automattic\Kkart\Admin\Features\OnboardingAutomateTaxes;

/**
 * Contains backend logic for the onboarding profile and checklist feature.
 */
class Onboarding {
	/**
	 * Class instance.
	 *
	 * @var Onboarding instance
	 */
	protected static $instance = null;

	/**
	 * Name of themes transient.
	 *
	 * @var string
	 */
	const THEMES_TRANSIENT = 'kkart_onboarding_themes';

	/**
	 * Name of product data transient.
	 *
	 * @var string
	 */
	const PRODUCT_DATA_TRANSIENT = 'kkart_onboarding_product_data';

	/**
	 * Profile data option name.
	 */
	const PROFILE_DATA_OPTION = 'kkart_onboarding_profile';

	/**
	 * Get class instance.
	 */
	public static function get_instance() {
		if ( ! self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Hook into Kkart.
	 */
	public function __construct() {
		$this->add_toggle_actions();

		// Include KKART Admin Onboarding classes.
		if ( self::should_show_tasks() ) {
			OnboardingTasks::get_instance();
		}

		// Add actions and filters.
		$this->add_actions();
		$this->add_filters();

		// Hook up dependent classes.
		new OnboardingSetUpShipping();
		new OnboardingAutomateTaxes();
	}

	/**
	 * Adds the ability to toggle the new onboarding experience on or off.
	 */
	private function add_toggle_actions() {
		add_action( 'kkart_updated', array( $this, 'maybe_mark_complete' ) );
		add_action( 'update_option_' . self::PROFILE_DATA_OPTION, array( $this, 'send_profile_data_on_update' ), 10, 2 );
		add_action( 'kkart_helper_connected', array( $this, 'send_profile_data_on_connect' ) );
	}

	/**
	 * Add onboarding actions.
	 */
	private function add_actions() {
		// Rest API hooks need to run before is_admin() checks.
		add_action( 'kkart_theme_installed', array( $this, 'delete_themes_transient' ) );
		add_action( 'after_switch_theme', array( $this, 'delete_themes_transient' ) );
		add_action(
			'update_option_' . self::PROFILE_DATA_OPTION,
			array(
				$this,
				'trigger_profile_completed_action',
			),
			10,
			2
		);

		// Always hook into Jetpack connection even if outside of admin.
		add_action( 'jetpack_site_registered', array( $this, 'set_kkart_setup_jetpack_opted_in' ) );

		if ( ! is_admin() ) {
			return;
		}

		add_action( 'admin_init', array( $this, 'admin_redirects' ) );
		add_action( 'current_screen', array( $this, 'finish_paypal_connect' ) );
		add_action( 'current_screen', array( $this, 'finish_square_connect' ) );
		add_action( 'current_screen', array( $this, 'add_help_tab' ), 60 );
		add_action( 'current_screen', array( $this, 'reset_profiler' ) );
		add_action( 'current_screen', array( $this, 'reset_task_list' ) );
		add_action( 'current_screen', array( $this, 'calypso_tests' ) );
		add_action( 'current_screen', array( $this, 'redirect_wccom_install' ) );
		add_action( 'current_screen', array( $this, 'redirect_old_onboarding' ) );
	}

	/**
	 * Test whether the context of execution comes from async action scheduler.
	 * Note: this is a polyfill for kkart_is_running_from_async_action_scheduler()
	 *       which was introduced in KKART 4.0.
	 *
	 * @return bool
	 */
	public static function is_running_from_async_action_scheduler() {
		if ( function_exists( '\kkart_is_running_from_async_action_scheduler' ) ) {
			return \kkart_is_running_from_async_action_scheduler();
		}

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		return isset( $_REQUEST['action'] ) && 'as_async_request_queue_runner' === $_REQUEST['action'];
	}

	/**
	 * Handle redirects to setup/welcome page after install and updates.
	 *
	 * For setup wizard, transient must be present, the user must have access rights, and we must ignore the network/bulk plugin updaters.
	 */
	public function admin_redirects() {
		// Don't run this fn from Action Scheduler requests, as it would clear _kkart_activation_redirect transient.
		// That means OBW would never be shown.
		if ( self::is_running_from_async_action_scheduler() ) {
			return;
		}

		// Setup wizard redirect.
		if ( get_transient( '_kkart_activation_redirect' ) && apply_filters( 'kkart_enable_setup_wizard', true ) ) {
			$do_redirect        = true;
			$current_page       = isset( $_GET['page'] ) ? kkart_clean( wp_unslash( $_GET['page'] ) ) : false; // phpcs:ignore WordPress.Security.NonceVerification
			$is_onboarding_path = ! isset( $_GET['path'] ) || '/setup-wizard' === kkart_clean( wp_unslash( $_GET['page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification

			// On these pages, or during these events, postpone the redirect.
			if ( wp_doing_ajax() || is_network_admin() || ! current_user_can( 'manage_kkart' ) ) {
				$do_redirect = false;
			}

			// On these pages, or during these events, disable the redirect.
			if (
				( 'kkart-admin' === $current_page && $is_onboarding_path ) ||
				apply_filters( 'kkart_prevent_automatic_wizard_redirect', false ) ||
				isset( $_GET['activate-multi'] ) // phpcs:ignore WordPress.Security.NonceVerification
			) {
				delete_transient( '_kkart_activation_redirect' );
				$do_redirect = false;
			}

			if ( $do_redirect ) {
				delete_transient( '_kkart_activation_redirect' );
				wp_safe_redirect( kkart_admin_url() );
				exit;
			}
		}
	}

	/**
	 * Sets the kkart_setup_jetpack_opted_in to true when Jetpack connects to WPCOM.
	 */
	public function set_kkart_setup_jetpack_opted_in() {
		update_option( 'kkart_setup_jetpack_opted_in', true );
	}

	/**
	 * Trigger the kkart_onboarding_profile_completed action
	 *
	 * @param array $old_value Previous value.
	 * @param array $value Current value.
	 */
	public function trigger_profile_completed_action( $old_value, $value ) {
		if ( isset( $old_value['completed'] ) && $old_value['completed'] ) {
			return;
		}

		if ( ! isset( $value['completed'] ) || ! $value['completed'] ) {
			return;
		}

		/**
		 * Action hook fired when the onboarding profile (or onboarding wizard,
		 * or profiler) is completed.
		 *
		 * @since 1.5.0
		 */
		do_action( 'kkart_onboarding_profile_completed' );
	}

	/**
	 * Add onboarding filters.
	 */
	private function add_filters() {
		// Rest API hooks need to run before is_admin() checks.
		add_filter( 'kkart_rest_prepare_themes', array( $this, 'add_uploaded_theme_data' ) );
		add_filter( 'kkart_admin_plugins_whitelist', array( $this, 'get_onboarding_allowed_plugins' ), 10, 2 );

		if ( ! is_admin() ) {
			return;
		}

		// Old settings injection.
		// Run after Automattic\Kkart\Admin\Loader.
		add_filter( 'kkart_components_settings', array( $this, 'component_settings' ), 20 );
		// New settings injection.
		add_filter( 'kkart_shared_settings', array( $this, 'component_settings' ), 20 );
		add_filter( 'kkart_admin_preload_options', array( $this, 'preload_options' ) );
		add_filter( 'kkart_admin_preload_settings', array( $this, 'preload_settings' ) );
		add_filter( 'kkart_admin_is_loading', array( $this, 'is_loading' ) );
		add_filter( 'kkart_show_admin_notice', array( $this, 'remove_install_notice' ), 10, 2 );
	}

	/**
	 * Send profile data to Kkart.com.
	 */
	public static function send_profile_data() {
		if ( 'yes' !== get_option( 'kkart_allow_tracking', 'no' ) ) {
			return;
		}

		if ( ! class_exists( '\KKART_Helper_API' ) || ! method_exists( '\KKART_Helper_API', 'put' ) ) {
			return;
		}

		if ( ! class_exists( '\KKART_Helper_Options' ) ) {
			return;
		}

		$auth = \KKART_Helper_Options::get( 'auth' );
		if ( empty( $auth['access_token'] ) || empty( $auth['access_token_secret'] ) ) {
			return false;
		}

		$profile       = get_option( self::PROFILE_DATA_OPTION, array() );
		$base_location = kkart_get_base_location();
		$defaults      = array(
			'plugins'             => 'skipped',
			'industry'            => array(),
			'product_types'       => array(),
			'product_count'       => '0',
			'selling_venues'      => 'no',
			'revenue'             => 'none',
			'other_platform'      => 'none',
			'business_extensions' => array(),
			'theme'               => get_stylesheet(),
			'setup_client'        => false,
			'store_location'      => $base_location['country'],
			'default_currency'    => get_kkart_currency(),
		);

		// Prepare industries as an array of slugs if they are in array format.
		if ( isset( $profile['industry'] ) && is_array( $profile['industry'] ) ) {
			$industry_slugs = array();
			foreach ( $profile['industry'] as $industry ) {
				$industry_slugs[] = is_array( $industry ) ? $industry['slug'] : $industry;
			}
			$profile['industry'] = $industry_slugs;
		}
		$body = wp_parse_args( $profile, $defaults );

		\KKART_Helper_API::put(
			'profile',
			array(
				'authenticated' => true,
				'body'          => wp_json_encode( $body ),
				'headers'       => array(
					'Content-Type' => 'application/json',
				),
			)
		);
	}

	/**
	 * Send profiler data on profiler change to completion.
	 *
	 * @param array $old_value Previous value.
	 * @param array $value Current value.
	 */
	public static function send_profile_data_on_update( $old_value, $value ) {
		if ( ! isset( $value['completed'] ) || ! $value['completed'] ) {
			return;
		}

		self::send_profile_data();
	}

	/**
	 * Send profiler data after a site is connected.
	 */
	public static function send_profile_data_on_connect() {
		$profile = get_option( self::PROFILE_DATA_OPTION, array() );
		if ( ! isset( $profile['completed'] ) || ! $profile['completed'] ) {
			return;
		}

		self::send_profile_data();
	}

	/**
	 * Redirect the old onboarding wizard to the profiler.
	 */
	public static function redirect_old_onboarding() {
		$current_page = isset( $_GET['page'] ) ? kkart_clean( wp_unslash( $_GET['page'] ) ) : false; // phpcs:ignore csrf okay.

		if ( 'kkart-setup' === $current_page ) {
			delete_transient( '_kkart_activation_redirect' );
			wp_safe_redirect( kkart_admin_url( '&reset_profiler=1' ) );
		}
	}

	/**
	 * Returns true if the profiler should be displayed (not completed and not skipped).
	 *
	 * @return bool
	 */
	public static function should_show_profiler() {
		/* phpcs:disable WordPress.Security.NonceVerification */
		$is_current_page = isset( $_GET['page'] ) &&
			'kkart-admin' === $_GET['page'] &&
			isset( $_GET['path'] ) &&
			'/setup-wizard' === $_GET['path'];
		/* phpcs: enable */

		if ( $is_current_page ) {
			return true;
		}

		$onboarding_data = get_option( self::PROFILE_DATA_OPTION, array() );

		$is_completed = isset( $onboarding_data['completed'] ) && true === $onboarding_data['completed'];
		$is_skipped   = isset( $onboarding_data['skipped'] ) && true === $onboarding_data['skipped'];

		// @todo When merging to Kkart Core, we should set the `completed` flag to true during the upgrade progress.
		// https://github.com/kkart/kkart-admin/pull/2300#discussion_r287237498.
		return ! $is_completed && ! $is_skipped;
	}

	/**
	 * Returns true if the task list should be displayed (not completed or hidden off the dashboard).
	 *
	 * @return bool
	 */
	public static function should_show_tasks() {
		return 'no' === get_option( 'kkart_task_list_hidden', 'no' );
	}

	/**
	 * Get a list of allowed industries for the onboarding wizard.
	 *
	 * @return array
	 */
	public static function get_allowed_industries() {
		/* With "use_description" we turn the description input on. With "description_label" we set the input label */
		return apply_filters(
			'kkart_admin_onboarding_industries',
			array(
				'fashion-apparel-accessories'     => array(
					'label'             => __( 'Fashion, apparel, and accessories', 'kkart' ),
					'use_description'   => false,
					'description_label' => '',
				),
				'health-beauty'                   => array(
					'label'             => __( 'Health and beauty', 'kkart' ),
					'use_description'   => false,
					'description_label' => '',
				),
				'electronics-computers'           => array(
					'label'             => __( 'Electronics and computers', 'kkart' ),
					'use_description'   => false,
					'description_label' => '',
				),
				'food-drink'                      => array(
					'label'             => __( 'Food and drink', 'kkart' ),
					'use_description'   => false,
					'description_label' => '',
				),
				'home-furniture-garden'           => array(
					'label'             => __( 'Home, furniture, and garden', 'kkart' ),
					'use_description'   => false,
					'description_label' => '',
				),
				'cbd-other-hemp-derived-products' => array(
					'label'             => __( 'CBD and other hemp-derived products', 'kkart' ),
					'use_description'   => false,
					'description_label' => '',
				),
				'education-and-learning'          => array(
					'label'             => __( 'Education and learning', 'kkart' ),
					'use_description'   => false,
					'description_label' => '',
				),
				'other'                           => array(
					'label'             => __( 'Other', 'kkart' ),
					'use_description'   => true,
					'description_label' => 'Description',
				),
			)
		);
	}

	/**
	 * Get a list of allowed product types for the onboarding wizard.
	 *
	 * @return array
	 */
	public static function get_allowed_product_types() {
		$product_types = self::append_product_data(
			array(
				'physical'        => array(
					'label'   => __( 'Physical products', 'kkart' ),
					'default' => true,
				),
				'downloads'       => array(
					'label' => __( 'Downloads', 'kkart' ),
				),
				'subscriptions'   => array(
					'label'   => __( 'Subscriptions', 'kkart' ),
					'product' => 27147,
				),
				'memberships'     => array(
					'label'   => __( 'Memberships', 'kkart' ),
					'product' => 958589,
				),
				'bookings'        => array(
					'label'   => __( 'Bookings', 'kkart' ),
					'product' => 390890,
				),
				'product-bundles' => array(
					'label'   => __( 'Bundles', 'kkart' ),
					'product' => 18716,
				),
				'product-add-ons' => array(
					'label'   => __( 'Customizable products', 'kkart' ),
					'product' => 18618,
				),
			)
		);

		return apply_filters( 'kkart_admin_onboarding_product_types', $product_types );
	}

	/**
	 * Sort themes returned from Kkart.com
	 *
	 * @param  array $themes Array of themes from Kkart.com.
	 * @return array
	 */
	public static function sort_kkart_themes( $themes ) {
		usort(
			$themes,
			function ( $product_1, $product_2 ) {
				if ( ! property_exists( $product_1, 'id' ) || ! property_exists( $product_1, 'slug' ) ) {
					return 1;
				}
				if ( ! property_exists( $product_2, 'id' ) || ! property_exists( $product_2, 'slug' ) ) {
					return 1;
				}
				if ( in_array( 'Storefront', array( $product_1->slug, $product_2->slug ), true ) ) {
					return 'Storefront' === $product_1->slug ? -1 : 1;
				}
				return $product_1->id < $product_2->id ? 1 : -1;
			}
		);
		return $themes;
	}

	/**
	 * Get a list of themes for the onboarding wizard.
	 *
	 * @return array
	 */
	public static function get_themes() {
		$themes = get_transient( self::THEMES_TRANSIENT );
		if ( false === $themes ) {
			$theme_data = wp_remote_get( 'https://kkart.com/sp-json/wccom-extensions/1.0/search?category=themes' );
			$themes     = array();

			if ( ! is_wp_error( $theme_data ) ) {
				$theme_data    = json_decode( $theme_data['body'] );
				$woo_themes    = property_exists( $theme_data, 'products' ) ? $theme_data->products : array();
				$sorted_themes = self::sort_kkart_themes( $woo_themes );

				foreach ( $sorted_themes as $theme ) {
					$slug                                       = sanitize_title_with_dashes( $theme->slug );
					$themes[ $slug ]                            = (array) $theme;
					$themes[ $slug ]['is_installed']            = false;
					$themes[ $slug ]['has_kkart_support'] = true;
					$themes[ $slug ]['slug']                    = $slug;
				}
			}

			$installed_themes = wp_get_themes();
			$active_theme     = get_option( 'stylesheet' );

			foreach ( $installed_themes as $slug => $theme ) {
				$theme_data       = self::get_theme_data( $theme );
				$installed_themes = wp_get_themes();
				$themes[ $slug ]  = $theme_data;
			}

			// Add the Kkart support tag for default themes that don't explicitly declare support.
			if ( function_exists( 'kkart_is_wp_default_theme_active' ) && kkart_is_wp_default_theme_active() ) {
				$themes[ $active_theme ]['has_kkart_support'] = true;
			}

			$themes = array( $active_theme => $themes[ $active_theme ] ) + $themes;

			set_transient( self::THEMES_TRANSIENT, $themes, DAY_IN_SECONDS );
		}

		$themes = apply_filters( 'kkart_admin_onboarding_themes', $themes );
		return array_values( $themes );
	}

	/**
	 * Get theme data used in onboarding theme browser.
	 *
	 * @param WP_Theme $theme Theme to gather data from.
	 * @return array
	 */
	public static function get_theme_data( $theme ) {
		return array(
			'slug'                    => sanitize_text_field( $theme->stylesheet ),
			'title'                   => $theme->get( 'Name' ),
			'price'                   => '0.00',
			'is_installed'            => true,
			'image'                   => $theme->get_screenshot(),
			'has_kkart_support' => self::has_kkart_support( $theme ),
		);
	}

	/**
	 * Add theme data to response from themes controller.
	 *
	 * @param WP_REST_Response $response Rest response.
	 * @return WP_REST_Response
	 */
	public static function add_uploaded_theme_data( $response ) {
		if ( ! isset( $response->data['theme'] ) ) {
			return $response;
		}

		$theme                        = wp_get_theme( $response->data['theme'] );
		$response->data['theme_data'] = self::get_theme_data( $theme );

		return $response;
	}

	/**
	 * Check if theme has declared support for Kkart.
	 *
	 * @param WP_Theme $theme Theme to check.
	 * @return bool
	 */
	public static function has_kkart_support( $theme ) {
		$themes = array( $theme );
		if ( $theme->get( 'Template' ) ) {
			$parent_theme = wp_get_theme( $theme->get( 'Template' ) );
			$themes[]     = $parent_theme;
		}

		foreach ( $themes as $theme ) {
			$directory = new \RecursiveDirectoryIterator( $theme->theme_root . '/' . $theme->stylesheet );
			$iterator  = new \RecursiveIteratorIterator( $directory );
			$files     = new \RegexIterator( $iterator, '/^.+\.php$/i', \RecursiveRegexIterator::GET_MATCH );

			foreach ( $files as $file ) {
				$content = file_get_contents( $file[0] );
				if ( preg_match( '/add_theme_support\(([^(]*)(\'|\")kkart(\'|\")([^(]*)/si', $content, $matches ) ) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Append dynamic product data from API.
	 *
	 * @param array $product_types Array of product types.
	 * @return array
	 */
	public static function append_product_data( $product_types ) {
		$kkart_products = get_transient( self::PRODUCT_DATA_TRANSIENT );
		if ( false === $kkart_products ) {
			$kkart_products = wp_remote_get( 'https://kkart.com/sp-json/wccom-extensions/1.0/search' );
			if ( is_wp_error( $kkart_products ) ) {
				return $product_types;
			}

			set_transient( self::PRODUCT_DATA_TRANSIENT, $kkart_products, DAY_IN_SECONDS );
		}

		$product_data = json_decode( $kkart_products['body'] );
		$products     = array();

		// Map product data by ID.
		if ( isset( $product_data ) && isset( $product_data->products ) ) {
			foreach ( $product_data->products as $product_datum ) {
				if ( isset( $product_datum->id ) ) {
					$products[ $product_datum->id ] = $product_datum;
				}
			}
		}

		// Loop over product types and append data.
		foreach ( $product_types as $key => $product_type ) {
			if ( isset( $product_type['product'] ) && isset( $products[ $product_type['product'] ] ) ) {
				$price        = html_entity_decode( $products[ $product_type['product'] ]->price );
				$yearly_price = (float) str_replace( '$', '', $price );

				$product_types[ $key ]['yearly_price'] = $yearly_price;
				$product_types[ $key ]['description']  = $products[ $product_type['product'] ]->excerpt;
				$product_types[ $key ]['more_url']     = $products[ $product_type['product'] ]->link;
				$product_types[ $key ]['slug']         = strtolower( preg_replace( '~[^\pL\d]+~u', '-', $products[ $product_type['product'] ]->slug ) );
			} elseif ( isset( $product_type['product'] ) ) {
				/* translators: site currency symbol (used to show that the product costs money) */
				$product_types[ $key ]['label'] .= sprintf( __( ' — %s', 'kkart' ), html_entity_decode( get_kkart_currency_symbol() ) );
			}
		}

		return $product_types;
	}

	/**
	 * Delete the stored themes transient.
	 */
	public static function delete_themes_transient() {
		delete_transient( self::THEMES_TRANSIENT );
	}

	/**
	 * Add profiler items to component settings.
	 *
	 * @param array $settings Component settings.
	 */
	public function component_settings( $settings ) {
		$profile                = (array) get_option( self::PROFILE_DATA_OPTION, array() );
		$settings['onboarding'] = array(
			'profile' => $profile,
		);

		// Only fetch if the onboarding wizard OR the task list is incomplete or currently shown.
		if ( ! self::should_show_profiler() && ! self::should_show_tasks() ) {
			return $settings;
		}

		include_once KKART_ABSPATH . 'includes/admin/helper/class-kkart-helper-options.php';
		$wccom_auth                 = \KKART_Helper_Options::get( 'auth' );
		$profile['wccom_connected'] = empty( $wccom_auth['access_token'] ) ? false : true;

		$settings['onboarding']['activeTheme']  = get_option( 'stylesheet' );
		$settings['onboarding']['euCountries']  = KKART()->countries->get_european_union_countries();
		$settings['onboarding']['industries']   = self::get_allowed_industries();
		$settings['onboarding']['productTypes'] = self::get_allowed_product_types();
		$settings['onboarding']['profile']      = $profile;
		$settings['onboarding']['themes']       = self::get_themes();

		return $settings;
	}

	/**
	 * Preload options to prime state of the application.
	 *
	 * @param array $options Array of options to preload.
	 * @return array
	 */
	public function preload_options( $options ) {
		$options[] = 'kkart_task_list_complete';
		$options[] = 'kkart_task_list_do_this_later';
		$options[] = 'kkart_task_list_hidden';

		if ( ! self::should_show_tasks() && ! self::should_show_profiler() ) {
			return $options;
		}

		$options[] = 'kkart_connect_options';
		$options[] = 'kkart_task_list_welcome_modal_dismissed';
		$options[] = 'kkart_task_list_prompt_shown';
		$options[] = 'kkart_task_list_tracked_completed_tasks';
		$options[] = 'kkart_task_list_dismissed_tasks';
		$options[] = 'kkart_allow_tracking';
		$options[] = 'kkart_stripe_settings';
		$options[] = 'kkart_ppec_paypal_settings';
		$options[] = 'kkart_square_refresh_tokens';
		$options[] = 'kkart_square_credit_card_settings';
		$options[] = 'kkart_payfast_settings';
		$options[] = 'kkart_kco_settings';
		$options[] = 'kkart_klarna_payments_settings';
		$options[] = 'kkart_cod_settings';
		$options[] = 'kkart_bacs_settings';
		$options[] = 'kkart_bacs_accounts';
		$options[] = 'kkart_kkart_payments_settings';
		$options[] = 'kkart_eway_settings';

		return $options;
	}

	/**
	 * Preload KKART setting options to prime state of the application.
	 *
	 * @param array $options Array of options to preload.
	 * @return array
	 */
	public function preload_settings( $options ) {
		$options[] = 'general';

		return $options;
	}

	/**
	 * Gets an array of plugins that can be installed & activated via the onboarding wizard.
	 *
	 * @param array $plugins Array of plugin slugs to be allowed.
	 *
	 * @return array
	 * @todo Handle edgecase of where installed plugins may have versioned folder names (i.e. `jetpack-main/jetpack.php`).
	 */
	public static function get_onboarding_allowed_plugins( $plugins ) {
		$onboarding_plugins = apply_filters(
			'kkart_admin_onboarding_plugins_whitelist',
			array(
				'facebook-for-kkart'            => 'facebook-for-kkart/facebook-for-kkart.php',
				'mailchimp-for-kkart'           => 'mailchimp-for-kkart/mailchimp-kkart.php',
				'creative-mail-by-constant-contact'   => 'creative-mail-by-constant-contact/creative-mail-plugin.php',
				'kliken-marketing-for-google'         => 'kliken-marketing-for-google/kliken-marketing-for-google.php',
				'jetpack'                             => 'jetpack/jetpack.php',
				'kkart-services'                => 'kkart-services/kkart-services.php',
				'kkart-gateway-stripe'          => 'kkart-gateway-stripe/kkart-gateway-stripe.php',
				'kkart-gateway-paypal-express-checkout' => 'kkart-gateway-paypal-express-checkout/kkart-gateway-paypal-express-checkout.php',
				'klarna-checkout-for-kkart'     => 'klarna-checkout-for-kkart/klarna-checkout-for-kkart.php',
				'klarna-payments-for-kkart'     => 'klarna-payments-for-kkart/klarna-payments-for-kkart.php',
				'kkart-square'                  => 'kkart-square/kkart-square.php',
				'kkart-shipstation-integration' => 'kkart-shipstation-integration/kkart-shipstation.php',
				'kkart-payfast-gateway'         => 'kkart-payfast-gateway/gateway-payfast.php',
				'kkart-payments'                => 'kkart-payments/kkart-payments.php',
				'kkart-gateway-eway'            => 'kkart-gateway-eway/kkart-gateway-eway.php',
			)
		);
		return array_merge( $plugins, $onboarding_plugins );
	}

	/**
	 * Gets an array of themes that can be installed & activated via the onboarding wizard.
	 *
	 * @return array
	 */
	public static function get_allowed_themes() {
		$allowed_themes = array();
		$themes         = self::get_themes();

		foreach ( $themes as $theme ) {
			$price = preg_replace( '/&#?[a-z0-9]+;/i', '', $theme['price'] );

			if ( $theme['is_installed'] || '0.00' === $price ) {
				$allowed_themes[] = $theme['slug'];
			}
		}

		return apply_filters( 'kkart_admin_onboarding_themes_whitelist', $allowed_themes );
	}

	/**
	 * Let the app know that we will be showing the onboarding route, so wp-admin elements should be hidden while loading.
	 *
	 * @param bool $is_loading Indicates if the `kkart-admin-is-loading` should be appended or not.
	 * @return bool
	 */
	public function is_loading( $is_loading ) {
		$show_profiler = self::should_show_profiler();

		if ( $show_profiler ) {
			return true;
		}

		return $is_loading;
	}

	/**
	 * Instead of redirecting back to the payment settings page, we will redirect back to the payments task list with our status.
	 *
	 * @param string $location URL of redirect.
	 * @param int    $status HTTP response status code.
	 * @return string URL of redirect.
	 */
	public function overwrite_paypal_redirect( $location, $status ) {
		$settings_page = 'tab=checkout&section=ppec_paypal';
		if ( substr( $location, -strlen( $settings_page ) ) === $settings_page ) {
			$settings_array = (array) get_option( 'kkart_ppec_paypal_settings', array() );
			$connected      = isset( $settings_array['api_username'] ) && isset( $settings_array['api_password'] ) ? true : false;
			return kkart_admin_url( '&task=payments&method=paypal&paypal-connect=' . $connected );
		}
		return $location;
	}

	/**
	 * Finishes the PayPal connection process by saving the correct settings.
	 */
	public function finish_paypal_connect() {
		if (
			! Loader::is_admin_page() ||
			! isset( $_GET['paypal-connect-finish'] ) // phpcs:ignore CSRF ok.
		) {
			return;
		}

		if ( ! function_exists( 'kkart_gateway_ppec' ) ) {
			return false;
		}

		// @todo This is a bit hacky but works. Ideally, kkart-gateway-paypal-express-checkout would contain a filter for us.
		add_filter( 'wp_redirect', array( $this, 'overwrite_paypal_redirect' ), 10, 2 );
		kkart_gateway_ppec()->ips->maybe_received_credentials();
		remove_filter( 'wp_redirect', array( $this, 'overwrite_paypal_redirect' ) );
	}

	/**
	 * Instead of redirecting back to the payment settings page, we will redirect back to the payments task list with our status.
	 *
	 * @param string $location URL of redirect.
	 * @param int    $status HTTP response status code.
	 * @return string URL of redirect.
	 */
	public function overwrite_square_redirect( $location, $status ) {
		$settings_page = 'page=kkart-settings&tab=square';
		if ( substr( $location, -strlen( $settings_page ) ) === $settings_page ) {
			return kkart_admin_url( '&task=payments&method=square&square-connect=1' );
		}
		return $location;
	}

	/**
	 * Finishes the Square connection process by saving the correct settings.
	 */
	public function finish_square_connect() {
		if (
			! Loader::is_admin_page() ||
			! isset( $_GET['square-connect-finish'] ) // phpcs:ignore CSRF ok.
		) {
			return;
		}

		if ( ! class_exists( '\Kkart\Square\Plugin' ) ) {
			return false;
		}

		$square = \Kkart\Square\Plugin::instance();

		// @todo This is a bit hacky but works. Ideally, kkart-square would contain a filter for us.
		add_filter( 'wp_redirect', array( $this, 'overwrite_square_redirect' ), 10, 2 );
		$square->get_connection_handler()->handle_connected();
		remove_filter( 'wp_redirect', array( $this, 'overwrite_square_redirect' ) );
	}

	/**
	 * Track changes to the onboarding option.
	 *
	 * @param mixed  $mixed Option name or previous value if option previously existed.
	 * @param string $value Value of the updated option.
	 */
	public static function track_onboarding_toggle( $mixed, $value ) {
		if ( defined( 'KKART_ADMIN_MIGRATING_OPTIONS' ) && KKART_ADMIN_MIGRATING_OPTIONS ) {
			return;
		};

		kkart_admin_record_tracks_event(
			'onboarding_toggled',
			array(
				'previous_value' => ! $value,
				'new_value'      => $value,
			)
		);
	}

	/**
	 * Update the help tab setup link to reset the onboarding profiler.
	 */
	public static function add_help_tab() {
		if ( ! function_exists( 'kkart_get_screen_ids' ) ) {
			return;
		}

		$screen = get_current_screen();

		if ( ! $screen || ! in_array( $screen->id, kkart_get_screen_ids(), true ) ) {
			return;
		}

		// Remove the old help tab if it exists.
		$help_tabs = $screen->get_help_tabs();
		foreach ( $help_tabs as $help_tab ) {
			if ( 'kkart_onboard_tab' !== $help_tab['id'] ) {
				continue;
			}

			$screen->remove_help_tab( 'kkart_onboard_tab' );
		}

		// Add the new help tab.
		$help_tab = array(
			'title' => __( 'Setup wizard', 'kkart' ),
			'id'    => 'kkart_onboard_tab',
		);

		$task_list_hidden = get_option( 'kkart_task_list_hidden', 'no' );

		$help_tab['content'] = '<h2>' . __( 'Kkart Onboarding', 'kkart' ) . '</h2>';

		$help_tab['content'] .= '<h3>' . __( 'Profile Setup Wizard', 'kkart' ) . '</h3>';
		$help_tab['content'] .= '<p>' . __( 'If you need to access the setup wizard again, please click on the button below.', 'kkart' ) . '</p>' .
			'<p><a href="' . kkart_admin_url( '&path=/setup-wizard' ) . '" class="button button-primary">' . __( 'Setup wizard', 'kkart' ) . '</a></p>';

		$help_tab['content'] .= '<h3>' . __( 'Task List', 'kkart' ) . '</h3>';
		$help_tab['content'] .= '<p>' . __( 'If you need to enable or disable the task list, please click on the button below.', 'kkart' ) . '</p>' .
		( 'yes' === $task_list_hidden
			? '<p><a href="' . kkart_admin_url( '&reset_task_list=1' ) . '" class="button button-primary">' . __( 'Enable', 'kkart' ) . '</a></p>'
			: '<p><a href="' . kkart_admin_url( '&reset_task_list=0' ) . '" class="button button-primary">' . __( 'Disable', 'kkart' ) . '</a></p>'
		);

		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
			$help_tab['content'] .= '<h3>' . __( 'Calypso / WordPress.com', 'kkart' ) . '</h3>';
			if ( class_exists( 'Jetpack' ) ) {
				$help_tab['content'] .= '<p>' . __( 'Quickly access the Jetpack connection flow in Calypso.', 'kkart' ) . '</p>';
				$help_tab['content'] .= '<p><a href="' . kkart_admin_url( '&test_kkart_jetpack_connect=1' ) . '" class="button button-primary">' . __( 'Connect', 'kkart' ) . '</a></p>';
			}

			$help_tab['content'] .= '<p>' . __( 'Quickly access the Kkart.com connection flow in Calypso.', 'kkart' ) . '</p>';
			$help_tab['content'] .= '<p><a href="' . kkart_admin_url( '&test_kkart_helper_connect=1' ) . '" class="button button-primary">' . __( 'Connect', 'kkart' ) . '</a></p>';
		}

		$screen->add_help_tab( $help_tab );
	}

	/**
	 * Allows quick access to testing the calypso parts of onboarding.
	 */
	public static function calypso_tests() {
		$calypso_env = defined( 'KKART_CALYPSO_ENVIRONMENT' ) && in_array( KKART_CALYPSO_ENVIRONMENT, array( 'development', 'wpcalypso', 'horizon', 'stage' ), true ) ? KKART_CALYPSO_ENVIRONMENT : 'production';

		if ( Loader::is_admin_page() && class_exists( 'Jetpack' ) && isset( $_GET['test_kkart_jetpack_connect'] ) && 1 === absint( $_GET['test_kkart_jetpack_connect'] ) ) { // phpcs:ignore CSRF ok.
			$redirect_url = esc_url_raw(
				add_query_arg(
					array(
						'page' => 'kkart-admin',
					),
					admin_url( 'admin.php' )
				)
			);

			$connect_url = \Jetpack::init()->build_connect_url( true, $redirect_url, 'kkart-onboarding' );
			$connect_url = add_query_arg( array( 'calypso_env' => $calypso_env ), $connect_url );

			wp_redirect( $connect_url );
			exit;
		}

		if ( Loader::is_admin_page() && isset( $_GET['test_kkart_helper_connect'] ) && 1 === absint( $_GET['test_kkart_helper_connect'] ) ) { // phpcs:ignore CSRF ok.
			include_once KKART_ABSPATH . 'includes/admin/helper/class-kkart-helper-api.php';

			$redirect_uri = kkart_admin_url( '&task=connect&wccom-connected=1' );

			$request = \KKART_Helper_API::post(
				'oauth/request_token',
				array(
					'body' => array(
						'home_url'     => home_url(),
						'redirect_uri' => $redirect_uri,
					),
				)
			);

			$code = wp_remote_retrieve_response_code( $request );
			if ( 200 !== $code ) {
				wp_die( esc_html__( 'Kkart Helper was not able to connect to Kkart.com.', 'kkart' ) );
				exit;
			}

			$secret = json_decode( wp_remote_retrieve_body( $request ) );
			if ( empty( $secret ) ) {
				wp_die( esc_html__( 'Kkart Helper was not able to connect to Kkart.com.', 'kkart' ) );
				exit;
			}

			$connect_url = add_query_arg(
				array(
					'home_url'     => rawurlencode( home_url() ),
					'redirect_uri' => rawurlencode( $redirect_uri ),
					'secret'       => rawurlencode( $secret ),
					'wccom-from'   => 'onboarding',
				),
				\KKART_Helper_API::url( 'oauth/authorize' )
			);

			$connect_url = add_query_arg( array( 'calypso_env' => $calypso_env ), $connect_url );

			wp_redirect( $connect_url );
			exit;
		}
	}

	/**
	 * Reset the onboarding profiler and redirect to the profiler.
	 */
	public static function reset_profiler() {
		if (
			! Loader::is_admin_page() ||
			! isset( $_GET['reset_profiler'] ) // phpcs:ignore CSRF ok.
		) {
			return;
		}

		$previous  = 1 === absint( $_GET['reset_profiler'] ); // phpcs:ignore CSRF ok.
		$new_value = ! $previous;

		kkart_admin_record_tracks_event(
			'storeprofiler_toggled',
			array(
				'previous'  => $previous,
				'new_value' => $new_value,
			)
		);

		$request = new \WP_REST_Request( 'POST', '/kkart-admin/onboarding/profile' );
		$request->set_headers( array( 'content-type' => 'application/json' ) );
		$request->set_body(
			wp_json_encode(
				array(
					'completed' => $new_value,
					'skipped'   => $new_value,
				)
			)
		);
		$response = rest_do_request( $request );
		wp_safe_redirect( kkart_admin_url() );
		exit;
	}

	/**
	 * Reset the onboarding task list and redirect to the dashboard.
	 */
	public static function reset_task_list() {
		if (
			! Loader::is_admin_page() ||
			! isset( $_GET['reset_task_list'] ) // phpcs:ignore CSRF ok.
		) {
			return;
		}

		$task_list_hidden = 1 === absint( $_GET['reset_task_list'] ) ? 'no' : 'yes'; // phpcs:ignore CSRF ok.
		update_option( 'kkart_task_list_hidden', $task_list_hidden );

		kkart_admin_record_tracks_event(
			'tasklist_toggled',
			array(
				'status' => 'yes' === $task_list_hidden ? 'disabled' : 'enabled',
			)
		);
		wp_safe_redirect( kkart_admin_url() );
		exit;
	}

	/**
	 * Remove the install notice that prompts the user to visit the old onboarding setup wizard.
	 *
	 * @param bool   $show Show or hide the notice.
	 * @param string $notice The slug of the notice.
	 * @return bool
	 */
	public static function remove_install_notice( $show, $notice ) {
		if ( 'install' === $notice ) {
			return false;
		}

		return $show;
	}

	/**
	 * Redirects the user to the task list if the task list is enabled and finishing a wccom checkout.
	 *
	 * @todo Once URL params are added to the redirect, we can check those instead of the referer.
	 */
	public static function redirect_wccom_install() {
		if (
			! self::should_show_tasks() ||
			! isset( $_SERVER['HTTP_REFERER'] ) ||
			0 !== strpos( $_SERVER['HTTP_REFERER'], 'https://kkart.com/checkout' ) // phpcs:ignore sanitization ok.
		) {
			return;
		}

		wp_safe_redirect( kkart_admin_url() );
	}

	/**
	 * When updating Kkart, mark the profiler and task list complete.
	 *
	 * @todo The `maybe_enable_setup_wizard()` method should be revamped on onboarding enable in core.
	 * See https://github.com/kkart/kkart/blob/1ca791f8f2325fe2ee0947b9c47e6a4627366374/includes/class-kkart-install.php#L341
	 */
	public static function maybe_mark_complete() {
		// The install notice still exists so don't complete the profiler.
		if ( ! class_exists( 'KKART_Admin_Notices' ) || \KKART_Admin_Notices::has_notice( 'install' ) ) {
			return;
		}

		$onboarding_data = get_option( self::PROFILE_DATA_OPTION, array() );
		// Don't make updates if the profiler is completed, but task list is potentially incomplete.
		if ( isset( $onboarding_data['completed'] ) && $onboarding_data['completed'] ) {
			return;
		}

		$onboarding_data['completed'] = true;
		update_option( self::PROFILE_DATA_OPTION, $onboarding_data );
		update_option( 'kkart_task_list_hidden', 'yes' );
	}
}
