<?php
/**
 * Class WC_Payments_WooPay_Button_Handler
 * Adds support for the WooPay express checkout button.
 *
 * Borrowed heavily from the WC_Payments_Payment_Request_Button_Handler class.
 *
 * @package WooCommerce\Payments
 */

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

// TODO: Not sure which of these are needed yet.
use WCPay\Exceptions\Invalid_Price_Exception;
use WCPay\Logger;
use WCPay\Payment_Information;
use WCPay\WooPay\WooPay_Session;
use WCPay\WooPay\WooPay_Utilities;

/**
 * WC_Payments_WooPay_Button_Handler class.
 */
class WC_Payments_WooPay_Button_Handler {
	/**
	 * WC_Payments_Account instance to get information about the account
	 *
	 * @var WC_Payments_Account
	 */
	private $account;

	/**
	 * WC_Payment_Gateway_WCPay instance.
	 *
	 * @var WC_Payment_Gateway_WCPay
	 */
	private $gateway;

	/**
	 * WooPay_Utilities instance.
	 *
	 * @var WooPay_Utilities
	 */
	private $woopay_utilities;

	/**
	 * Express Checkout Helper instance.
	 *
	 * @var WC_Payments_Express_Checkout_Button_Helper
	 */
	private $express_checkout_helper;

	/**
	 * Initialize class actions.
	 *
	 * @param WC_Payments_Account                        $account Account information.
	 * @param WC_Payment_Gateway_WCPay                   $gateway WCPay gateway.
	 * @param WooPay_Utilities                           $woopay_utilities WCPay gateway.
	 * @param WC_Payments_Express_Checkout_Button_Helper $express_checkout_helper Express checkout helper.
	 */
	public function __construct( WC_Payments_Account $account, WC_Payment_Gateway_WCPay $gateway, WooPay_Utilities $woopay_utilities, WC_Payments_Express_Checkout_Button_Helper $express_checkout_helper ) {
		$this->account                 = $account;
		$this->gateway                 = $gateway;
		$this->woopay_utilities        = $woopay_utilities;
		$this->express_checkout_helper = $express_checkout_helper;
	}

	/**
	 * Indicates eligibility for WooPay via feature flag.
	 *
	 * @var bool
	 */
	private $is_woopay_eligible;

	/**
	 * Indicates whether WooPay is enabled.
	 *
	 * @var bool
	 */
	private $is_woopay_enabled;

	/**
	 * Indicates whether WooPay express checkout is enabled.
	 *
	 * @var bool
	 */
	private $is_woopay_express_button_enabled;

	/**
	 * Indicates whether WooPay and WooPay express checkout are enabled.
	 *
	 * @return bool
	 */
	public function is_woopay_enabled() {
		/**
		 * Allows 3PD to programmatically show/hide the WooPay button.
		 *
		 * @since 9.5.0
		 */
		return apply_filters( 'wcpay_woopay_enabled', $this->is_woopay_eligible && $this->is_woopay_enabled && $this->is_woopay_express_button_enabled );
	}

	/**
	 * Initialize hooks.
	 *
	 * @return  void
	 */
	public function init() {
		// Checks if WCPay is enabled.
		if ( ! $this->gateway->is_enabled() ) {
			return;
		}

		// Checks if WooPay is enabled.
		$this->is_woopay_eligible               = WC_Payments_Features::is_woopay_eligible(); // Feature flag.
		$this->is_woopay_enabled                = 'yes' === $this->gateway->get_option( 'platform_checkout', 'no' );
		$this->is_woopay_express_button_enabled = WC_Payments_Features::is_woopay_express_checkout_enabled();

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

		// Don't load for change payment method page.
		if ( isset( $_GET['change_payment_method'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
			return;
		}

		add_action( 'wp_enqueue_scripts', [ $this, 'scripts' ] );

		add_filter( 'wcpay_payment_fields_js_config', [ $this, 'add_woopay_config' ] );

		add_action( 'wp_ajax_woopay_express_checkout_button_show_error_notice', [ $this, 'show_error_notice' ] );
		add_action( 'wp_ajax_nopriv_woopay_express_checkout_button_show_error_notice', [ $this, 'show_error_notice' ] );
	}

	/**
	 * Add the woopay button config to wcpay_config.
	 *
	 * @param array $config The existing config array.
	 *
	 * @return array The modified config array.
	 */
	public function add_woopay_config( $config ) {
		$user = wp_get_current_user();

		$config['woopayButton']             = $this->get_button_settings();
		$config['woopayButtonNonce']        = wp_create_nonce( 'woopay_button_nonce' );
		$config['addToCartNonce']           = wp_create_nonce( 'wcpay-add-to-cart' );
		$config['shouldShowWooPayButton']   = $this->should_show_woopay_button();
		$config['woopaySessionEmail']       = WooPay_Session::get_user_email( $user );
		$config['woopayIsCountryAvailable'] = $this->woopay_utilities->is_country_available( $this->gateway );
		$config['woopayAppearance']         = $this->gateway->is_woopay_global_theme_support_enabled()
			? WC_Payments_Styles_Cache::get_woopay_appearance()
			: null;
		$config['woopayFontRules']          = $this->gateway->is_woopay_global_theme_support_enabled()
			? WC_Payments_Styles_Cache::get_woopay_font_rules()
			: [];

		return $config;
	}

	/**
	 * Load public scripts and styles.
	 */
	public function scripts() {
		// Don't load scripts if we should not show the button.
		if ( ! $this->should_show_woopay_button() ) {
			return;
		}

		WC_Payments::register_script_with_dependencies( 'WCPAY_WOOPAY_EXPRESS_BUTTON', 'dist/woopay-express-button' );

		$wcpay_config = rawurlencode( wp_json_encode( WC_Payments::get_wc_payments_checkout()->get_payment_fields_js_config() ) );

		wp_add_inline_script(
			'WCPAY_WOOPAY_EXPRESS_BUTTON',
			"
			var wcpayConfig = wcpayConfig || JSON.parse( decodeURIComponent( '" . esc_js( $wcpay_config ) . "' ) );
			",
			'before'
		);

		wp_set_script_translations( 'WCPAY_WOOPAY_EXPRESS_BUTTON', 'woocommerce-payments' );

		wp_enqueue_script( 'WCPAY_WOOPAY_EXPRESS_BUTTON' );

		WC_Payments_Utils::enqueue_style(
			'WCPAY_WOOPAY',
			plugins_url( 'dist/woopay.css', WCPAY_PLUGIN_FILE ),
			[],
			WCPAY_VERSION_NUMBER,
			'all'
		);
	}

	/**
	 * Returns the error notice HTML.
	 */
	public function show_error_notice() {
		$is_nonce_valid = check_ajax_referer( 'woopay_button_nonce', false, false );

		if ( ! $is_nonce_valid ) {
			wp_send_json_error(
				__( 'You aren’t authorized to do that.', 'woocommerce-payments' ),
				403
			);
		}

		$message = isset( $_POST['message'] ) ? sanitize_text_field( wp_unslash( $_POST['message'] ) ) : '';

		// $message has already been translated.
		wc_add_notice( $message, 'error' );
		$notice = wc_print_notices( true );

		wp_send_json_success(
			[
				'notice' => $notice,
			]
		);

		wp_die();
	}

	/**
	 * The settings for the `button` attribute - they depend on the "grouped settings" flag value.
	 *
	 * @return array
	 */
	public function get_button_settings() {
		$common_settings        = $this->express_checkout_helper->get_common_button_settings();
		$woopay_button_settings = [
			'size'    => $this->gateway->get_option( 'payment_request_button_size' ),
			'context' => $this->express_checkout_helper->get_button_context(),
		];

		return array_merge( $common_settings, $woopay_button_settings );
	}

	/**
	 * Checks whether Payment Request Button should be available on this page.
	 *
	 * @return bool
	 */
	public function should_show_woopay_button() {
		// WCPay is not available.
		$gateways = WC()->payment_gateways->get_available_payment_gateways();
		if ( ! isset( $gateways['woocommerce_payments'] ) ) {
			return false;
		}

		// WooPay is not enabled.
		if ( ! $this->is_woopay_enabled() ) {
			return false;
		}

		// Page not supported.
		if ( ! $this->express_checkout_helper->is_product() && ! $this->express_checkout_helper->is_cart() && ! $this->express_checkout_helper->is_checkout() ) {
			return false;
		}

		// Check if WooPay is available in the user country.
		if ( ! $this->woopay_utilities->is_country_available( $this->gateway ) ) {
			return false;
		}

		// Product page, but not available in settings.
		if ( $this->express_checkout_helper->is_product() && ! $this->express_checkout_helper->is_express_checkout_method_enabled_at( 'product', 'woopay' ) ) {
			return false;
		}

		// Checkout page, but not available in settings.
		if ( $this->express_checkout_helper->is_checkout() && ! $this->express_checkout_helper->is_express_checkout_method_enabled_at( 'checkout', 'woopay' ) ) {
			return false;
		}

		// Cart page, but not available in settings.
		if ( $this->express_checkout_helper->is_cart() && ! $this->express_checkout_helper->is_express_checkout_method_enabled_at( 'cart', 'woopay' ) ) {
			return false;
		}

		// Product page, but has unsupported product type.
		if ( $this->express_checkout_helper->is_product() && ! $this->is_product_supported() ) {
			Logger::log( 'Product page has unsupported product type ( WooPay Express button disabled )' );
			return false;
		}

		// Cart has unsupported product type.
		if ( ( $this->express_checkout_helper->is_checkout() || $this->express_checkout_helper->is_cart() ) && ! $this->has_allowed_items_in_cart() ) {
			Logger::log( 'Items in the cart have unsupported product type ( WooPay Express button disabled )' );
			return false;
		}

		if ( ! is_user_logged_in() ) {
			// On product page for a subscription product, but not logged in, making WooPay unavailable.
			if ( $this->express_checkout_helper->is_product() ) {
				$current_product = wc_get_product();

				if ( $current_product && $this->express_checkout_helper->is_product_subscription( $current_product ) ) {
					return false;
				}
			}

			// On cart or checkout page with a subscription product in cart, but not logged in, making WooPay unavailable.
			if ( ( $this->express_checkout_helper->is_checkout() || $this->express_checkout_helper->is_cart() ) && class_exists( 'WC_Subscriptions_Cart' ) && WC_Subscriptions_Cart::cart_contains_subscription() ) {
				// Check cart for subscription products.
				return false;
			}

			// If guest checkout is not allowed, and customer is not logged in, disable the WooPay button.
			if ( ! $this->woopay_utilities->is_guest_checkout_enabled() ) {
				return false;
			}
		}

		/**
		 * TODO: We need to do some research here and see if there are any product types that we
		 * absolutely cannot support with WooPay at this time. There are some examples in the
		 * `WC_Payments_Payment_Request_Button_Handler->is_product_supported()` method.
		 */

		return true;
	}

	/**
	 * Display the payment request button.
	 */
	public function display_woopay_button_html() {
		if ( ! $this->should_show_woopay_button() ) {
			return;
		}

		$settings = $this->get_button_settings();

		// Use a <div> placeholder instead of <button>. The Add to Cart + Options block
		// scans hooked content for form elements (BUTTON, INPUT, etc.) to decide between
		// Interactivity API mode and legacy form-submit mode. A <button> here forces
		// legacy mode, which breaks the mini-cart drawer. The React component hydrates
		// the #wcpay-woopay-button container regardless of the placeholder element type,
		// and .woopay-express-button CSS fully styles the element, so <div> is visually
		// identical.
		?>
		<div id="wcpay-woopay-button" data-product_page=<?php echo esc_attr( $this->express_checkout_helper->is_product() ); ?>>
			<div
				class="woopay-express-button is-placeholder"
				aria-label="<?php esc_attr_e( 'WooPay', 'woocommerce-payments' ); ?>"
				data-type="<?php echo esc_attr( $settings['type'] ); ?>"
				data-theme="<?php echo esc_attr( $settings['theme'] ); ?>"
				data-size="<?php echo esc_attr( $settings['size'] ); ?>"
				style="height: <?php echo esc_attr( $settings['height'] ); ?>px; border-radius: <?php echo esc_attr( $settings['radius'] ); ?>px"
			></div>
		</div>
		<?php
	}

	/**
	 * Whether the product page has a product compatible with the WooPay Express button.
	 *
	 * @return boolean
	 */
	private function is_product_supported() {
		$product      = $this->express_checkout_helper->get_product();
		$is_supported = true;

		if ( ! is_object( $product ) ) {
			$is_supported = false;
		}

		// External/affiliate products are not supported.
		if ( is_a( $product, 'WC_Product' ) && $product->is_type( 'external' ) ) {
			$is_supported = false;
		}

		// Pre Orders products to be charged upon release are not supported.
		if (
			class_exists( 'WC_Pre_Orders_Product' ) &&
			method_exists( 'WC_Pre_Orders_Product', 'product_is_charged_upon_release' ) &&
			WC_Pre_Orders_Product::product_is_charged_upon_release( $product )
		) {
			$is_supported = false;
		}

		// WC Bookings require confirmation products are not supported.
		if (
			is_a( $product, 'WC_Product_Booking' ) &&
			method_exists( $product, 'get_requires_confirmation' ) &&
			$product->get_requires_confirmation()
		) {
			$is_supported = false;
		}

		/**
		 * Filters whether the WooPay Express button supports the given product.
		 *
		 * @since 5.9.0
		 *
		 * @param bool                   $is_supported Whether the product is supported by the WooPay Express button.
		 * @param WC_Product|false|null  $product      The product being checked, or false/null when none could be resolved.
		 */
		return apply_filters( 'wcpay_woopay_button_is_product_supported', $is_supported, $product );
	}

	/**
	 * Checks the cart to see if the WooPay Express button supports all of its items.
	 *
	 * @todo Abstract this. This is a copy of the same method in the `WC_Payments_Payment_Request_Button_Handler` class.
	 *
	 * @return boolean
	 */
	private function has_allowed_items_in_cart() {
		$is_supported = true;

		// We don't support pre-order products to be paid upon release.
		if ( class_exists( 'WC_Pre_Orders_Cart' ) && class_exists( 'WC_Pre_Orders_Product' ) ) {
			if (
				WC_Pre_Orders_Cart::cart_contains_pre_order() &&
				WC_Pre_Orders_Product::product_is_charged_upon_release( WC_Pre_Orders_Cart::get_pre_order_product() )
			) {
				$is_supported = false;
			}
		}

		/**
		 * Filters whether the WooPay Express button supports all of the items currently in the cart.
		 *
		 * @since 5.7.0
		 *
		 * @param bool $is_supported Whether all cart items are supported by the WooPay Express button.
		 */
		return apply_filters( 'wcpay_platform_checkout_button_are_cart_items_supported', $is_supported );
	}
}
