<?php

namespace Automattic\WCShipping\Connect;

use Automattic\WCShipping\LabelPurchase\ViewService;
use Jetpack_Connection_Banner;
use Automattic\WCShipping\Utils;
use WP_Screen;

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

class WC_Connect_Nux {
	/**
	 * Jetpack status constants.
	 */
	const JETPACK_NOT_CONNECTED = 'not-connected';
	const JETPACK_DEV           = 'dev';
	const JETPACK_CONNECTED     = 'connected';

	/**
	 * Option name for dismissing success banner
	 * after the JP connection flow
	 */
	const SHOULD_SHOW_AFTER_CXN_BANNER = 'should_display_nux_after_jp_cxn_banner';

	const TOS_ACCEPTED = 'tos_accepted';

	/**
	 * The GET parameter used to recognize returns after successful connection authorization.
	 */
	const AUTH_SUCCESS_SOURCE_RETURN_PARAM = 'wcshipping-auth-successful';

	/**
	 * The name of the nonce GET parameter after successful connection authorization.
	 */
	const AUTH_SUCCESS_NONCE_RETURN_PARAM = '_wcshipping-auth-successful-nonce';

	/**
	 * The name of the nonce action used to recognize successful connection authorization.
	 */
	const AUTH_SUCCESS_NONCE_ACTION = 'wcshipping_auth_successful';

	/**
	 * @var ViewService
	 */
	private $view_service;

	/**
	 * WC_Connect_Nux constructor.
	 *
	 * @param ViewService $view_service
	 *
	 * @return void
	 */
	function __construct( ViewService $view_service ) {
		$this->view_service = $view_service;

		add_action( 'admin_enqueue_scripts', array( $this, 'load_scripts_and_styles' ) );
	}

	private function get_notice_states() {
		$states = get_user_meta( get_current_user_id(), 'wcshipping_nux_notices', true );

		if ( ! is_array( $states ) ) {
			return array();
		}

		return $states;
	}

	public function is_notice_dismissed( $notice ) {
		$notices = $this->get_notice_states();

		return isset( $notices[ $notice ] ) && $notices[ $notice ];
	}

	public function dismiss_notice( $notice ) {
		$notices            = $this->get_notice_states();
		$notices[ $notice ] = true;
		update_user_meta( get_current_user_id(), 'wcshipping_nux_notices', $notices );
	}

	public function ajax_dismiss_notice() {
		if ( empty( $_POST['dismissible_id'] ) ) {
			return;
		}

		check_ajax_referer( 'wcshipping_dismiss_notice', 'nonce' );
		$this->dismiss_notice( sanitize_key( $_POST['dismissible_id'] ) );
		wp_die();
	}

	/**
	 * Check that the current user is the owner of the Jetpack connection
	 * - Only that person can accept the TOS
	 *
	 * @return bool
	 * @uses self::get_jetpack_install_status()
	 */
	public function can_accept_tos() {
		$jetpack_status = $this->get_jetpack_install_status();

		// Developer case
		if ( self::JETPACK_DEV === $jetpack_status ) {
			return true;
		}

		$can_accept = WC_Connect_Jetpack::is_current_user_connected();

		return $can_accept;
	}

	public static function get_banner_type_to_display( $status = array() ) {
		if ( ! isset( $status['jetpack_connection_status'] ) ) {
			return false;
		}

		/*
		The NUX Flow:
		- Case 1: Jetpack not connected (with TOS or no TOS accepted):
			1. show_banner_before_connection()
			2. connect to JP
			3. show_banner_after_connection(), which sets the TOS acceptance in options
		- Case 2: Jetpack connected, no TOS
			1. show_tos_only_banner(), which accepts TOS on button click
		- Case 3: Jetpack connected, and TOS accepted
			This is an existing user. Do nothing.
		*/
		switch ( $status['jetpack_connection_status'] ) {
			case self::JETPACK_NOT_CONNECTED:
				return 'before_jetpack_connection';
			case self::JETPACK_CONNECTED:
			case self::JETPACK_DEV:
				// Has the user just gone through our NUX connection flow?
				if ( isset( $status['should_display_after_cxn_banner'] ) && $status['should_display_after_cxn_banner'] ) {
					return 'after_jetpack_connection';
				}

				// Has the user already accepted our TOS? Then do nothing.
				// Note: TOS is accepted during the after_connection banner
				if (
					isset( $status[ self::TOS_ACCEPTED ] )
					&& ! $status[ self::TOS_ACCEPTED ]
					&& isset( $status['can_accept_tos'] )
					&& $status['can_accept_tos']
				) {
					return 'tos_only_banner';
				}

				return false;
			default:
				return false;
		}
	}

	public function get_jetpack_install_status() {
		$status = self::JETPACK_CONNECTED;

		if ( defined( 'JETPACK_DEV_DEBUG' ) && true === JETPACK_DEV_DEBUG ) {
			// activated, and dev mode on
			$status = self::JETPACK_DEV;
		}
		// dev mode off, check if connected.
		elseif ( ! WC_Connect_Jetpack::is_connected() ) {
			$status = self::JETPACK_NOT_CONNECTED;
		}

		/**
		 * Filter the derived Jetpack install/connection status used by NUX checks.
		 *
		 * Internal hook used by WooCommerce Shipping test/bootstrap infrastructure.
		 * It is not designed for third-party plugin integrations and may change or
		 * be removed in future releases without notice.
		 *
		 * @internal
		 *
		 * @param string $status Calculated Jetpack status.
		 */
		$filtered_status = apply_filters( 'wcshipping_jetpack_install_status', $status );
		if ( ! is_string( $filtered_status ) ) {
			return $status;
		}

		$allowed_statuses = array(
			self::JETPACK_NOT_CONNECTED,
			self::JETPACK_DEV,
			self::JETPACK_CONNECTED,
		);

		if ( ! in_array( $filtered_status, $allowed_statuses, true ) ) {
			return $status;
		}

		return $filtered_status;
	}

	public function should_display_nux_notice_on_screen( $screen ) {
		if ( // Display if on any of these admin pages.
			( // Products list.
				'product' === $screen->post_type
				&& 'edit' === $screen->base
			)
			|| ( // Orders list.
				'shop_order' === $screen->post_type
				&& 'edit' === $screen->base
			)
			|| ( // Edit order page.
				'shop_order' === $screen->post_type
				&& 'post' === $screen->base
			)
			|| ( // Orders list && Edit order page ( HPOS ).
				'woocommerce_page_wc-orders' === $screen->base
			)
			|| ( // WooCommerce settings.
				'woocommerce_page_wc-settings' === $screen->base
			)
			|| ( // WooCommerce featured extension page.
				'woocommerce_page_wc-addons' === $screen->base
				&& isset( $_GET['section'] ) && 'featured' === sanitize_text_field( wp_unslash( $_GET['section'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended --- Ignoring this as no DB operation
			)
			|| ( // WooCommerce shipping extension page.
				'woocommerce_page_wc-addons' === $screen->base
				&& isset( $_GET['section'] ) && 'shipping_methods' === sanitize_text_field( wp_unslash( $_GET['section'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended --- Ignoring this as no DB operation
			)
			|| 'plugins' === $screen->base
		) {
			return true;
		}

		return false;
	}

	/**
	 * Check if the current page is the WC Shipping settings page.
	 *
	 * @param WP_Screen|null $current_screen The object that represents the current page.
	 * @return bool
	 */
	public function is_wcshipping_settings_page( ?WP_Screen $current_screen ) {
		if ( ! $current_screen instanceof WP_Screen ) {
			$current_screen = get_current_screen();
		}

		$settings_section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if (
			'woocommerce_page_wc-settings' === $current_screen->base
			&& 'woocommerce-shipping-settings' === $settings_section
		) {
			return true;
		}

		return false;
	}

	public function get_feature_list_for_country( $country ) {
		$feature_list    = false;
		$supports_labels = ( 'US' === $country );

		$is_ppec_active    = is_plugin_active( 'woocommerce-gateway-paypal-express-checkout/woocommerce-gateway-paypal-express-checkout.php' );
		$ppec_settings     = get_option( 'woocommerce_ppec_paypal_settings', array() );
		$supports_payments = $is_ppec_active && ( ! isset( $ppec_settings['enabled'] ) || 'yes' === $ppec_settings['enabled'] );

		if ( $supports_payments && $supports_labels ) {
			$feature_list = __( 'shipping label printing, and smoother payment setup', 'woocommerce-shipping' );
		} elseif ( $supports_payments ) {
			$feature_list = __( 'smoother payment setup', 'woocommerce-shipping' );
		} elseif ( $supports_labels ) {
			$feature_list = __( 'shipping label printing', 'woocommerce-shipping' );
		} elseif ( $supports_payments && $supports_labels ) {
			$feature_list = __( 'shipping label printing and smoother payment setup', 'woocommerce-shipping' );
		} elseif ( $supports_payments ) {
			$feature_list = __( 'smoother payment setup', 'woocommerce-shipping' );
		} elseif ( $supports_labels ) {
			$feature_list = __( 'shipping label printing', 'woocommerce-shipping' );
		}

		return $feature_list;
	}

	public function get_jetpack_redirect_url() {
		$full_path = add_query_arg( array() );
		// Remove [...]/wp-admin so we can use admin_url().
		$new_index = strpos( $full_path, '/wp-admin' ) + strlen( '/wp-admin' );
		$path      = substr( $full_path, $new_index );

		return esc_url( admin_url( $path ) );
	}

	public static function is_tos_accepted() {
		return WC_Connect_Options::get_option( self::TOS_ACCEPTED );
	}

	/**
	 * Register Terms of Service acceptance.
	 *
	 * @param string $source Where the acceptance originates from.
	 * @return void
	 */
	public static function accept_tos( $source = '' ) {
		if ( self::is_tos_accepted() ) {
			/**
			 * Fires if the user has already accepted our ToS.
			 *
			 * @since 1.0.5
			 *
			 * @param string $source The source of the ToS acceptance.
			 */
			do_action( 'wcshipping_tos_already_accepted', $source );

			return;
		}

		WC_Connect_Options::update_option( self::TOS_ACCEPTED, true );

		/**
		 * Fires when the user accepts our ToS.
		 *
		 * @since 1.0.0
		 *
		 * @param string $source The source of the ToS acceptance.
		 */
		do_action( 'wcshipping_tos_accepted', $source );
	}

	public function set_up_nux_notices() {
		if ( ! current_user_can( 'manage_woocommerce' ) ) {
			return;
		}

		// Check for plugin install and activate permissions to handle Jetpack on multisites:
		// Admins might not be able to install or activate plugins, but Jetpack might already have been installed by a superadmin.
		// If this is the case, the admin can connect the site on their own, and should be able to use WCS as ususal
		$jetpack_install_status = $this->get_jetpack_install_status();

		$banner_to_display = self::get_banner_type_to_display(
			array(
				'jetpack_connection_status'       => $jetpack_install_status,
				'tos_accepted'                    => self::is_tos_accepted(),
				'can_accept_tos'                  => $this->can_accept_tos(),
				'should_display_after_cxn_banner' => WC_Connect_Options::get_option( self::SHOULD_SHOW_AFTER_CXN_BANNER ),
			)
		);

		switch ( $banner_to_display ) {
			case 'before_jetpack_connection':
				wp_enqueue_style( 'wcshipping_connect_banner' );

				add_action(
					'admin_post_register_wcshipping_jetpack',
					array( $this, 'register_wcshipping_jetpack' )
				);

				add_action( 'admin_notices', array( $this, 'show_banner_before_connection' ), 9 );
				break;
			case 'tos_only_banner':
				wp_enqueue_style( 'wcshipping_connect_banner' );
				add_action( 'admin_notices', array( $this, 'show_tos_banner' ) );
				break;
			case 'after_jetpack_connection':
				wp_enqueue_style( 'wcshipping_connect_banner' );
				add_action( 'admin_notices', array( $this, 'show_banner_after_connection' ) );
				break;
		}

		$this->register_callback_listeners();

		add_action( 'wp_ajax_wcshipping_dismiss_notice', array( $this, 'ajax_dismiss_notice' ) );
	}

	/**
	 * Register callback listeners.
	 *
	 * @return void
	 */
	private function register_callback_listeners() {
		if (
			isset( $_GET[ self::AUTH_SUCCESS_NONCE_RETURN_PARAM ] )
			&& wp_verify_nonce( sanitize_key( wp_unslash( $_GET[ self::AUTH_SUCCESS_NONCE_RETURN_PARAM ] ) ), self::AUTH_SUCCESS_NONCE_ACTION )
			&& isset( $_GET[ self::AUTH_SUCCESS_SOURCE_RETURN_PARAM ] )
		) {
			$source = sanitize_text_field( wp_unslash( $_GET[ self::AUTH_SUCCESS_SOURCE_RETURN_PARAM ] ) );

			$allowed_sources = array(
				'connection_banner',
				'onboarding-connect-button',
			);

			if ( ! in_array( $source, $allowed_sources, true ) ) {
				return;
			}

			/**
			 * Fires when we have a successful authorization return.
			 *
			 * @since 1.0.5
			 *
			 * @param string $source The source of the return value.
			 */
			do_action( 'wcshipping_wpcom_connect_site_connected', $source );
		}
	}

	public function show_banner_before_connection() {
		if ( ! $this->view_service->is_store_eligible_for_shipping_label_creation() ) {
			return;
		}

		$current_screen = get_current_screen();
		if ( ! $this->should_display_nux_notice_on_screen( $current_screen ) ) {
			return;
		}

		// The settings page itself will display a separate component with the same purpose,
		// so there is no need for us to show duplicate messages.
		if ( $this->is_wcshipping_settings_page( $current_screen ) ) {
			return;
		}

		// Remove Jetpack's connect banners since we're showing our own.
		if ( class_exists( 'Jetpack_Connection_Banner' ) ) {
			$jetpack_banner = Jetpack_Connection_Banner::init();

			remove_action( 'admin_notices', array( $jetpack_banner, 'render_banner' ) );
			remove_action( 'admin_notices', array( $jetpack_banner, 'render_connect_prompt_full_screen' ) );
		}

		// Make sure that we wait until the button is clicked before displaying
		// the after_connection banner.
		WC_Connect_Options::delete_option( self::SHOULD_SHOW_AFTER_CXN_BANNER );

		$country = WC()->countries->get_base_country();
		/* translators: %s: list of features, potentially comma separated */
		$description_base = __( "Connect your store to activate <strong>WooCommerce Shipping</strong>. Once you connect your store to WordPress.com, you'll have access to %s.", 'woocommerce-shipping' );
		$feature_list     = $this->get_feature_list_for_country( $country );
		$banner_content   = array(
			'source'            => 'banner_before_connection',
			'description'       => sprintf( $description_base, $feature_list ),
			'button_text'       => __( 'Connect', 'woocommerce-shipping' ),
			'should_show_jp'    => true,
			'should_show_terms' => true,
		);

		$this->show_nux_banner( $banner_content );
	}

	public function show_banner_after_connection() {
		if ( ! $this->view_service->is_store_eligible_for_shipping_label_creation() ) {
			return;
		}

		if ( ! $this->should_display_nux_notice_on_screen( get_current_screen() ) ) {
			return;
		}

		// Did the user just dismiss?
		if (
			isset( $_GET['wcshipping-nux-notice'] )
			&& isset( $_GET['_wpnonce'] )
			&& check_admin_referer( 'wcshipping_dismiss_notice' )
			&& 'dismiss' === sanitize_text_field( wp_unslash( $_GET['wcshipping-nux-notice'] ) ) ) {
			// No longer need to keep track of whether the before connection banner was displayed.
			WC_Connect_Options::delete_option( self::SHOULD_SHOW_AFTER_CXN_BANNER );

			/**
			 * Fires when the user dismisses the setup complete banner.
			 *
			 * @since 1.0.0
			 */
			do_action( 'wcshipping_setup_complete_banner_dismissed' );

			wp_safe_redirect( remove_query_arg( array( '_wpnonce', 'wcshipping-nux-notice' ) ) );
			exit;
		}

		$country = WC()->countries->get_base_country();
		/* translators: %s: list of features, potentially comma separated */
		$description_base = __( 'You can now enjoy %s.', 'woocommerce-shipping' );
		$feature_list     = $this->get_feature_list_for_country( $country );

		$this->show_nux_banner(
			array(
				'source'            => 'banner_after_connection',
				'description'       => sprintf( $description_base, $feature_list ),
				'button_text'       => __( 'Got it, thanks!', 'woocommerce-shipping' ),
				'button_link'       => add_query_arg(
					array(
						// The source differs because we're registering that we had a successful return initiated by
						// the "connection_banner" source aka "banner_before_connection" banner.
					self::AUTH_SUCCESS_SOURCE_RETURN_PARAM => 'connection_banner',
						self::AUTH_SUCCESS_NONCE_RETURN_PARAM => wp_create_nonce( self::AUTH_SUCCESS_NONCE_ACTION ),
						'wcshipping-nux-notice'            => 'dismiss',
						'_wpnonce'                         => wp_create_nonce( 'wcshipping_dismiss_notice' ),
					)
				),
				'should_show_jp'    => false,
				'should_show_terms' => false,
			)
		);
	}

	public function show_tos_banner() {
		if ( ! $this->view_service->is_store_eligible_for_shipping_label_creation() ) {
			return;
		}

		$current_screen = get_current_screen();
		if ( ! $this->should_display_nux_notice_on_screen( $current_screen ) ) {
			return;
		}

		// The settings page itself will display a separate component with the same purpose,
		// so there is no need for us to show duplicate messages.
		if ( $this->is_wcshipping_settings_page( $current_screen ) ) {
			return;
		}

		$source = 'tos_banner';

		if (
			isset( $_GET['wcshipping-nux-tos'] )
			&& isset( $_GET['_wpnonce'] )
			&& check_admin_referer( 'wcshipping_accepted_tos' )
			&& 'accept' === sanitize_text_field( wp_unslash( $_GET['wcshipping-nux-tos'] ) ) ) {
			// Make sure we queue up the "after_jetpack_connection" banner if ToS was accepted.
			// We normally queue this before a Jetpack connection is attempted, but since the
			// connection already exists for ToS only banners, then we need to register it here
			// as well to ensure a coherent user-experience.
			WC_Connect_Options::update_option( self::SHOULD_SHOW_AFTER_CXN_BANNER, true );

			self::accept_tos( $source );

			wp_safe_redirect( remove_query_arg( array( '_wpnonce', 'wc-nux-tos' ) ) );
			exit;
		}

		$country = WC()->countries->get_base_country();
		/* translators: %s: list of features, potentially comma separated */
		$description_base = __( "Connect your store to activate <strong>WooCommerce Shipping</strong>. Once you connect your store to WordPress.com, you'll have access to %s.", 'woocommerce-shipping' );
		$feature_list     = $this->get_feature_list_for_country( $country );

		$this->show_nux_banner(
			array(
				'source'            => $source,
				'description'       => sprintf( $description_base, $feature_list ),
				'button_text'       => __( 'Connect', 'woocommerce-shipping' ),
				'button_link'       => add_query_arg(
					array(
						'wcshipping-nux-tos' => 'accept',
						'_wpnonce'           => wp_create_nonce( 'wcshipping_accepted_tos' ),
					)
				),
				'should_show_jp'    => false,
				'should_show_terms' => true,
			)
		);
	}

	public function show_nux_banner( $content ) {
		if ( isset( $content['dismissible_id'] ) && $this->is_notice_dismissed( sanitize_key( $content['dismissible_id'] ) ) ) {
			return;
		}

		if ( isset( $content['source'] ) ) {
			/**
			 * Track when a banner is shown.
			 *
			 * @since 1.0.5
			 *
			 * @param string $source The source aka what type of banner we are displaying.
			 * @return void
			 */
			do_action( 'wcshipping_show_banner', $content['source'] );
		}

		$allowed_html = array(
			'a'      => array( 'href' => array() ),
			'strong' => array(),
			'b'      => array(),
			'br'     => array(),
			'em'     => array(),
			'i'      => array(),
		);

		?>
		<div class="notice wcshipping-nux__notice notice-<?php echo WC_Connect_Jetpack::is_connected() && self::is_tos_accepted() ? 'success' : 'warning'; ?> <?php echo isset( $content['dismissible_id'] ) ? 'is-dismissible' : ''; ?>"
			data-dismissible-id="<?php echo isset( $content['dismissible_id'] ) ? esc_attr( $content['dismissible_id'] ) : ''; ?>">
			<div class="wcshipping-nux__notice-content">
				<p>
					<?php
					echo wp_kses( $content['description'], $allowed_html );
					?>
				</p>
				<?php if ( isset( $content['should_show_terms'] ) && $content['should_show_terms'] ) : ?>
					<p class="wcshipping-nux__notice-content-tos">
						<?php
						/* translators: %1$s example values include "Install Jetpack and CONNECT >", "Activate Jetpack and CONNECT >", "CONNECT >" */
						printf(
							wp_kses(
								// translators: %1$s: "Terms of Service", %2$s: "https://wordpress.com/tos/", %3$s: "https://automattic.com/privacy/"
								__( 'By clicking "%1$s", you agree to the <a href="%2$s">Terms of Service</a> and have read our <a href="%3$s">Privacy Policy</a>', 'woocommerce-shipping' ),
								$allowed_html
							),
							esc_html( $content['button_text'] ),
							'https://wordpress.com/tos/',
							'https://automattic.com/privacy/'
						);
						?>
					</p>
				<?php endif; ?>

			</div>
			<div class="wcshipping-nux__notice-button">

				<?php if ( isset( $content['button_link'] ) ) : ?>
					<a class="button button-primary" href="<?php echo esc_url( $content['button_link'] ); ?>">
						<?php echo esc_html( $content['button_text'] ); ?>
					</a>
				<?php else : ?>
					<form action="<?php echo esc_attr( admin_url( 'admin-post.php' ) ); ?>" method="post">
						<input type="hidden" name="action" value="register_wcshipping_jetpack" />
						<input type="hidden" name="redirect_url"
							value="<?php echo esc_url( $this->get_jetpack_redirect_url() ); ?>" />
						<?php wp_nonce_field( 'wcshipping_nux_notice' ); ?>
						<button
							class="woocommerce-shipping__connect-jetpack wcshipping-nux__notice-content-button button button-primary"
							onclick="this.className += ' disabled';">
							<?php echo esc_html( $content['button_text'] ); ?>
						</button>
					</form>
				<?php endif; ?>
			</div>
		</div>
		<?php
		if ( isset( $content['dismissible_id'] ) ) :
			wp_enqueue_script( 'wcshipping-connect-banner' );
		endif;
	}

	/**
	 * Get Jetpack connection URL.
	 */
	public function register_wcshipping_jetpack() {
		check_ajax_referer( 'wcshipping_nux_notice' );

		$redirect_url = '';
		if ( isset( $_POST['redirect_url'] ) ) {
			$redirect_url = esc_url_raw( wp_unslash( $_POST['redirect_url'] ) );
		}

		$source = 'connection_banner';

		// Clicking the "Connect" button is enough acceptance of ToS that we can
		// register it now, independent of what happens with the connection itself.
		self::accept_tos( $source );

		// Make sure we always display the after-connection banner
		// after the before_connection button is clicked.
		WC_Connect_Options::update_option( self::SHOULD_SHOW_AFTER_CXN_BANNER, true );

		WC_Connect_Jetpack::connect_site( $redirect_url, $source );
	}

	/**
	 * Load scripts and styles.
	 *
	 * @return void
	 */
	public function load_scripts_and_styles() {
		wp_register_style(
			'wcshipping_connect_banner',
			WCSHIPPING_STYLESHEETS_URL . 'connect-banner.css',
			array(),
			Utils::get_file_version( WCSHIPPING_STYLESHEETS_DIR . 'connect-banner.css' )
		);
		wp_register_script(
			'wcshipping-connect-banner',
			WCSHIPPING_JAVASCRIPT_URL . 'wc-connect-nux.js',
			array( 'jquery', 'wp-util' ),
			Utils::get_file_version( WCSHIPPING_JAVASCRIPT_DIR . 'wc-connect-nux.js' ),
			array( 'in_footer' => true )
		);
		$banner_info = wp_json_encode(
			array(
				'ajaxurl' => admin_url( 'admin-ajax.php' ),
				'nonce'   => wp_create_nonce( 'wcshipping_dismiss_notice' ),
			)
		);
		wp_add_inline_script(
			'wcshipping-connect-banner',
			"var wcShippingConnectBanner = Object.assign( {}, wcShippingConnectBanner, $banner_info );",
			'before'
		);
	}
}