<?php
/**
 * WooCommerce Braintree Gateway
 *
 * This source file is subject to the GNU General Public License v3.0
 * that is bundled with this package in the file license.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl-3.0.html
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@woocommerce.com so we can send you a copy immediately.
 *
 * DISCLAIMER
 *
 * Do not edit or add to this file if you wish to upgrade WooCommerce Braintree Gateway to newer
 * versions in the future. If you wish to customize WooCommerce Braintree Gateway for your
 * needs please refer to http://docs.woocommerce.com/document/braintree/
 *
 * @package   WC-Braintree/Buttons
 * @author    WooCommerce
 * @copyright Copyright (c) 2016-2025, Automattic, Inc.
 * @license   http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
 */

namespace WC_Braintree\Venmo\Buttons;

use SkyVerge\WooCommerce\PluginFramework\v6_0_1 as Framework;

defined( 'ABSPATH' ) || exit;

/**
 * Abstract Venmo button class.
 *
 * @since 3.5.0
 */
abstract class Abstract_Button extends Framework\Handlers\Script_Handler {


	/**
	 * Venmo gateway instance.
	 *
	 * @var \WC_Braintree\WC_Gateway_Braintree_Venmo
	 */
	protected $gateway;

	/**
	 * Tracks whether a cancel link has been rendered from any button
	 *
	 * @var bool
	 */
	protected static $rendered_cancel_link = false;


	/**
	 * Constructs the button handler.
	 *
	 * @since 3.5.0
	 *
	 * @param \WC_Braintree\WC_Gateway_Braintree_Venmo $gateway the gateway instance.
	 */
	public function __construct( $gateway ) {

		$this->gateway = $gateway;

		parent::__construct();
	}


	/**
	 * Checks if this button should be enabled or not.
	 *
	 * @since 3.5.0
	 *
	 * @return bool
	 */
	protected function is_enabled() {
		// override in concrete classes.
		return false;
	}


	/**
	 * Adds the action and filter hooks.
	 *
	 * @since 3.5.0
	 */
	protected function add_hooks() {

		if ( $this->is_enabled() ) {

			parent::add_hooks();

			$this->add_button_hooks();
		}
	}


	/**
	 * Adds any actions and filters needed for the button, if the button is enabled.
	 *
	 * @since 3.5.0
	 */
	protected function add_button_hooks() {
		// handle the Venmo token generated by Braintree JS.
		add_action( 'woocommerce_api_' . stripslashes( strtolower( get_class( $this->get_gateway() ) ) ), [ $this, 'handle_wc_api' ] );

		// disable all other gateways at checkout when confirming payment.
		add_action( 'woocommerce_available_payment_gateways', [ $this, 'disable_other_gateways' ] );

		// add styles for the cart & confirmation page.
		add_action( 'wp_head', [ $this, 'enqueue_styles' ] );

		// add a "Cancel" link below the order button at checkout confirmation.
		add_action( 'woocommerce_review_order_after_submit', [ $this, 'render_cancel_link' ] );

		// clear the session data when the cart is emptied.
		add_action( 'woocommerce_cart_emptied', [ $this, 'clear_session_data' ] );
	}


	/**
	 * Enqueues button-specific scripts.
	 *
	 * @since 3.5.0
	 */
	protected function enqueue_scripts() {
		$this->gateway->register_gateway_assets();

		// Enqueue Braintree Venmo SDK.
		wp_enqueue_script( 'braintree-js-venmo', 'https://js.braintreegateway.com/web/' . \WC_Braintree\WC_Braintree::BRAINTREE_JS_SDK_VERSION . '/js/venmo.min.js', array( 'braintree-js-client' ), \WC_Braintree\WC_Braintree::VERSION, true );
		wp_enqueue_script( 'braintree-js-data-collector' );
	}


	/**
	 * Renders the button markup and JS.
	 *
	 * @since 3.5.0
	 */
	public function render() {

		$available_gateways = WC()->payment_gateways->get_available_payment_gateways();

		if ( ! isset( $available_gateways['braintree_venmo'] ) ) {
			return;
		}

		$this->clear_session_data();
		$this->render_button();
		$this->render_js();
	}

	/**
	 * Handles the request made to the WC API after the JS has generated a token.
	 *
	 * @since 3.5.0
	 */
	public function handle_wc_api() {

		if ( $this->is_wc_api_request_valid() ) {
			$this->process_wc_api_request();
		}
	}


	/**
	 * Validates a WC API request.
	 *
	 * @since 3.5.0
	 *
	 * @return bool
	 */
	abstract protected function is_wc_api_request_valid();


	/**
	 * Processes a WC API request after the JS has generated a token.
	 *
	 * @since 3.5.0
	 */
	protected function process_wc_api_request() {
		// phpcs:disable WordPress.Security.NonceVerification.Missing

		if ( ! WC()->session ) {
			wp_send_json_error( __( 'Session not available', 'woocommerce-gateway-paypal-powered-by-braintree' ) );
			return;
		}

		WC()->session->set( 'wc_braintree_venmo_cart_nonce', Framework\SV_WC_Helper::get_posted_value( 'nonce' ) );

		if ( ! empty( $_POST['device_data'] ) ) {
			WC()->session->set( 'wc_braintree_venmo_device_data', wc_clean( wp_unslash( $_POST['device_data'] ) ) );
		}

		wp_send_json(
			[
				'redirect_url' => wc_get_checkout_url(),
			]
		);
		// phpcs:enable
	}


	/**
	 * Gets the JS handler arguments.
	 *
	 * @since 3.5.0
	 *
	 * @return array
	 */
	protected function get_js_handler_args() {

		$args = array_merge(
			[
				'id'                       => $this->get_gateway()->get_id(),
				'id_dasherized'            => $this->get_gateway()->get_id_dasherized(),
				'name'                     => $this->get_gateway()->get_method_title(),
				'debug'                    => $this->get_gateway()->debug_log(),
				'client_token_nonce'       => wp_create_nonce( 'wc_' . $this->get_gateway()->get_id() . '_get_client_token' ),
				'set_payment_method_nonce' => wp_create_nonce( 'wc_' . $this->get_gateway()->get_id() . '_cart_set_payment_method' ),
				'cart_handler_url'         => add_query_arg( 'wc-api', get_class( $this->get_gateway() ), home_url() ),
				'ajax_url'                 => admin_url( 'admin-ajax.php' ),
				'venmo_logo_svg_url'       => $this->get_gateway()->get_plugin()->get_plugin_url() . '/assets/images/white_venmo_logo.svg',
			],
			$this->get_additional_js_handler_params()
		);

		/**
		 * Filters the JS handler params.
		 *
		 * @since 3.5.0
		 *
		 * @param array params
		 * @param Abstract_Button button handler instance.
		 */
		return (array) apply_filters( 'wc_' . $this->get_gateway()->get_id() . '_button_js_handler_params', $args, $this );
	}

	/**
	 * Gets additional JS Handler params - Override in concrete classes.
	 *
	 * @since 3.5.0
	 *
	 * @return array
	 */
	protected function get_additional_js_handler_params() {

		return [];
	}


	/**
	 * Renders the Venmo button JS.
	 *
	 * @since 3.5.0
	 */
	protected function render_js() {

		ob_start();
		?>
		jQuery( function( $ ) {
		<?php echo $this->get_safe_handler_js(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- get_safe_handler_js returns safe JS ?>
		} );
		<?php
		$javascript = ob_get_clean();
		$handle     = 'wc-braintree-' . $this->get_id_dasherized() . '-inline';
		\WC_Braintree\WC_Braintree::enqueue_inline_script( $handle, $javascript );
	}


	/**
	 * Gets the handler instantiation JS.
	 *
	 * @since 3.5.0
	 *
	 * @param array  $additional_args additional handler arguments, if any.
	 * @param string $handler_name handler name, if different from self::get_js_handler_class_name().
	 * @param string $object_name object name, if different from self::get_js_handler_object_name().
	 * @return string
	 */
	protected function get_handler_js( array $additional_args = [], $handler_name = '', $object_name = '' ) {

		$js = parent::get_handler_js( $additional_args, $handler_name, $object_name );

		$js .= sprintf( 'window.%s.init();', $object_name ? $object_name : $this->get_js_handler_object_name() );

		return $js;
	}


	/**
	 * Renders the Venmo button markup.
	 *
	 * @since 3.5.0
	 */
	public function render_button() {

		?>

		<div id="<?php echo esc_attr( 'wc_' . $this->get_gateway()->get_id() . '_button_container' ); ?>" class="wc-braintree-venmo-button-wrapper"></div>

		<?php foreach ( $this->get_button_params() as $key => $value ) : ?>

			<input type="hidden"
				name="<?php echo esc_attr( 'wc_' . $this->get_gateway()->get_id() . '_' . $key ); ?>"
				value="<?php echo esc_attr( $value ); ?>"
			/>

			<?php
			endforeach;
	}


	/**
	 * Gets an array of params for rendering the button HTML.
	 *
	 * @since 3.5.0
	 *
	 * @return array
	 */
	protected function get_button_params() {

		/**
		 * Filters the markup params for a button.
		 *
		 * @since 3.5.0
		 *
		 * @param array params
		 * @param Abstract_Button button handler instance
		 */
		return (array) apply_filters(
			'wc_' . $this->get_gateway()->get_id() . '_button_markup_params',
			array_merge(
				[
					'amount'   => Framework\SV_WC_Helper::number_format( $this->get_button_total() ),
					'currency' => $this->get_button_currency(),
				],
				$this->get_additional_button_params()
			),
			$this
		);
	}


	/**
	 * Gets additional button params for the button markup - override in concrete classes.
	 *
	 * @since 3.5.0
	 *
	 * @return array
	 */
	protected function get_additional_button_params() {
		return [];
	}


	/**
	 * Gets the total amount the button should charge - override in concrete classes.
	 *
	 * @since 3.5.0
	 *
	 * @return float
	 */
	protected function get_button_total() {
		return 0.0;
	}


	/**
	 * Gets the currency to be used for the button.
	 *
	 * @since 3.5.0
	 *
	 * @return string
	 */
	protected function get_button_currency() {
		return get_woocommerce_currency();
	}


	/**
	 * Disables all other gateways at checkout when confirming payment.
	 *
	 * @since 3.5.0
	 * @param array $gateways Available gateways.
	 * @return array
	 */
	public function disable_other_gateways( $gateways ) {

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

		foreach ( $gateways as $id => $gateway ) {

			if ( $id !== $this->get_gateway()->get_id() ) {
				unset( $gateways[ $id ] );
			}
		}

		return $gateways;
	}


	/**
	 * Adds styles for the cart & confirmation page.
	 *
	 * @since 3.5.0
	 */
	public function enqueue_styles() {

		if ( ! is_cart() && ! $this->is_checkout_confirmation() ) {
			return;
		}

		?>

		<style>
			.payment_box.payment_method_braintree_venmo {
				padding: 0;
			}
			.payment_box.payment_method_braintree_venmo fieldset {
				display: none;
			}
			.wc-braintree-venmo-cancel {
				display: block;
				text-align: center;
				padding: 15px;
			}
		</style>

		<?php
	}


	/**
	 * Renders a "Cancel" link.
	 *
	 * @since 3.5.0
	 */
	public function render_cancel_link() {

		if ( self::$rendered_cancel_link || ! $this->is_checkout_confirmation() ) {
			return;
		}

		printf(
			'<a href="%1$s" class="wc-' . sanitize_html_class( $this->get_gateway()->get_id_dasherized() ) . '-cancel">%2$s</a>',
			esc_url( add_query_arg( [ 'wc_' . $this->get_gateway()->get_id() . '_clear_session' => true ], wc_get_cart_url() ) ),
			esc_html__( 'Cancel', 'woocommerce-gateway-paypal-powered-by-braintree' )
		);

		self::$rendered_cancel_link = true;
	}


	/**
	 * Determines if the current view is at Checkout, confirming the cart Venmo purchase.
	 *
	 * @since 3.5.0
	 *
	 * @return bool
	 */
	public function is_checkout_confirmation() {

		return $this->get_gateway()->get_payment_form_instance()->is_checkout_confirmation();
	}


	/**
	 * Gets the cart nonce from the session, if any.
	 *
	 * @since 3.5.0
	 *
	 * @return string
	 */
	public function get_cart_nonce() {

		return $this->get_gateway()->get_payment_form_instance()->get_cart_nonce();
	}


	/**
	 * Clears any Venmo cart session data.
	 *
	 * @since 3.5.0
	 */
	public function clear_session_data() {

		if ( ! WC()->session ) {
			return;
		}

		unset(
			WC()->session->wc_braintree_venmo_cart_nonce,
			WC()->session->wc_braintree_venmo_device_data
		);
	}


	/**
	 * Returns the gateway instance.
	 *
	 * @since 3.5.0
	 *
	 * @return \WC_Braintree\WC_Gateway_Braintree_Venmo
	 */
	protected function get_gateway() {
		return $this->gateway;
	}


	/**
	 * Adds a log entry.
	 *
	 * @since 3.5.0
	 *
	 * @param string $message message to log.
	 */
	protected function log_event( $message ) {

		$this->get_gateway()->add_debug_message( $message );
	}


	/**
	 * Determines whether logging is enabled.
	 *
	 * @since 3.5.0
	 *
	 * @return bool
	 */
	protected function is_logging_enabled() {

		return $this->get_gateway()->debug_log();
	}
}
