<?php
/**
 * PayPal Payment Gateway
 *
 * Handles PayPal payment processing via PayPal JS SDK.
 *
 * @package GGM_Commerce
 */

// If this file is called directly, abort.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
// phpcs:disable PluginCheck.Security.DirectDB.UnescapedDBParameter

// Load base class.
require_once dirname( __DIR__ ) . '/abstract-payment-gateway.php';

/**
 * Class GGM_Commerce_Gateway_PayPal
 *
 * Implements PayPal payment processing.
 */
class GGM_Commerce_Gateway_PayPal extends GGM_Commerce_Payment_Gateway {

	/**
	 * PayPal API version.
	 *
	 * @var string
	 */
	const API_VERSION = 'v2';

	/**
	 * PayPal API base URLs.
	 */
	const API_SANDBOX = 'https://api-m.sandbox.paypal.com';
	const API_LIVE    = 'https://api-m.paypal.com';

	/**
	 * Cached access token.
	 *
	 * @var string|null
	 */
	private $access_token = null;

	/**
	 * Token expiration timestamp.
	 *
	 * @var int
	 */
	private $token_expires = 0;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->id                     = 'paypal';
		$this->title                  = __( 'PayPal', 'ggm-commerce' );
		$this->description            = __( 'Pay securely with PayPal.', 'ggm-commerce' );
		$this->premium_component_slug = 'paypal-gateway';
		$this->supports               = array( 'paypal', 'pay_later', 'venmo', 'card' );

		parent::__construct();
	}

	/**
	 * Get gateway icon URL.
	 *
	 * @return string
	 */
	public function get_icon() {
		return plugins_url( 'assets/images/paypal-logo.svg', dirname( __DIR__, 3 ) . '/goose-commerce.php' );
	}

	/**
	 * Get settings fields for admin configuration.
	 *
	 * @return array
	 */
	public function get_settings_fields() {
		return array(
			'enabled'             => array(
				'title'   => __( 'Enable/Disable', 'ggm-commerce' ),
				'type'    => 'checkbox',
				'label'   => __( 'Enable PayPal payments', 'ggm-commerce' ),
				'default' => 'no',
			),
			'test_mode'           => array(
				'title'       => __( 'Sandbox Mode', 'ggm-commerce' ),
				'type'        => 'checkbox',
				'label'       => __( 'Enable PayPal sandbox for testing', 'ggm-commerce' ),
				'description' => __( 'Use PayPal sandbox credentials for development.', 'ggm-commerce' ),
				'default'     => 'yes',
			),
			'live_client_id'      => array(
				'title'       => __( 'Live Client ID', 'ggm-commerce' ),
				'type'        => 'text',
				'description' => __( 'Get this from your PayPal Developer Dashboard', 'ggm-commerce' ),
				'default'     => '',
			),
			'live_client_secret'  => array(
				'title'       => __( 'Live Secret', 'ggm-commerce' ),
				'type'        => 'password',
				'description' => __( 'Keep this secret! Never share it.', 'ggm-commerce' ),
				'default'     => '',
			),
			'test_client_id'      => array(
				'title'       => __( 'Sandbox Client ID', 'ggm-commerce' ),
				'type'        => 'text',
				'description' => __( 'PayPal sandbox client ID for testing.', 'ggm-commerce' ),
				'default'     => '',
			),
			'test_client_secret'  => array(
				'title'       => __( 'Sandbox Secret', 'ggm-commerce' ),
				'type'        => 'password',
				'description' => __( 'PayPal sandbox secret for testing.', 'ggm-commerce' ),
				'default'     => '',
			),
			'webhook_id'          => array(
				'title'       => __( 'Webhook ID', 'ggm-commerce' ),
				'type'        => 'text',
				'description' => sprintf(
					/* translators: %s: webhook URL */
					__( 'Create a webhook at PayPal Developer Dashboard. Endpoint URL: %s', 'ggm-commerce' ),
					'<code>' . rest_url( 'ggm-commerce/v1/webhooks/paypal' ) . '</code>'
				),
				'default'     => '',
			),
			'payment_methods'     => array(
				'title'   => __( 'Payment Methods', 'ggm-commerce' ),
				'type'    => 'section',
				'default' => '',
			),
			'enable_paypal'       => array(
				'title'   => __( 'PayPal', 'ggm-commerce' ),
				'type'    => 'checkbox',
				'label'   => __( 'Enable PayPal button', 'ggm-commerce' ),
				'default' => 'yes',
			),
			'enable_pay_later'    => array(
				'title'       => __( 'Pay Later', 'ggm-commerce' ),
				'type'        => 'checkbox',
				'label'       => __( 'Enable Pay Later options', 'ggm-commerce' ),
				'description' => __( 'Buy now, pay later financing options.', 'ggm-commerce' ),
				'default'     => 'yes',
			),
			'enable_venmo'        => array(
				'title'       => __( 'Venmo', 'ggm-commerce' ),
				'type'        => 'checkbox',
				'label'       => __( 'Enable Venmo', 'ggm-commerce' ),
				'description' => __( 'Available for US customers only.', 'ggm-commerce' ),
				'default'     => 'no',
			),
			'enable_card'         => array(
				'title'       => __( 'Debit/Credit Cards', 'ggm-commerce' ),
				'type'        => 'checkbox',
				'label'       => __( 'Enable card payments via PayPal', 'ggm-commerce' ),
				'description' => __( 'Allow customers to pay with cards through PayPal.', 'ggm-commerce' ),
				'default'     => 'yes',
			),
			'button_style'        => array(
				'title'   => __( 'Button Style', 'ggm-commerce' ),
				'type'    => 'section',
				'default' => '',
			),
			'button_layout'       => array(
				'title'   => __( 'Layout', 'ggm-commerce' ),
				'type'    => 'select',
				'options' => array(
					'vertical'   => __( 'Vertical (stacked)', 'ggm-commerce' ),
					'horizontal' => __( 'Horizontal (side by side)', 'ggm-commerce' ),
				),
				'default' => 'vertical',
			),
			'button_color'        => array(
				'title'   => __( 'Button Color', 'ggm-commerce' ),
				'type'    => 'select',
				'options' => array(
					'gold'   => __( 'Gold', 'ggm-commerce' ),
					'blue'   => __( 'Blue', 'ggm-commerce' ),
					'silver' => __( 'Silver', 'ggm-commerce' ),
					'white'  => __( 'White', 'ggm-commerce' ),
					'black'  => __( 'Black', 'ggm-commerce' ),
				),
				'default' => 'gold',
			),
			'button_shape'        => array(
				'title'   => __( 'Button Shape', 'ggm-commerce' ),
				'type'    => 'select',
				'options' => array(
					'rect' => __( 'Rectangle', 'ggm-commerce' ),
					'pill' => __( 'Pill (rounded)', 'ggm-commerce' ),
				),
				'default' => 'rect',
			),
			'button_label'        => array(
				'title'   => __( 'Button Label', 'ggm-commerce' ),
				'type'    => 'select',
				'options' => array(
					'paypal'   => __( 'PayPal', 'ggm-commerce' ),
					'checkout' => __( 'Checkout', 'ggm-commerce' ),
					'buynow'   => __( 'Buy Now', 'ggm-commerce' ),
					'pay'      => __( 'Pay', 'ggm-commerce' ),
				),
				'default' => 'paypal',
			),
		);
	}

	/**
	 * Check if gateway is properly configured.
	 *
	 * @return bool
	 */
	public function is_configured() {
		$client_id     = $this->get_client_id();
		$client_secret = $this->get_client_secret();

		return ! empty( $client_id ) && ! empty( $client_secret );
	}

	/**
	 * Get client ID based on mode.
	 *
	 * @return string
	 */
	public function get_client_id() {
		$prefix = $this->is_test_mode() ? 'test_' : 'live_';
		return $this->get_option( $prefix . 'client_id', '' );
	}

	/**
	 * Get client secret based on mode.
	 *
	 * @return string
	 */
	public function get_client_secret() {
		$prefix = $this->is_test_mode() ? 'test_' : 'live_';
		$secret = $this->get_option( $prefix . 'client_secret', '' );

		// Try to decrypt if it looks encrypted.
		if ( ! empty( $secret ) && strlen( $secret ) > 50 ) {
			$decrypted = $this->decrypt_key( $secret );
			if ( ! empty( $decrypted ) && $decrypted !== $secret ) {
				return $decrypted;
			}
		}

		return $secret;
	}

	/**
	 * Get API base URL.
	 *
	 * @return string
	 */
	private function get_api_base() {
		return $this->is_test_mode() ? self::API_SANDBOX : self::API_LIVE;
	}

	/**
	 * Get enabled funding sources for PayPal JS SDK.
	 *
	 * @return array
	 */
	public function get_enabled_funding() {
		$funding = array();

		if ( 'yes' === $this->get_option( 'enable_paypal', 'yes' ) ) {
			$funding[] = 'paypal';
		}

		if ( 'yes' === $this->get_option( 'enable_pay_later', 'yes' ) ) {
			$funding[] = 'paylater';
		}

		if ( 'yes' === $this->get_option( 'enable_venmo', 'no' ) ) {
			$funding[] = 'venmo';
		}

		if ( 'yes' === $this->get_option( 'enable_card', 'yes' ) ) {
			$funding[] = 'card';
		}

		return $funding;
	}

	/**
	 * Get disabled funding sources.
	 *
	 * @return array
	 */
	public function get_disabled_funding() {
		$disabled = array();

		if ( 'yes' !== $this->get_option( 'enable_pay_later', 'yes' ) ) {
			$disabled[] = 'paylater';
			$disabled[] = 'credit';
		}

		if ( 'yes' !== $this->get_option( 'enable_venmo', 'no' ) ) {
			$disabled[] = 'venmo';
		}

		if ( 'yes' !== $this->get_option( 'enable_card', 'yes' ) ) {
			$disabled[] = 'card';
		}

		return $disabled;
	}

	/**
	 * Get button style configuration.
	 *
	 * @return array
	 */
	public function get_button_style() {
		return array(
			'layout' => $this->get_option( 'button_layout', 'vertical' ),
			'color'  => $this->get_option( 'button_color', 'gold' ),
			'shape'  => $this->get_option( 'button_shape', 'rect' ),
			'label'  => $this->get_option( 'button_label', 'paypal' ),
		);
	}

	/**
	 * Get access token for API requests.
	 *
	 * @return string|false
	 */
	private function get_access_token() {
		// Return cached token if still valid.
		if ( $this->access_token && time() < $this->token_expires ) {
			return $this->access_token;
		}

		$client_id     = $this->get_client_id();
		$client_secret = $this->get_client_secret();

		if ( empty( $client_id ) || empty( $client_secret ) ) {
			return false;
		}

		$response = wp_remote_post(
			$this->get_api_base() . '/v1/oauth2/token',
			array(
				'headers' => array(
					'Authorization' => 'Basic ' . base64_encode( $client_id . ':' . $client_secret ),
					'Content-Type'  => 'application/x-www-form-urlencoded',
				),
				'body'    => 'grant_type=client_credentials',
				'timeout' => 60,
			)
		);

		if ( is_wp_error( $response ) ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging for PayPal API errors
				error_log( 'PayPal token error: ' . $response->get_error_message() );
			}
			return false;
		}

		$body = json_decode( wp_remote_retrieve_body( $response ), true );

		if ( empty( $body['access_token'] ) ) {
			if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging for PayPal API errors
				error_log( 'PayPal token error: ' . wp_json_encode( $body ) );
			}
			return false;
		}

		$this->access_token  = $body['access_token'];
		$this->token_expires = time() + ( intval( $body['expires_in'] ?? 3600 ) - 60 );

		return $this->access_token;
	}

	/**
	 * Create a PayPal order.
	 *
	 * @param int $order_id GGM Commerce order ID.
	 * @return array
	 */
	public function create_paypal_order( $order_id ) {
		$order = $this->get_order( $order_id );

		if ( ! $order ) {
			return $this->error_response( __( 'Order not found.', 'ggm-commerce' ), 'order_not_found' );
		}

		$currency = strtoupper( $this->get_currency() );
		$amount   = number_format( $order->total_amount, 2, '.', '' );

		$paypal_order = array(
			'intent'         => 'CAPTURE',
			'purchase_units' => array(
				array(
					'reference_id' => (string) $order_id,
					'description'  => sprintf(
						/* translators: %s: order number */
						__( 'Order %s', 'ggm-commerce' ),
						$order->order_number
					),
					'custom_id'    => (string) $order_id,
					'invoice_id'   => $order->order_number,
					'amount'       => array(
						'currency_code' => $currency,
						'value'         => $amount,
					),
				),
			),
			'application_context' => array(
				'brand_name'          => get_bloginfo( 'name' ),
				'shipping_preference' => 'NO_SHIPPING',
				'user_action'         => 'PAY_NOW',
			),
		);

		$response = $this->api_request( '/v2/checkout/orders', $paypal_order, 'POST' );

		if ( isset( $response['error'] ) ) {
			$this->log_payment_event(
				$order_id,
				'',
				'order.create_failed',
				$order->total_amount,
				$currency,
				'failed',
				$response
			);

			return $this->error_response(
				$response['error']['message'] ?? __( 'Failed to create PayPal order.', 'ggm-commerce' ),
				$response['error']['code'] ?? 'paypal_error'
			);
		}

		// Store PayPal order ID.
		global $wpdb;
		$wpdb->update(
			"{$wpdb->prefix}ggmc_orders",
			array(
				'payment_intent_id' => $response['id'],
				'payment_method'    => 'paypal',
			),
			array( 'id' => $order_id )
		);

		$this->log_payment_event(
			$order_id,
			$response['id'],
			'order.created',
			$order->total_amount,
			$currency,
			'created',
			$response
		);

		return $this->success_response(
			array(
				'paypal_order_id' => $response['id'],
			)
		);
	}

	/**
	 * Capture a PayPal order after approval.
	 *
	 * @param string $paypal_order_id PayPal order ID.
	 * @param int    $order_id        GGM Commerce order ID.
	 * @return array
	 */
	public function capture_paypal_order( $paypal_order_id, $order_id ) {
		$response = $this->api_request(
			'/v2/checkout/orders/' . $paypal_order_id . '/capture',
			array(),
			'POST'
		);

		if ( isset( $response['error'] ) ) {
			$this->update_order_payment_status(
				$order_id,
				'failed',
				'',
				$response['error']['message'] ?? __( 'Capture failed.', 'ggm-commerce' )
			);

			$this->log_payment_event(
				$order_id,
				$paypal_order_id,
				'order.capture_failed',
				0,
				'',
				'failed',
				$response
			);

			return $this->error_response(
				$response['error']['message'] ?? __( 'Failed to capture payment.', 'ggm-commerce' ),
				$response['error']['code'] ?? 'capture_error'
			);
		}

		if ( 'COMPLETED' === ( $response['status'] ?? '' ) ) {
			$capture        = $response['purchase_units'][0]['payments']['captures'][0] ?? array();
			$transaction_id = $capture['id'] ?? $paypal_order_id;
			$amount         = floatval( $capture['amount']['value'] ?? 0 );
			$currency       = $capture['amount']['currency_code'] ?? '';

			$this->update_order_payment_status( $order_id, 'paid', $transaction_id );

			$this->log_payment_event(
				$order_id,
				$transaction_id,
				'order.captured',
				$amount,
				$currency,
				'paid',
				$response
			);

			/**
			 * Fires when a PayPal payment is completed.
			 *
			 * @param int   $order_id Order ID.
			 * @param array $response PayPal response.
			 */
			do_action( 'ggm_commerce_paypal_payment_completed', $order_id, $response );

			return $this->success_response(
				array(
					'status'         => 'completed',
					'transaction_id' => $transaction_id,
				)
			);
		}

		return $this->error_response(
			__( 'Unexpected payment status.', 'ggm-commerce' ),
			'unexpected_status'
		);
	}

	/**
	 * Process payment for an order.
	 *
	 * @param int   $order_id     Order ID.
	 * @param array $payment_data Payment data.
	 * @return array
	 */
	public function process_payment( $order_id, $payment_data ) {
		// For PayPal, the flow is:
		// 1. Create PayPal order (this method or create_paypal_order)
		// 2. Customer approves on PayPal
		// 3. Capture the payment (capture_paypal_order)
		return $this->create_paypal_order( $order_id );
	}

	/**
	 * Handle webhook callback.
	 *
	 * @return array
	 */
	public function handle_webhook() {
		$payload = file_get_contents( 'php://input' );
		$headers = $this->get_webhook_headers();

		if ( empty( $payload ) ) {
			return $this->error_response( 'Missing payload', 'webhook_error' );
		}

		// Verify webhook signature.
		$webhook_id = $this->get_option( 'webhook_id', '' );
		if ( ! empty( $webhook_id ) && ! $this->verify_webhook( $headers, $payload ) ) {
			return $this->error_response( 'Invalid signature', 'webhook_signature_invalid' );
		}

		$event = json_decode( $payload, true );

		if ( json_last_error() !== JSON_ERROR_NONE ) {
			return $this->error_response( 'Invalid JSON payload', 'webhook_invalid_json' );
		}

		$event_type = $event['event_type'] ?? '';
		$resource   = $event['resource'] ?? array();

		switch ( $event_type ) {
			case 'CHECKOUT.ORDER.APPROVED':
				// Order was approved but not yet captured.
				// Usually we capture immediately via client-side.
				break;

			case 'PAYMENT.CAPTURE.COMPLETED':
				$this->webhook_capture_completed( $resource );
				break;

			case 'PAYMENT.CAPTURE.DENIED':
			case 'PAYMENT.CAPTURE.DECLINED':
				$this->webhook_capture_failed( $resource );
				break;

			case 'PAYMENT.CAPTURE.REFUNDED':
				$this->webhook_capture_refunded( $resource );
				break;

			// PayPal Billing Subscription webhooks.
			case 'BILLING.SUBSCRIPTION.ACTIVATED':
				$this->webhook_subscription_activated( $resource );
				break;

			case 'BILLING.SUBSCRIPTION.CANCELLED':
				$this->webhook_subscription_cancelled( $resource );
				break;

			case 'BILLING.SUBSCRIPTION.SUSPENDED':
				$this->webhook_subscription_suspended( $resource );
				break;

			case 'BILLING.SUBSCRIPTION.PAYMENT.FAILED':
				$this->webhook_subscription_payment_failed( $resource );
				break;

			case 'PAYMENT.SALE.COMPLETED':
				// Check if this is a subscription renewal payment.
				$billing_agreement_id = $resource['billing_agreement_id'] ?? '';
				if ( ! empty( $billing_agreement_id ) ) {
					$this->webhook_subscription_payment_completed( $resource );
				}
				break;

			default:
				if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
					// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging for unhandled webhook events
					error_log( 'GGM Commerce PayPal: Unhandled webhook event: ' . $event_type );
				}
				break;
		}

		return $this->success_response( array( 'received' => true ) );
	}

	/**
	 * Get webhook headers.
	 *
	 * @return array
	 */
	private function get_webhook_headers() {
		$headers = array();

		foreach ( $_SERVER as $key => $value ) {
			if ( strpos( $key, 'HTTP_PAYPAL_' ) === 0 ) {
				$header_name             = str_replace( 'HTTP_PAYPAL_', 'PAYPAL-', $key );
				$header_name             = str_replace( '_', '-', $header_name );
				$headers[ $header_name ] = sanitize_text_field( wp_unslash( $value ) );
			}
		}

		return $headers;
	}

	/**
	 * Verify PayPal webhook signature.
	 *
	 * @param array  $headers Webhook headers.
	 * @param string $payload Raw payload.
	 * @return bool
	 */
	private function verify_webhook( $headers, $payload ) {
		$webhook_id = $this->get_option( 'webhook_id', '' );

		if ( empty( $webhook_id ) ) {
			return true; // No verification if webhook ID not set.
		}

		$verify_request = array(
			'auth_algo'         => $headers['PAYPAL-AUTH-ALGO'] ?? '',
			'cert_url'          => $headers['PAYPAL-CERT-URL'] ?? '',
			'transmission_id'   => $headers['PAYPAL-TRANSMISSION-ID'] ?? '',
			'transmission_sig'  => $headers['PAYPAL-TRANSMISSION-SIG'] ?? '',
			'transmission_time' => $headers['PAYPAL-TRANSMISSION-TIME'] ?? '',
			'webhook_id'        => $webhook_id,
			'webhook_event'     => json_decode( $payload, true ),
		);

		$response = $this->api_request( '/v1/notifications/verify-webhook-signature', $verify_request, 'POST' );

		return ( $response['verification_status'] ?? '' ) === 'SUCCESS';
	}

	/**
	 * Handle capture completed webhook.
	 *
	 * @param array $resource Resource data.
	 * @return void
	 */
	private function webhook_capture_completed( $resource ) {
		$custom_id = '';

		// Try to find order ID from various locations.
		if ( isset( $resource['custom_id'] ) ) {
			$custom_id = $resource['custom_id'];
		} elseif ( isset( $resource['supplementary_data']['related_ids']['order_id'] ) ) {
			// Get order from PayPal order ID.
			$paypal_order_id = $resource['supplementary_data']['related_ids']['order_id'];
			$custom_id       = $this->get_order_id_from_paypal_order( $paypal_order_id );
		}

		if ( empty( $custom_id ) ) {
			return;
		}

		$order_id = intval( $custom_id );
		$order    = $this->get_order( $order_id );

		if ( ! $order || 'paid' === $order->payment_status ) {
			return;
		}

		$transaction_id = $resource['id'] ?? '';
		$amount         = floatval( $resource['amount']['value'] ?? 0 );
		$currency       = $resource['amount']['currency_code'] ?? '';

		$this->update_order_payment_status( $order_id, 'paid', $transaction_id );

		$this->log_payment_event(
			$order_id,
			$transaction_id,
			'webhook.capture.completed',
			$amount,
			$currency,
			'paid',
			$resource
		);
	}

	/**
	 * Handle capture failed webhook.
	 *
	 * @param array $resource Resource data.
	 * @return void
	 */
	private function webhook_capture_failed( $resource ) {
		$custom_id = $resource['custom_id'] ?? '';

		if ( empty( $custom_id ) ) {
			return;
		}

		$order_id      = intval( $custom_id );
		$error_message = $resource['status_details']['reason'] ?? __( 'Payment was declined.', 'ggm-commerce' );

		$this->update_order_payment_status( $order_id, 'failed', '', $error_message );

		$this->log_payment_event(
			$order_id,
			$resource['id'] ?? '',
			'webhook.capture.failed',
			floatval( $resource['amount']['value'] ?? 0 ),
			$resource['amount']['currency_code'] ?? '',
			'failed',
			$resource
		);
	}

	/**
	 * Handle capture refunded webhook.
	 *
	 * @param array $resource Resource data.
	 * @return void
	 */
	private function webhook_capture_refunded( $resource ) {
		global $wpdb;

		$capture_id = $resource['id'] ?? '';

		if ( empty( $capture_id ) ) {
			return;
		}

		// Find order by transaction ID.
		$order = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM {$wpdb->prefix}ggmc_orders WHERE payment_transaction_id = %s",
				$capture_id
			)
		);

		if ( ! $order ) {
			return;
		}

		$refund_amount = floatval( $resource['refund_amount']['value'] ?? 0 );

		$this->log_payment_event(
			$order->id,
			$capture_id,
			'webhook.capture.refunded',
			$refund_amount,
			$resource['refund_amount']['currency_code'] ?? '',
			'refunded',
			$resource
		);

		// Update order status to refunded.
		$wpdb->update(
			"{$wpdb->prefix}ggmc_orders",
			array(
				'status'         => 'refunded',
				'payment_status' => 'refunded',
				'updated_at'     => current_time( 'mysql' ),
			),
			array( 'id' => $order->id )
		);
	}

	/**
	 * Get order ID from PayPal order ID.
	 *
	 * @param string $paypal_order_id PayPal order ID.
	 * @return int|string
	 */
	private function get_order_id_from_paypal_order( $paypal_order_id ) {
		global $wpdb;

		return $wpdb->get_var(
			$wpdb->prepare(
				"SELECT id FROM {$wpdb->prefix}ggmc_orders WHERE payment_intent_id = %s",
				$paypal_order_id
			)
		) ?: '';
	}

	/**
	 * Process a refund.
	 *
	 * @param int    $order_id Order ID.
	 * @param float  $amount   Amount to refund (null for full refund).
	 * @param string $reason   Refund reason.
	 * @return array
	 */
	public function process_refund( $order_id, $amount = null, $reason = '' ) {
		$order = $this->get_order( $order_id );

		if ( ! $order || empty( $order->payment_transaction_id ) ) {
			return $this->error_response( __( 'Order not found or has no payment.', 'ggm-commerce' ) );
		}

		$refund_data = array();

		if ( null !== $amount ) {
			$refund_data['amount'] = array(
				'value'         => number_format( $amount, 2, '.', '' ),
				'currency_code' => strtoupper( $this->get_currency() ),
			);
		}

		if ( ! empty( $reason ) ) {
			$refund_data['note_to_payer'] = substr( $reason, 0, 255 );
		}

		$response = $this->api_request(
			'/v2/payments/captures/' . $order->payment_transaction_id . '/refund',
			$refund_data,
			'POST'
		);

		if ( isset( $response['error'] ) ) {
			return $this->error_response(
				$response['error']['message'] ?? __( 'Refund failed.', 'ggm-commerce' ),
				$response['error']['code'] ?? 'refund_error'
			);
		}

		$refund_amount = floatval( $response['amount']['value'] ?? $amount ?? $order->total_amount );

		$this->log_payment_event(
			$order_id,
			$response['id'] ?? '',
			'refund.created',
			$refund_amount,
			$response['amount']['currency_code'] ?? '',
			$response['status'] ?? 'pending',
			$response
		);

		return $this->success_response(
			array(
				'refund_id' => $response['id'] ?? '',
				'amount'    => $refund_amount,
				'status'    => $response['status'] ?? 'pending',
			)
		);
	}

	/**
	 * Make a request to the PayPal API.
	 *
	 * @param string $endpoint API endpoint.
	 * @param array  $data     Request data.
	 * @param string $method   HTTP method.
	 * @return array
	 */
	private function api_request( $endpoint, $data = array(), $method = 'POST' ) {
		$access_token = $this->get_access_token();

		if ( ! $access_token ) {
			return array(
				'error' => array(
					'message' => __( 'Failed to authenticate with PayPal.', 'ggm-commerce' ),
					'code'    => 'auth_failed',
				),
			);
		}

		$url = $this->get_api_base() . $endpoint;

		$args = array(
			'method'  => $method,
			'headers' => array(
				'Authorization' => 'Bearer ' . $access_token,
				'Content-Type'  => 'application/json',
			),
			'timeout' => 60,
		);

		if ( 'POST' === $method && ! empty( $data ) ) {
			$args['body'] = wp_json_encode( $data );
		}

		$response = wp_remote_request( $url, $args );

		if ( is_wp_error( $response ) ) {
			return array(
				'error' => array(
					'message' => $response->get_error_message(),
					'code'    => 'wp_error',
				),
			);
		}

		$body = json_decode( wp_remote_retrieve_body( $response ), true );
		$code = wp_remote_retrieve_response_code( $response );

		if ( $code >= 400 ) {
			return array(
				'error' => array(
					'message' => $body['message'] ?? $body['details'][0]['description'] ?? 'API error',
					'code'    => $body['name'] ?? 'api_error',
				),
			);
		}

		return $body;
	}

	/**
	 * Render payment fields for checkout.
	 *
	 * @return void
	 */
	public function render_payment_fields() {
		?>
		<div id="ggmc-paypal-button-container">
			<!-- PayPal Smart Payment Buttons will be rendered here -->
		</div>
		<div id="ggmc-paypal-payment-message" class="ggmc-payment-message" style="display: none;"></div>
		<?php
	}

	/**
	 * Get JavaScript dependencies.
	 *
	 * @return array
	 */
	public function get_scripts() {
		$client_id        = $this->get_client_id();
		$currency         = strtoupper( $this->get_currency() );
		$disabled_funding = $this->get_disabled_funding();

		// Build SDK URL parameters.
		$sdk_params = array(
			'client-id' => $client_id,
			'currency'  => $currency,
			'intent'    => 'capture',
		);

		// Only add disable-funding if there are sources to disable.
		if ( ! empty( $disabled_funding ) ) {
			$sdk_params['disable-funding'] = implode( ',', $disabled_funding );
		}

		$sdk_url = 'https://www.paypal.com/sdk/js?' . http_build_query( $sdk_params );

		return array(
			array(
				'handle' => 'paypal-sdk',
				'src'    => $sdk_url,
				'deps'   => array(),
				'ver'    => null,
			),
			array(
				'handle' => 'ggmc-paypal-payment',
				'src'    => plugins_url( 'assets/js/paypal-payment.js', dirname( __DIR__, 3 ) . '/goose-commerce.php' ),
				'deps'   => array( 'jquery', 'paypal-sdk' ),
				'ver'    => '1.0.3', // Version bump - fixed order.order_id property access.
			),
		);
	}

	/**
	 * Create a PayPal Billing Plan for a subscription product.
	 *
	 * @param array $product_data Subscription product data.
	 * @return array|WP_Error PayPal Plan response or error.
	 */
	public function create_billing_plan( $product_data ) {
		$currency = strtoupper( $this->get_currency() );

		// First create a PayPal Product (Catalog).
		$paypal_product = $this->api_request( '/v1/catalogs/products', array(
			'name'        => $product_data['name'] ?? 'Subscription',
			'type'        => 'SERVICE',
			'category'    => 'SOFTWARE',
			'description' => $product_data['description'] ?? 'Subscription product',
		) );

		if ( isset( $paypal_product['error'] ) ) {
			return new \WP_Error( 'paypal_product_error', $paypal_product['error']['message'] ?? 'Failed to create PayPal product.' );
		}

		$period_map = array(
			'day'   => 'DAY',
			'week'  => 'WEEK',
			'month' => 'MONTH',
			'year'  => 'YEAR',
		);

		$billing_cycles = array();

		// Add trial cycle if configured.
		if ( ! empty( $product_data['trial_length'] ) && (int) $product_data['trial_length'] > 0 ) {
			$trial_unit = $period_map[ $product_data['trial_period'] ?? 'day' ] ?? 'DAY';
			$billing_cycles[] = array(
				'frequency'      => array(
					'interval_unit'  => $trial_unit,
					'interval_count' => (int) $product_data['trial_length'],
				),
				'tenure_type'    => 'TRIAL',
				'sequence'       => 1,
				'total_cycles'   => 1,
				'pricing_scheme' => array(
					'fixed_price' => array(
						'value'         => '0.00',
						'currency_code' => $currency,
					),
				),
			);
		}

		// Regular billing cycle.
		$interval_unit = $period_map[ $product_data['period'] ?? 'month' ] ?? 'MONTH';
		$total_cycles  = ! empty( $product_data['subscription_length'] ) ? (int) $product_data['subscription_length'] : 0;

		$billing_cycles[] = array(
			'frequency'      => array(
				'interval_unit'  => $interval_unit,
				'interval_count' => (int) ( $product_data['interval'] ?? 1 ),
			),
			'tenure_type'    => 'REGULAR',
			'sequence'       => ! empty( $product_data['trial_length'] ) ? 2 : 1,
			'total_cycles'   => $total_cycles,
			'pricing_scheme' => array(
				'fixed_price' => array(
					'value'         => number_format( (float) $product_data['price'], 2, '.', '' ),
					'currency_code' => $currency,
				),
			),
		);

		$plan_data = array(
			'product_id'          => $paypal_product['id'],
			'name'                => $product_data['name'] ?? 'Subscription Plan',
			'billing_cycles'      => $billing_cycles,
			'payment_preferences' => array(
				'auto_bill_outstanding'     => true,
				'payment_failure_threshold' => 3,
			),
		);

		// Add sign-up fee if present.
		if ( ! empty( $product_data['sign_up_fee'] ) && (float) $product_data['sign_up_fee'] > 0 ) {
			$plan_data['payment_preferences']['setup_fee'] = array(
				'value'         => number_format( (float) $product_data['sign_up_fee'], 2, '.', '' ),
				'currency_code' => $currency,
			);
		}

		$result = $this->api_request( '/v1/billing/plans', $plan_data );

		if ( isset( $result['error'] ) ) {
			return new \WP_Error( 'paypal_plan_error', $result['error']['message'] ?? 'Failed to create billing plan.' );
		}

		return $result;
	}

	/**
	 * Create a PayPal Subscription.
	 *
	 * @param string $plan_id  PayPal Billing Plan ID.
	 * @param array  $sub_data Subscription data (email, name, return_url, cancel_url).
	 * @return array|WP_Error PayPal Subscription response or error.
	 */
	public function create_paypal_subscription( $plan_id, $sub_data = array() ) {
		$data = array(
			'plan_id'    => $plan_id,
			'application_context' => array(
				'brand_name'          => get_bloginfo( 'name' ),
				'user_action'         => 'SUBSCRIBE_NOW',
				'shipping_preference' => 'NO_SHIPPING',
				'return_url'          => $sub_data['return_url'] ?? home_url( '/checkout/?paypal_subscription_confirm=1' ),
				'cancel_url'          => $sub_data['cancel_url'] ?? home_url( '/checkout/?paypal_subscription_cancel=1' ),
			),
		);

		if ( ! empty( $sub_data['email'] ) ) {
			$data['subscriber'] = array(
				'email_address' => $sub_data['email'],
			);
			if ( ! empty( $sub_data['name'] ) ) {
				$data['subscriber']['name'] = array(
					'given_name' => $sub_data['name'],
				);
			}
		}

		$result = $this->api_request( '/v1/billing/subscriptions', $data );

		if ( isset( $result['error'] ) ) {
			return new \WP_Error( 'paypal_subscription_error', $result['error']['message'] ?? 'Failed to create PayPal subscription.' );
		}

		return $result;
	}

	/**
	 * Cancel a PayPal Subscription.
	 *
	 * @param string $paypal_subscription_id PayPal Subscription ID.
	 * @param string $reason                 Cancellation reason.
	 * @return bool True on success.
	 */
	public function cancel_paypal_subscription( $paypal_subscription_id, $reason = '' ) {
		$result = $this->api_request(
			'/v1/billing/subscriptions/' . $paypal_subscription_id . '/cancel',
			array( 'reason' => $reason ?: 'Customer requested cancellation.' )
		);

		return ! isset( $result['error'] );
	}

	/**
	 * Suspend a PayPal Subscription.
	 *
	 * @param string $paypal_subscription_id PayPal Subscription ID.
	 * @return bool True on success.
	 */
	public function suspend_paypal_subscription( $paypal_subscription_id ) {
		$result = $this->api_request(
			'/v1/billing/subscriptions/' . $paypal_subscription_id . '/suspend',
			array( 'reason' => 'Subscription paused by customer or admin.' )
		);

		return ! isset( $result['error'] );
	}

	/**
	 * Activate a PayPal Subscription.
	 *
	 * @param string $paypal_subscription_id PayPal Subscription ID.
	 * @return bool True on success.
	 */
	public function activate_paypal_subscription( $paypal_subscription_id ) {
		$result = $this->api_request(
			'/v1/billing/subscriptions/' . $paypal_subscription_id . '/activate',
			array( 'reason' => 'Subscription reactivated.' )
		);

		return ! isset( $result['error'] );
	}

	/**
	 * Handle PayPal BILLING.SUBSCRIPTION.ACTIVATED webhook.
	 *
	 * @param array $resource Webhook resource data.
	 */
	private function webhook_subscription_activated( $resource ) {
		if ( ! class_exists( 'GGM_Commerce_Subscriptions' ) ) {
			return;
		}

		$paypal_sub_id = $resource['id'] ?? '';
		if ( empty( $paypal_sub_id ) ) {
			return;
		}

		$sub_manager  = GGM_Commerce_Subscriptions::instance();
		$subscription = $sub_manager->get_subscription_by_gateway_id( $paypal_sub_id );

		if ( $subscription ) {
			$sub_manager->activate_subscription( $subscription->id );
			$sub_manager->log_event(
				$subscription->id,
				'paypal_subscription_activated',
				'PayPal subscription activated.',
				null,
				null,
				'gateway'
			);
		}
	}

	/**
	 * Handle PayPal BILLING.SUBSCRIPTION.CANCELLED webhook.
	 *
	 * @param array $resource Webhook resource data.
	 */
	private function webhook_subscription_cancelled( $resource ) {
		if ( ! class_exists( 'GGM_Commerce_Subscriptions' ) ) {
			return;
		}

		$paypal_sub_id = $resource['id'] ?? '';
		if ( empty( $paypal_sub_id ) ) {
			return;
		}

		$sub_manager  = GGM_Commerce_Subscriptions::instance();
		$subscription = $sub_manager->get_subscription_by_gateway_id( $paypal_sub_id );

		if ( $subscription ) {
			$sub_manager->cancel_subscription( $subscription->id, 'Cancelled via PayPal', 'gateway' );
		}
	}

	/**
	 * Handle PayPal BILLING.SUBSCRIPTION.SUSPENDED webhook.
	 *
	 * @param array $resource Webhook resource data.
	 */
	private function webhook_subscription_suspended( $resource ) {
		if ( ! class_exists( 'GGM_Commerce_Subscriptions' ) ) {
			return;
		}

		$paypal_sub_id = $resource['id'] ?? '';
		if ( empty( $paypal_sub_id ) ) {
			return;
		}

		$sub_manager  = GGM_Commerce_Subscriptions::instance();
		$subscription = $sub_manager->get_subscription_by_gateway_id( $paypal_sub_id );

		if ( $subscription ) {
			$sub_manager->pause_subscription( $subscription->id, 'gateway' );
		}
	}

	/**
	 * Handle PayPal BILLING.SUBSCRIPTION.PAYMENT.FAILED webhook.
	 *
	 * @param array $resource Webhook resource data.
	 */
	private function webhook_subscription_payment_failed( $resource ) {
		if ( ! class_exists( 'GGM_Commerce_Subscriptions' ) ) {
			return;
		}

		$paypal_sub_id = $resource['id'] ?? '';
		if ( empty( $paypal_sub_id ) ) {
			return;
		}

		$sub_manager  = GGM_Commerce_Subscriptions::instance();
		$subscription = $sub_manager->get_subscription_by_gateway_id( $paypal_sub_id );

		if ( $subscription ) {
			$sub_manager->log_event(
				$subscription->id,
				'paypal_payment_failed',
				'PayPal subscription payment failed.',
				null,
				null,
				'gateway'
			);

			$sub_manager->update_subscription( $subscription->id, array( 'status' => 'failed_payment' ) );
		}
	}

	/**
	 * Handle PayPal PAYMENT.SALE.COMPLETED webhook for subscription renewal.
	 *
	 * @param array $resource Webhook resource data.
	 */
	private function webhook_subscription_payment_completed( $resource ) {
		if ( ! class_exists( 'GGM_Commerce_Subscriptions' ) ) {
			return;
		}

		$paypal_sub_id = $resource['billing_agreement_id'] ?? '';
		if ( empty( $paypal_sub_id ) ) {
			return;
		}

		$sub_manager  = GGM_Commerce_Subscriptions::instance();
		$subscription = $sub_manager->get_subscription_by_gateway_id( $paypal_sub_id );

		if ( ! $subscription ) {
			return;
		}

		$amount = (float) ( $resource['amount']['total'] ?? $subscription->recurring_total );

		// Create renewal order.
		$orders_handler = GGM_Commerce_Orders::get_instance();
		$order_id       = $orders_handler->create_renewal_order( $subscription );

		if ( ! is_wp_error( $order_id ) ) {
			global $wpdb;
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
			$wpdb->update(
				"{$wpdb->prefix}ggmc_orders",
				array(
					'payment_status'         => 'paid',
					'status'                 => 'completed',
					'payment_method'         => 'paypal',
					'payment_transaction_id' => $resource['id'] ?? '',
					'updated_at'             => current_time( 'mysql' ),
				),
				array( 'id' => $order_id )
			);
		}

		$next_payment = $sub_manager->calculate_next_payment_date(
			$subscription->billing_period,
			$subscription->billing_interval
		);

		$sub_manager->update_subscription( $subscription->id, array(
			'last_payment_date'  => current_time( 'mysql', true ),
			'next_payment_date'  => $next_payment,
			'status'             => 'active',
		) );

		$sub_manager->log_event(
			$subscription->id,
			'renewal_payment_succeeded',
			sprintf( 'PayPal renewal payment of %s completed.', ggm_commerce_format_price( $amount ) ),
			is_wp_error( $order_id ) ? null : $order_id,
			$amount,
			'gateway'
		);

		do_action( 'ggmc_subscription_renewal_succeeded', $subscription->id, $resource );
	}

	/**
	 * Get JavaScript localization data.
	 *
	 * @return array
	 */
	public function get_js_data() {
		// Generate nonces for payment API calls.
		$nonce          = wp_create_nonce( 'ggm_commerce_payment' );
		$rest_nonce     = wp_create_nonce( 'wp_rest' );
		$checkout_nonce = wp_create_nonce( 'ggm_commerce_checkout' );

		// Get cart items for order creation.
		// Convert to indexed array for JavaScript compatibility.
		$cart_items = array();
		if ( class_exists( 'GGM_Commerce_Cart' ) ) {
			$cart           = GGM_Commerce_Cart::get_instance();
			$raw_cart_items = $cart->get_cart_items();
			foreach ( $raw_cart_items as $key => $item ) {
				$cart_items[] = array(
					'cart_key'   => $key,
					'product_id' => $item['product_id'],
					'quantity'   => $item['quantity'],
				);
			}
		}

		return array(
			'clientId'       => $this->get_client_id(),
			'currency'       => strtoupper( $this->get_currency() ),
			'testMode'       => $this->is_test_mode(),
			'buttonStyle'    => $this->get_button_style(),
			'enabledFunding' => $this->get_enabled_funding(),
			'nonce'          => $nonce,
			'restNonce'      => $rest_nonce,
			'checkoutNonce'  => $checkout_nonce,
			'ajaxUrl'        => admin_url( 'admin-ajax.php' ),
			'cartItems'      => $cart_items,
			'createOrderUrl' => rest_url( 'ggm-commerce/v1/paypal/create-order' ),
			'captureUrl'     => rest_url( 'ggm-commerce/v1/paypal/capture-order' ),
			'i18n'           => array(
				'processing' => __( 'Processing payment...', 'ggm-commerce' ),
				'error'      => __( 'An error occurred. Please try again.', 'ggm-commerce' ),
			),
		);
	}
}
