<?php
/**
 * ConvesioPay Accelerated Checkout Element.
 *
 * Single-purpose class that owns every front-end concern for the accelerated
 * checkout (Apple Pay / Google Pay) feature:
 *
 *   1. Script enqueue  — loads the accelerated-checkout JS only on the pages
 *                        that actually display the buttons (product, cart,
 *                        checkout).  The unified gateway loads on all pages;
 *                        this class scopes the load correctly.
 *
 *   2. Script data     — supplies window.peachpay_convesiopay_unified_data via
 *                        wp_add_inline_script( 'before' ) so the merge happens
 *                        AFTER the unified gateway's wp_footer priority-1 inline
 *                        script and BEFORE the accelerated-checkout script tag.
 *
 *                        Execution order in the footer (guaranteed by WP):
 *                          ① wp_footer priority 1 → gateway sets the full data
 *                             object (fee_config, active_methods, convesio_config…)
 *                          ② wp_print_scripts (priority 20):
 *                             a. 'before' inline: merges element's slim data on
 *                                top of the gateway data — gateway-only keys are
 *                                preserved, element keys take precedence.
 *                             b. <script src="accelerated-checkout.js"> runs.
 *
 *                        Using wp_localize_script here would output a bare
 *                        `var peachpay_convesiopay_unified_data = {...}` that
 *                        OVERWRITES the gateway's object and loses fee_config
 *                        etc., so we intentionally avoid it.
 *
 *   3. HTML containers — emits mount-point divs via the same three WC template
 *                        hooks that Stripe's accelerated checkout element uses.
 *
 * @package PeachPay
 */

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

/**
 * Manages the front-end lifecycle of the ConvesioPay accelerated checkout.
 */
class PeachPay_ConvesioPay_Accelerated_Checkout_Element {

	/**
	 * Script handle used for enqueue and inline-script attachment.
	 *
	 * @var string
	 */
	const SCRIPT_HANDLE = 'peachpay-convesiopay-accelerated-checkout';

	/**
	 * Tracks which container IDs have already been output on this request.
	 *
	 * Static so deduplication works even if multiple class instances exist.
	 * This is essential because we register two hooks for the product page
	 * (primary + fallback) and must only emit the div once.
	 *
	 * @var array<string>
	 */
	private static $rendered = array();

	// -------------------------------------------------------------------------
	// Boot
	// -------------------------------------------------------------------------

	/**
	 * Register all front-end hooks.
	 *
	 * Early guards keep hooks off admin pages, REST requests, and stores where
	 * ConvesioPay is not yet connected.
	 *
	 * Hook priorities match Stripe's WC_Stripe_Express_Checkout_Element:
	 *   woocommerce_after_add_to_cart_form           priority 1  (product page)
	 *   woocommerce_proceed_to_checkout              priority 20 (cart page)
	 *   woocommerce_checkout_before_customer_details priority 1  (checkout page)
	 */
	public function init() {
		if ( ! PeachPay_ConvesioPay_Integration::connected() ) {
			return;
		}

		if ( is_admin() || ( defined( 'REST_REQUEST' ) && REST_REQUEST ) ) {
			return;
		}

		add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );

		// ── Product page ──────────────────────────────────────────────────────
		// Primary: fires right after the Add to Cart form in standard WC templates.
		add_action( 'woocommerce_after_add_to_cart_form', array( $this, 'display_accelerated_checkout_button_html' ), 1 );
		// Fallback: fires after the whole product summary block (lives in
		// single-product.php, not the add-to-cart sub-template).  Many premium
		// themes override add-to-cart templates without calling the primary hook,
		// but almost none override single-product.php itself.  The static
		// $rendered guard in the callback ensures only one div is ever emitted.
		add_action( 'woocommerce_after_single_product_summary', array( $this, 'display_accelerated_checkout_button_html' ), 1 );

		// ── Cart page ─────────────────────────────────────────────────────────
		// Primary: fires inside cart/proceed-to-checkout-button.php, right at the
		// checkout button — ideal placement, but this sub-template is commonly
		// overridden by premium themes without calling the hook.
		add_action( 'woocommerce_proceed_to_checkout', array( $this, 'display_accelerated_checkout_button_html' ), 20 );
		// Fallback: fires in cart/cart-totals.php immediately after the order total
		// row, one line before woocommerce_proceed_to_checkout — effectively the
		// same position.  cart-totals.php is far less commonly overridden.
		add_action( 'woocommerce_cart_totals_after_order_total', array( $this, 'display_accelerated_checkout_button_html' ), 1 );

		// ── Checkout page ─────────────────────────────────────────────────────
		add_action( 'woocommerce_checkout_before_customer_details', array( $this, 'display_accelerated_checkout_button_html' ), 1 );
	}

	// -------------------------------------------------------------------------
	// Script enqueue + data
	// -------------------------------------------------------------------------

	/**
	 * Enqueue the accelerated-checkout script and supply its configuration data.
	 *
	 * Runs only on product, cart, and checkout pages — keeping the script off
	 * every other page load unlike the unified gateway which loads on all pages.
	 *
	 * Data is injected via wp_add_inline_script( 'before' ) rather than
	 * wp_localize_script.  See class-level docblock for the full timing
	 * explanation of why this matters.
	 */
	public function enqueue_scripts() {
		if ( ! is_product() && ! is_cart() && ! is_checkout() ) {
			return;
		}

		// The ConvesioPay SDK must be available as a dependency.  The unified
		// gateway registers it on every page; on pages where the gateway is not
		// available we register it here as a safe fallback.
		if ( ! wp_script_is( 'convesiopay-sdk', 'registered' ) && ! wp_script_is( 'convesiopay-sdk', 'enqueued' ) ) {
			wp_register_script(
				'convesiopay-sdk',
				'https://js.convesiopay.com/v1/index.js',
				array(),
				'1.0',
				true
			);
		}

		wp_enqueue_script(
			self::SCRIPT_HANDLE,
			plugins_url( 'core/payments/convesiopay/assets/js/convesiopay-accelerated-checkout.js', PEACHPAY_PLUGIN_FILE ),
			array( 'convesiopay-sdk' ),
			'1.8.0',
			true
		);

		// Merge our accelerated-specific data on top of whatever the unified
		// gateway has already placed in window.peachpay_convesiopay_unified_data
		// (fee_config, active_methods, convesio_config, cart_total…).
		//
		// Object.assign({}, existing, elementData):
		//   • Keys only in existing  → preserved  (fee_config, active_methods…)
		//   • Keys only in element   → added       (routes, nonce, accel settings)
		//   • Keys in both           → element wins (accelerated_checkout settings
		//                              with computed allowed_countries override)
		$element_data_json = wp_json_encode( $this->build_script_data() );

		wp_add_inline_script(
			self::SCRIPT_HANDLE,
			'(function(){
				var existing = window.peachpay_convesiopay_unified_data || {};
				window.peachpay_convesiopay_unified_data = Object.assign( {}, existing, ' . $element_data_json . ' );
			})();',
			'before'
		);
	}

	/**
	 * Build the accelerated-checkout-specific slice of the data object.
	 *
	 * Includes the gateway-level keys that accelerated checkout needs to
	 * initialize so wallet buttons are not coupled to native checkout availability.
	 * If the unified gateway also emits data, Object.assign preserves the rest.
	 *
	 * @return array
	 */
	private function build_script_data() {
		$config = PeachPay_ConvesioPay_Integration::config();

		$defaults = array(
			'applepay_enabled'  => 'yes',
			'googlepay_enabled' => 'yes',
			'checkout_enabled'  => 'yes',
			'cart_enabled'      => 'no',
			'product_enabled'   => 'no',
		);

		$saved = (array) get_option( 'peachpay_convesiopay_accelerated_checkout_settings', array() );

		$computed = array(
			// Derive allowed countries from WC shipping zones so stores outside
			// US / CA / GB receive wallet buttons without manual configuration.
			'allowed_countries' => array_values( array_unique( array_filter( array_map(
				'strtoupper',
				array_keys( WC()->countries ? WC()->countries->get_shipping_countries() : array() )
			) ) ) ),
		);

		// Pass the current product's type to JS so canRender() can gate on it
		// without needing an extra AJAX call.  Only populated on single-product
		// pages; null everywhere else.
		$product_type = null;
		if ( is_product() ) {
			$product = wc_get_product( get_the_ID() );
			if ( $product instanceof WC_Abstract_Product ) {
				$product_type = $product->get_type();
			}
		}

		return array(
			'integration_name'              => $config['integration_name'] ?? '',
			'store_name'                    => get_bloginfo( 'name' ),
			'store_url'                     => (string) wp_parse_url( get_site_url(), PHP_URL_HOST ) ?: get_site_url(),
			'checkout_theme'                => get_option( 'peachpay_convesiopay_checkout_theme', 'light' ),
			'convesiopay_config'            => array(
				'apiKey'    => $config['api_key'] ?? '',
				'clientKey' => $config['client_key'] ?? '',
				'api_url'   => $config['api_url'] ?? '',
			),
			'accelerated_checkout'          => array_merge( $defaults, $saved, $computed ),
			'accelerated_checkout_route'    => rest_url( 'peachpay/v1/convesiopay/accelerated-checkout/create-order' ),
			'accelerated_recalculate_route' => rest_url( 'peachpay/v1/convesiopay/accelerated-checkout/recalculate' ),
			'rest_nonce'                    => wp_create_nonce( 'wp_rest' ),
			'product_type'                  => $product_type,
		);
	}

	// -------------------------------------------------------------------------
	// HTML containers
	// -------------------------------------------------------------------------

	/**
	 * Output the accelerated checkout mount-point container for the current page.
	 *
	 * Each supported context gets a uniquely identified, initially-empty <div>.
	 * The JS detects the correct container by ID and mounts the wallet buttons
	 * inside it — no fragile querySelector heuristics required.
	 *
	 * Container IDs:
	 *   Product page  → #pp-accelerated-checkout-product
	 *   Cart page     → #pp-accelerated-checkout-cart
	 *   Checkout page → #pp-accelerated-checkout-checkout
	 *
	 * @return void
	 */
	public function display_accelerated_checkout_button_html() {
		if ( is_product() ) {
			// Skip the container entirely for product types that are never supported
			// on the product page — no point mounting the component only to hide it.
			// 'composite' products need component selections we cannot capture.
			// 'grouped' and 'external' products are not purchasable via express checkout.
			$unsupported_on_product_page = array( 'grouped', 'external', 'composite' );
			$product = wc_get_product( get_the_ID() );
			if ( $product instanceof WC_Abstract_Product && in_array( $product->get_type(), $unsupported_on_product_page, true ) ) {
				return;
			}
			$element_id = 'pp-accelerated-checkout-product';
		} elseif ( is_cart() ) {
			$element_id = 'pp-accelerated-checkout-cart';
		} elseif ( is_checkout() && ! is_wc_endpoint_url( 'order-received' ) ) {
			$element_id = 'pp-accelerated-checkout-checkout';
		} else {
			return;
		}

		// Guard against duplicate output when multiple hooks are registered for
		// the same context (e.g. woocommerce_after_add_to_cart_form + fallback
		// woocommerce_after_single_product_summary on the product page).
		if ( in_array( $element_id, self::$rendered, true ) ) {
			return;
		}
		self::$rendered[] = $element_id;

		echo '<div id="' . esc_attr( $element_id ) . '" class="pp-accelerated-checkout-element" style="margin-top:1em;clear:both;"></div>';
	}
}
