<?php
/**
 * Gift Card Payment Gateway Class
 *
 * @package FunnelKit_Square
 * @subpackage Payment
 */

namespace FKWCSQ\Includes\Modules\Payment\Gateways;

use FKWCSQ\Includes\Logger;
use Exception;
use WP_Error;

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

/**
 * Gift_Card Class
 *
 * Architecture follows WooCommerce Square's gift card approach:
 *
 * 1. Apply   → tokenize via Square SDK → store nonce in session (no API call).
 * 2. Review  → woocommerce_update_order_review_fragments fires → retrieve_gift_card()
 *              checks balance (read-only), updates session, returns HTML fragment.
 * 3. Cart    → apply_gift_card_discount() applies a negative fee from session data.
 * 4. Payment → process_payment() creates + completes the charge in a single API call
 *              (autocomplete=true). No pre-authorization, no orphaned payments.
 * 5. Remove  → clear session only — no API call needed.
 */
class Gift_Card extends \WC_Payment_Gateway {

	/**
	 * Gateway ID.
	 *
	 * @var string
	 */
	public $id = 'fkwcsq_gift_card';

	/**
	 * API instance for Square integration.
	 *
	 * @var \FKWCSQ\Includes\API
	 */
	protected $api;

	/**
	 * General plugin settings.
	 *
	 * @var array
	 */
	protected $general_settings;

	/**
	 * Whether logging is enabled.
	 *
	 * @var bool
	 */
	protected $logging_enabled;

	/**
	 * Whether partial payments are allowed.
	 *
	 * @var bool
	 */
	protected $allow_partial_payment;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->setup_gateway_properties();
		$this->init_form_fields();
		$this->init_settings();
		$this->load_settings();
		$this->init_hooks();
	}

	/**
	 * Set up basic gateway properties.
	 */
	public function setup_gateway_properties() {
		$this->has_fields         = true;
		$this->method_title       = __( 'Square Gift Card', 'funnelkit-payment-gateway-square-for-woocommerce' );
		$this->method_description = __( 'Accept Square Gift Card payments. Customers can pay partially with a gift card and use another payment method for the remaining balance.', 'funnelkit-payment-gateway-square-for-woocommerce' );
		$this->supports           = array(
			'products',
			'refunds',
		);

		$this->api = \FKWCSQ\Includes\API::get_instance();
	}

	/**
	 * Load settings from database.
	 */
	public function load_settings() {
		$this->general_settings      = get_option( 'fkwcsq_settings', array() );
		$this->logging_enabled       = isset( $this->general_settings['enable_logging'] ) && $this->general_settings['enable_logging'] === 'yes';
		$this->title                 = $this->get_option( 'title' );
		$this->description           = $this->get_option( 'description' );
		$this->allow_partial_payment = $this->get_option( 'allow_partial_payment' ) === 'yes';
	}

	/**
	 * Initialize action hooks.
	 */
	public function init_hooks() {
		add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'payment_scripts' ) );

		// AJAX handlers for gift card operations.
		add_action( 'wp_ajax_fkwcsq_apply_gift_card', array( $this, 'ajax_apply_gift_card' ) );
		add_action( 'wp_ajax_nopriv_fkwcsq_apply_gift_card', array( $this, 'ajax_apply_gift_card' ) );
		add_action( 'wp_ajax_fkwcsq_remove_gift_card', array( $this, 'ajax_remove_gift_card' ) );
		add_action( 'wp_ajax_nopriv_fkwcsq_remove_gift_card', array( $this, 'ajax_remove_gift_card' ) );

		// Balance check + fragment update on every checkout review refresh.
		add_filter( 'woocommerce_update_order_review_fragments', array( $this, 'add_gift_card_fragments' ) );

		// Cart fee for gift card discount.
		add_action( 'woocommerce_cart_calculate_fees', array( $this, 'apply_gift_card_discount' ) );

		// Handle zero-total checkout path where WooCommerce skips gateway process_payment().
		add_action( 'woocommerce_checkout_order_processed', array( $this, 'maybe_finalize_gift_card_order' ), 20, 3 );

		// Clean up session on order completion.
		add_action( 'woocommerce_thankyou', array( $this, 'clear_gift_card_session' ) );
	}

	/**
	 * Initialize form fields.
	 */
	public function init_form_fields() {
		$this->form_fields = array(
			'enabled'               => array(
				'title'       => __( 'Enable Square Gift Card', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'label'       => ' ',
				'type'        => 'checkbox',
				'description' => '',
				'default'     => 'no',
			),
			'title'                 => array(
				'title'       => __( 'Title', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'type'        => 'text',
				'description' => __( 'This controls the title which the user sees during checkout.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'default'     => __( 'Gift Card', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'desc_tip'    => true,
			),
			'description'           => array(
				'title'       => __( 'Description', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'type'        => 'textarea',
				'description' => __( 'This controls the description which the user sees during checkout.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'default'     => __( 'Pay with a Square Gift Card.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'desc_tip'    => true,
			),
			'allow_partial_payment' => array(
				'title'       => __( 'Partial Payment', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'label'       => __( 'Allow partial payment with gift card', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'type'        => 'checkbox',
				'description' => __( 'If enabled, customers can use a gift card for partial payment and pay the remaining balance with another payment method.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'default'     => 'yes',
				'desc_tip'    => true,
			),
		);
	}

	/**
	 * Check if the gateway is available.
	 *
	 * @return bool
	 */
	public function is_available() {
		if ( 'yes' !== $this->enabled ) {
			return false;
		}

		if ( ! \FKWCSQ\Square::is_configured() ) {
			return false;
		}

		return true;
	}

	/**
	 * Get the gateway icon.
	 *
	 * @return string
	 */
	public function get_icon() {
		$icon_url  = FKWCSQ_PLUGIN_URL . 'assets/images/gift-card.svg';
		$icon_html = sprintf(
			'<img src="%s" alt="%s" class="fkwcsq-gift-card-icon" width="32" style="margin-left: 0.3em" />',
			esc_url( $icon_url ),
			esc_attr__( 'Gift Card', 'funnelkit-payment-gateway-square-for-woocommerce' )
		);

		return apply_filters( 'woocommerce_gateway_icon', $icon_html, $this->id );
	}

	/**
	 * Render payment fields on checkout.
	 *
	 * Shows the Square SDK iframe when no gift card is applied, or the applied-state
	 * summary (populated by add_gift_card_fragments() on every checkout refresh).
	 */
	public function payment_fields() {
		if ( $this->description ) {
			echo wp_kses_post( wpautop( $this->description ) );
		}

		if ( \FKWCSQ\Square::is_sandbox_mode() ) {
			?>
			<p class="fkwcsq-gift-card-test-info">
				<?php esc_html_e( 'Test Gift Card Number:', 'funnelkit-payment-gateway-square-for-woocommerce' ); ?>
				<code>7783 3200 0000 0000</code>
			</p>
			<?php
		}

		$applied    = WC()->session ? WC()->session->get( 'fkwcsq_gift_card_applied' ) : null;
		$is_applied = ! empty( $applied['token'] ) && isset( $applied['paid_amount'] );
		?>
		<div class="fkwcsq-gift-card-payment-form">
			<?php if ( $is_applied ) : ?>
				<div class="fkwcsq-gift-card-fragment fkwcsq-gift-card-applied">
					<?php $this->render_applied_gift_card_html( $applied ); ?>
				</div>
			<?php else : ?>
				<div class="fkwcsq-gift-card-fragment fkwcsq-gift-card-not-applied">
					<div class="fkwcsq-gift-card-form">
						<div id="fkwcsq-gift-card-container" class="fkwcsq-shimmer-container">
							<div class="fkwcsq-shimmer-line"></div>
						</div>
						<div id="fkwcsq-gift-card-error" class="woocommerce-error" style="display:none;"></div>
					</div>
					<div class="fkwcsq-gift-card-result" style="display:none;">
						<p class="fkwcsq-gift-card-notice"></p>
					</div>
					<input type="hidden" name="fkwcsq_gift_card_token" id="fkwcsq_gift_card_token" value="" />
				</div>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Render the applied gift card HTML block (used in both payment_fields and fragments).
	 *
	 * @param array $applied Session gift card data.
	 */
	protected function render_applied_gift_card_html( $applied ) {
		?>
		<p class="fkwcsq-gift-card-notice success">
			<?php
			printf(
				/* translators: %s: applied amount */
				esc_html__( 'Gift card applied: %s credit', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				wc_price( $applied['paid_amount'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
			);
			?>
			<button type="button" class="fkwcsq-remove-gift-card" title="<?php esc_attr_e( 'Remove', 'funnelkit-payment-gateway-square-for-woocommerce' ); ?>">&times;</button>
		</p>
		<?php if ( $applied['balance_due'] > 0 ) : ?>
			<p class="fkwcsq-gift-card-balance-notice">
				<?php
				printf(
					/* translators: %s: remaining balance */
					esc_html__( 'Remaining balance to pay: %s', 'funnelkit-payment-gateway-square-for-woocommerce' ),
					wc_price( $applied['balance_due'] ) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
				);
				?>
			</p>
		<?php endif; ?>
		<input type="hidden" name="fkwcsq_gift_card_token" value="<?php echo esc_attr( $applied['token'] ); ?>" />
		<?php
	}

	/**
	 * Update checkout fragments with gift card balance info.
	 *
	 * Called on every woocommerce_update_order_review_fragments (checkout refresh).
	 * Calls retrieve_gift_card() — a read-only balance check — to get the current
	 * balance, recalculates paid_amount / balance_due against the live cart total,
	 * updates the session, and returns an HTML fragment for the payment fields area.
	 *
	 * @param array $fragments Existing WooCommerce checkout fragments.
	 * @return array Updated fragments.
	 */
	public function add_gift_card_fragments( $fragments ) {
		if ( ! WC()->session ) {
			return $fragments;
		}

		$gift_card_data = WC()->session->get( 'fkwcsq_gift_card_applied' );

		if ( empty( $gift_card_data['token'] ) ) {
			return $fragments;
		}

		$token      = $gift_card_data['token'];
		$cart_total = WC()->cart ? (float) WC()->cart->get_total( 'edit' ) : 0;

		if ( \FKWCSQ\Square::is_sandbox_mode() ) {
			// Square sandbox test gift cards have a fixed $1 balance.
			// retrieveGiftCardFromNonce does not work with sandbox test cards —
			// this matches WooCommerce Square's own sandbox handling.
			$balance = 1.0;
		} else {
			$result = $this->api->retrieve_gift_card( $token );

			if ( is_wp_error( $result ) ) {
				$this->log_error( 'Gift card balance check failed: ' . $result->get_error_message() );
				// Token is invalid or expired — remove it from session.
				WC()->session->set( 'fkwcsq_gift_card_applied', null );
				return $fragments;
			}

			$balance = (float) $result['balance'];
		}

		// Insufficient balance — clear session and return an error fragment so the
		// JS knows not to submit and the user sees an actionable message.
		if ( $balance <= 0 || $balance < $cart_total ) {
			$error_message = $balance <= 0
				? __( 'This gift card has no available balance.', 'funnelkit-payment-gateway-square-for-woocommerce' )
				: sprintf(
					/* translators: 1: gift card balance amount 2: order total amount */
					__( 'Your gift card balance (%1$s) is insufficient to cover the order total (%2$s). Please select a different payment method.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
					wc_price( $balance ),
					wc_price( $cart_total )
				);

			WC()->session->set( 'fkwcsq_gift_card_applied', null );

			ob_start();
			?>
			<div class="fkwcsq-gift-card-fragment fkwcsq-gift-card-not-applied">
				<div class="fkwcsq-gift-card-form">
					<div id="fkwcsq-gift-card-container" class="fkwcsq-shimmer-container">
						<div class="fkwcsq-shimmer-line"></div>
					</div>
					<div id="fkwcsq-gift-card-error" class="woocommerce-error" style="display:block;">
						<?php echo wp_kses_post( $error_message ); ?>
					</div>
				</div>
				<input type="hidden" name="fkwcsq_gift_card_token" id="fkwcsq_gift_card_token" value="" />
			</div>
			<?php
			$fragments['.fkwcsq-gift-card-fragment'] = ob_get_clean();

			return $fragments;
		}

		// Balance covers the full order total — charge exactly the cart total.
		$paid_amount = $cart_total;
		$balance_due = 0.0;

		$updated = array(
			'token'       => $token,
			'balance'     => $balance,
			'paid_amount' => $paid_amount,
			'balance_due' => $balance_due,
			'cart_total'  => $cart_total,
		);

		WC()->session->set( 'fkwcsq_gift_card_applied', $updated );

		// Build the HTML fragment that replaces .fkwcsq-gift-card-fragment.
		ob_start();
		?>
		<div class="fkwcsq-gift-card-fragment fkwcsq-gift-card-applied">
			<?php $this->render_applied_gift_card_html( $updated ); ?>
		</div>
		<?php
		$fragments['.fkwcsq-gift-card-fragment'] = ob_get_clean();

		return $fragments;
	}

	/**
	 * Get gateway-specific localized data.
	 *
	 * @return array
	 */
	public function get_gateway_data() {
		return array(
			'apply_nonce'     => wp_create_nonce( 'fkwcsq_apply_gift_card' ),
			'remove_nonce'    => wp_create_nonce( 'fkwcsq_remove_gift_card' ),
			'partial_payment' => $this->allow_partial_payment,
			'i18n'            => array(
				'apply_button'     => __( 'Apply Gift Card', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'applying'         => __( 'Applying...', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'error_invalid'    => __( 'Invalid gift card. Please check and try again.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'error_no_balance' => __( 'This gift card has no available balance.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'error_generic'    => __( 'An error occurred. Please try again.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'success_full'     => __( 'Gift card applied! Your order will be fully paid.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				/* translators: %s: remaining balance amount */
				'success_partial'  => __( 'Gift card applied! Remaining balance: %s', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				'partial_disabled' => __( 'This gift card does not have enough balance. Partial payments are not enabled.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
			),
		);
	}

	/**
	 * Enqueue payment scripts.
	 */
	public function payment_scripts() {
		if ( ! is_checkout() || 'no' === $this->enabled ) {
			return;
		}

		$checkout_scripts = \FKWCSQ\Includes\Checkout_Scripts::get_instance();
		$checkout_scripts->enqueue_checkout_script();
		$checkout_scripts->localize_gateway_data( 'fkwcsq_gift_card_params', $this->id, $this->get_gateway_data() );

		wp_add_inline_style(
			'woocommerce-inline',
			'
			.fkwcsq-gift-card-payment-form {
				padding: 10px 0;
			}
			#fkwcsq-gift-card-container {
				margin: 10px 0;
				min-height: 50px;
			}
			.fkwcsq-shimmer-container {
				background: #f6f7f8;
				border-radius: 4px;
				padding: 15px;
				overflow: hidden;
			}
			.fkwcsq-shimmer-line {
				height: 40px;
				background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
				background-size: 200% 100%;
				animation: fkwcsq-shimmer 1.5s infinite;
				border-radius: 4px;
			}
			@keyframes fkwcsq-shimmer {
				0% { background-position: -200% 0; }
				100% { background-position: 200% 0; }
			}
			#fkwcsq-gift-card-container.fkwcsq-initialized .fkwcsq-shimmer-line {
				display: none;
			}
			.fkwcsq-gift-card-notice {
				padding: 10px 15px;
				border-radius: 4px;
				margin: 10px 0;
			}
			.fkwcsq-gift-card-notice.success {
				background: #d4edda;
				border: 1px solid #c3e6cb;
				color: #155724;
			}
			.fkwcsq-gift-card-notice.error {
				background: #f8d7da;
				border: 1px solid #f5c6cb;
				color: #721c24;
			}
			.fkwcsq-remove-gift-card {
				float: right;
				background: none;
				border: none;
				font-size: 20px;
				cursor: pointer;
				padding: 0 5px;
				line-height: 1;
			}
			.fkwcsq-gift-card-balance-notice {
				font-style: italic;
				color: #666;
			}
			#fkwcsq-gift-card-error {
				margin-top: 10px;
			}
			'
		);
	}

	/**
	 * Process payment.
	 *
	 * Creates AND completes the Square gift card payment in a single API call
	 * (autocomplete=true). No pre-authorization step — the token was stored on
	 * apply and the balance was verified on each checkout review refresh.
	 *
	 * @param int $order_id Order ID.
	 * @return array
	 */
	public function process_payment( $order_id ) {
		try {
			$order = wc_get_order( $order_id );

			if ( ! $order ) {
				throw new Exception( __( 'Order not found.', 'funnelkit-payment-gateway-square-for-woocommerce' ) );
			}

			// maybe_finalize_gift_card_order() already handled this (zero-total path):
			// it created + completed the Square payment and restored the order total so
			// WC routed here. Just call payment_complete() and return.
			if ( $order->get_meta( '_fkwcsq_gc_captured' ) === 'yes' ) {
				$payment_id = $order->get_meta( '_fkwcsq_gift_card_payment_id' );
				$order->payment_complete( $payment_id );
				$order->save();

				return array(
					'result'   => 'success',
					'redirect' => $this->get_return_url( $order ),
				);
			}

			// Get gift card token — prefer session (classic checkout), fall back to POST
			// (Block Checkout sends fkwcsq_gift_card_token as a hidden field).
			$gift_card_data = WC()->session ? WC()->session->get( 'fkwcsq_gift_card_applied' ) : null;

			$posted_token = isset( $_POST['fkwcsq_gift_card_token'] ) ? sanitize_text_field( wp_unslash( $_POST['fkwcsq_gift_card_token'] ) ) : ''; //phpcs:ignore WordPress.Security.NonceVerification.Missing, FKWCS.CodeAnalysis.FKWCSSpecific.MissingCapabilityCheck -- WooCommerce verifies checkout nonce, guest users allowed

			if ( empty( $gift_card_data['token'] ) && empty( $posted_token ) ) {
				throw new Exception( __( 'No gift card found. Please apply a gift card first.', 'funnelkit-payment-gateway-square-for-woocommerce' ) );
			}

			$token       = ! empty( $gift_card_data['token'] ) ? $gift_card_data['token'] : $posted_token;
			$order_total = $order->get_total();

			// For Block Checkout, session may not have paid_amount yet — derive it now.
			if ( ! empty( $gift_card_data['paid_amount'] ) ) {
				$paid_amount = (float) $gift_card_data['paid_amount'];
				$balance_due = (float) $gift_card_data['balance_due'];
			} else {
				// Treat as full payment; Square will return the actual charged amount.
				$paid_amount = $order_total;
				$balance_due = 0.0;
			}

			// Server-side guard: gift card must cover the full order total.
			// Partial payments are not supported — the balance check in
			// add_gift_card_fragments() should have already blocked this path,
			// but we validate here as a safety net (e.g. Block Checkout, stale session).
			if ( $paid_amount < $order_total - 0.01 ) {
				throw new Exception(
					sprintf(
						/* translators: 1: gift card balance amount 2: order total amount */
						__( 'Your gift card balance (%1$s) is insufficient to cover the order total (%2$s). Please select a different payment method.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
						wc_price( $paid_amount ),
						wc_price( $order_total )
					)
				);
			}

			// Create AND complete the payment in one step.
			$result = $this->create_and_complete_gift_card_payment( $token, $paid_amount );

			if ( is_wp_error( $result ) ) {
				throw new Exception( $result->get_error_message() );
			}

			$actual_paid = $result['paid_amount'];
			$balance_due = max( 0.0, $order_total - $actual_paid );

			if ( $balance_due > 0 && ! $this->allow_partial_payment ) {
				throw new Exception( __( 'This gift card does not have enough balance. Partial payments are not enabled.', 'funnelkit-payment-gateway-square-for-woocommerce' ) );
			}

			$this->save_payment_meta( $order, $result['payment_id'], $actual_paid, $balance_due );

			if ( $balance_due > 0 ) {
				// Partial payment — order stays pending until another method covers remainder.
				$order->update_status(
					'pending',
					sprintf(
						/* translators: 1: gift card amount 2: remaining balance */
						__( 'Gift card payment of %1$s received. Remaining balance of %2$s requires additional payment.', 'funnelkit-payment-gateway-square-for-woocommerce' ),
						wc_price( $actual_paid ),
						wc_price( $balance_due )
					)
				);
			} else {
				// Full payment.
				$this->normalize_full_gift_card_order_totals( $order );
				$order->payment_complete( $result['payment_id'] );
			}

			$this->clear_gift_card_session( $order_id );
			$order->save();

			return array(
				'result'   => 'success',
				'redirect' => $this->get_return_url( $order ),
			);

		} catch ( Exception $e ) {
			$this->log_error( 'Gift card payment failed: ' . $e->getMessage() );
			wc_add_notice( $e->getMessage(), 'error' );

			return array(
				'result'   => 'failure',
				'messages' => $e->getMessage(),
			);
		}
	}

	/**
	 * Handle the zero-total checkout path.
	 *
	 * When the applied gift card covers the full order amount the negative fee
	 * reduces the WooCommerce order total to zero. WC then skips process_payment()
	 * entirely and calls payment_complete() directly (its zero-total shortcut).
	 *
	 * Strategy (mirrors the fix from the previous payment_complete duplicate bug):
	 * - For the gift card gateway: create + complete the Square payment here, then
	 *   restore the order total (remove negative fee) so WC sees a non-zero amount
	 *   and routes through process_payment(). process_payment() detects
	 *   _fkwcsq_gc_captured and only calls payment_complete() — single call.
	 * - For any other gateway: create + complete the Square payment here but do NOT
	 *   restore the total. WC's zero-total path calls payment_complete() once.
	 *   We deliberately do not call it here to avoid a duplicate.
	 *
	 * @param int       $order_id    Order ID.
	 * @param array     $posted_data Raw posted checkout data.
	 * @param \WC_Order $order       Order object.
	 */
	public function maybe_finalize_gift_card_order( $order_id, $posted_data, $order ) {
		if ( ! ( $order instanceof \WC_Order ) ) {
			$order = wc_get_order( $order_id );
		}

		if ( ! ( $order instanceof \WC_Order ) || ! WC()->session ) {
			return;
		}

		$gift_card_data = WC()->session->get( 'fkwcsq_gift_card_applied' );

		if ( empty( $gift_card_data['token'] ) ) {
			return;
		}

		// Guard: Square payment already created for this order.
		if ( $order->get_meta( '_fkwcsq_gift_card_payment_id' ) ) {
			return;
		}

		// Only act if order total is zero (otherwise process_payment() will handle it).
		if ( $order->get_total() > 0 ) {
			return;
		}

		$token       = $gift_card_data['token'];
		$paid_amount = isset( $gift_card_data['paid_amount'] ) ? (float) $gift_card_data['paid_amount'] : 0.0;

		if ( $paid_amount <= 0 ) {
			return;
		}

		$result = $this->create_and_complete_gift_card_payment( $token, $paid_amount );

		if ( is_wp_error( $result ) ) {
			$this->log_error( 'Gift card zero-total finalization failed: ' . $result->get_error_message() );
			return;
		}

		$this->save_payment_meta( $order, $result['payment_id'], $result['paid_amount'], 0.0 );

		$selected_method = $order->get_payment_method();

		if ( $selected_method === $this->id ) {
			// Restore order total so WC calls process_payment() which owns payment_complete().
			$this->normalize_full_gift_card_order_totals( $order );
			$order->update_meta_data( '_fkwcsq_gc_captured', 'yes' );
		}
		// For any other gateway: leave total at zero. WC's payment_complete() fires once.

		$order->save();
		$this->clear_gift_card_session( $order_id );
	}

	/**
	 * Create and complete a gift card payment in a single Square API call.
	 *
	 * Unlike the old approach (autocomplete=false + separate complete_payment()),
	 * this uses autocomplete=true so funds are captured immediately with no
	 * intermediate AUTHORIZED state that could be orphaned.
	 *
	 * @param string $token  Gift card nonce from Square Web Payments SDK.
	 * @param float  $amount Amount in dollars to charge.
	 * @return array|\WP_Error Array with 'payment_id' and 'paid_amount', or WP_Error.
	 */
	protected function create_and_complete_gift_card_payment( $token, $amount ) {
		$amount_cents = (int) round( $amount * 100 );

		$payment_data = array(
			'source_id'      => $token,
			'idempotency_key' => fkwcsq_idempotency_key( 'fkwcsq_gc_' ),
			'amount_money'   => array(
				'amount'   => $amount_cents,
				'currency' => get_woocommerce_currency(),
			),
			'location_id'    => $this->api->get_location_id(),
			'autocomplete'   => true,  // Create + capture in one call.
			// Note: accept_partial_authorization cannot be used with autocomplete=true.
			// We always charge the exact known amount (min(balance, cart_total)) derived
			// from the read-only balance check in add_gift_card_fragments(), so partial
			// authorization is never needed.
			'note'           => __( 'WooCommerce Gift Card Payment', 'funnelkit-payment-gateway-square-for-woocommerce' ),
		);

		$this->log_debug( 'Creating gift card payment: ' . wp_json_encode( $payment_data ) );

		$response = $this->api->create_payment( $payment_data );

		if ( is_wp_error( $response ) ) {
			return $response;
		}

		if ( empty( $response['payment']['id'] ) ) {
			return new WP_Error( 'invalid_response', __( 'Invalid response from Square.', 'funnelkit-payment-gateway-square-for-woocommerce' ) );
		}

		$payment     = $response['payment'];
		$paid_amount = isset( $payment['amount_money']['amount'] ) ? $payment['amount_money']['amount'] / 100 : 0.0;

		$this->log_debug( 'Gift card payment completed: ID=' . $payment['id'] . ', Paid=' . $paid_amount );

		return array(
			'payment_id'  => $payment['id'],
			'paid_amount' => $paid_amount,
		);
	}

	/**
	 * Save payment details to order meta.
	 *
	 * @param \WC_Order $order       Order object.
	 * @param string    $payment_id  Square payment ID.
	 * @param float     $paid_amount Amount paid by gift card.
	 * @param float     $balance_due Remaining balance (0 for full payment).
	 */
	protected function save_payment_meta( $order, $payment_id, $paid_amount, $balance_due ) {
		$order->set_payment_method( $this->id );
		$order->set_payment_method_title( $this->get_title() );
		$order->set_transaction_id( $payment_id );
		$order->update_meta_data( '_transaction_id', $payment_id );
		$order->update_meta_data( '_square_payment_id', $payment_id );
		$order->update_meta_data( '_square_payment_method', 'gift_card' );
		$order->update_meta_data( '_fkwcsq_gift_card_payment_id', $payment_id );
		$order->update_meta_data( '_fkwcsq_gift_card_amount', $paid_amount );

		if ( $balance_due > 0 ) {
			$order->update_meta_data( '_fkwcsq_gift_card_partial', 'yes' );
			$order->update_meta_data( '_fkwcsq_gift_card_balance_due', $balance_due );
		}
	}

	/**
	 * Remove the temporary negative "Gift Card" fee from the order.
	 *
	 * Called for full gift card payments before payment_complete() so the admin
	 * refund UI shows a positive available-to-refund amount.
	 *
	 * @param \WC_Order $order Order object.
	 */
	protected function normalize_full_gift_card_order_totals( $order ) {
		if ( ! ( $order instanceof \WC_Order ) ) {
			return;
		}

		foreach ( $order->get_items( 'fee' ) as $item_id => $fee_item ) {
			if ( ! ( $fee_item instanceof \WC_Order_Item_Fee ) ) {
				continue;
			}

			if ( strtolower( trim( $fee_item->get_name() ) ) === strtolower( trim( __( 'Gift Card', 'funnelkit-payment-gateway-square-for-woocommerce' ) ) ) ) {
				$order->remove_item( $item_id );
			}
		}

		$order->calculate_totals( false );
	}

	/**
	 * AJAX handler — store gift card token in session.
	 *
	 * No API call is made here. The balance will be checked on the next
	 * checkout review refresh via add_gift_card_fragments().
	 */
	public function ajax_apply_gift_card() {
		check_ajax_referer( 'fkwcsq_apply_gift_card', 'nonce' );

		// phpcs:ignore FKWCS.CodeAnalysis.FKWCSSpecific.MissingCapabilityCheck -- guest checkout allowed
		$token = isset( $_POST['token'] ) ? sanitize_text_field( wp_unslash( $_POST['token'] ) ) : '';

		if ( empty( $token ) ) {
			wp_send_json_error( array( 'message' => __( 'Gift card token is missing.', 'funnelkit-payment-gateway-square-for-woocommerce' ) ) );
		}

		// Store token only — balance will be checked during the next checkout
		// fragment update (triggered by the JS update_checkout call after this).
		WC()->session->set(
			'fkwcsq_gift_card_applied',
			array( 'token' => $token )
		);

		wp_send_json_success( array( 'token_stored' => true ) );
	}

	/**
	 * AJAX handler — remove applied gift card.
	 *
	 * No API call needed: with autocomplete=true there is no pre-authorization
	 * to cancel. Clearing the session is sufficient.
	 */
	public function ajax_remove_gift_card() {
		check_ajax_referer( 'fkwcsq_remove_gift_card', 'nonce' );

		WC()->session->set( 'fkwcsq_gift_card_applied', null );

		wp_send_json_success( array( 'message' => __( 'Gift card removed.', 'funnelkit-payment-gateway-square-for-woocommerce' ) ) );
	}

	/**
	 * Apply gift card discount as a negative cart fee.
	 *
	 * Uses paid_amount from session (set by add_gift_card_fragments()).
	 *
	 * @param \WC_Cart $cart Cart object.
	 */
	public function apply_gift_card_discount( $cart ) {
		if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
			return;
		}

		$gift_card_data = WC()->session ? WC()->session->get( 'fkwcsq_gift_card_applied' ) : null;

		if ( empty( $gift_card_data['paid_amount'] ) ) {
			return;
		}

		$cart->add_fee(
			__( 'Gift Card', 'funnelkit-payment-gateway-square-for-woocommerce' ),
			-$gift_card_data['paid_amount'],
			false
		);
	}

	/**
	 * Clear gift card session data.
	 *
	 * @param int $order_id Order ID (unused but required by hook signature).
	 */
	public function clear_gift_card_session( $order_id = 0 ) {
		if ( WC()->session ) {
			WC()->session->set( 'fkwcsq_gift_card_applied', null );
		}
	}

	/**
	 * Process refund.
	 *
	 * @param int    $order_id Order ID.
	 * @param float  $amount   Refund amount.
	 * @param string $reason   Refund reason.
	 * @return bool|\WP_Error
	 */
	public function process_refund( $order_id, $amount = null, $reason = '' ) {
		$order = wc_get_order( $order_id );

		if ( ! $order ) {
			return new WP_Error( 'invalid_order', __( 'Order not found.', 'funnelkit-payment-gateway-square-for-woocommerce' ) );
		}

		$payment_id = $order->get_meta( '_fkwcsq_gift_card_payment_id' );

		if ( empty( $payment_id ) ) {
			$payment_id = $order->get_meta( '_square_payment_id' );
		}

		if ( empty( $payment_id ) ) {
			return new WP_Error( 'no_payment_id', __( 'No payment ID found for this order.', 'funnelkit-payment-gateway-square-for-woocommerce' ) );
		}

		$gift_card_paid = (float) $order->get_meta( '_fkwcsq_gift_card_amount' );

		if ( $gift_card_paid <= 0 ) {
			$payment_info = $this->api->get_payment( $payment_id );
			if ( is_array( $payment_info ) && isset( $payment_info['amount'] ) ) {
				$gift_card_paid = (float) $payment_info['amount'];
			}
		}

		if ( $gift_card_paid <= 0 ) {
			return new WP_Error( 'invalid_refund_amount', __( 'Invalid refund amount.', 'funnelkit-payment-gateway-square-for-woocommerce' ) );
		}

		$already_refunded = (float) $order->get_meta( '_fkwcsq_gift_card_refunded_amount' );
		$max_refundable   = max( 0, $gift_card_paid - $already_refunded );

		if ( $max_refundable <= 0 ) {
			return new WP_Error( 'nothing_to_refund', __( 'No refundable gift card amount remaining.', 'funnelkit-payment-gateway-square-for-woocommerce' ) );
		}

		if ( $amount === null || (float) $amount <= 0 ) {
			$amount = $max_refundable;
		}

		$amount = (float) $amount;

		if ( $amount > $max_refundable ) {
			return new WP_Error( 'invalid_refund_amount', __( 'Invalid refund amount.', 'funnelkit-payment-gateway-square-for-woocommerce' ) );
		}

		$result = $this->api->process_refund( $payment_id, $amount, $reason );

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		$order->update_meta_data( '_fkwcsq_gift_card_refunded_amount', $already_refunded + $amount );
		$order->save();

		$order->add_order_note(
			sprintf(
				/* translators: 1: refund amount 2: refund ID */
				__( 'Refunded %1$s via Square Gift Card (Refund ID: %2$s)', 'funnelkit-payment-gateway-square-for-woocommerce' ),
				wc_price( $amount ),
				$result['refund_id']
			)
		);

		return true;
	}

	/**
	 * Log debug message.
	 *
	 * @param string $message Message to log.
	 */
	protected function log_debug( $message ) {
		if ( $this->logging_enabled ) {
			Logger::debug( '[Gift Card] ' . $message );
		}
	}

	/**
	 * Log error message.
	 *
	 * @param string $message Message to log.
	 */
	protected function log_error( $message ) {
		if ( $this->logging_enabled ) {
			Logger::error( '[Gift Card] ' . $message );
		}
	}
}
