<?php
/**
 * WooCommerce Square
 *
 * This source file is subject to the GNU General Public License v3.0
 * that is bundled with this package in the file license.txt.
 *
 * @package WooCommerce\Square\Utilities
 */

namespace WooCommerce\Square\Utilities;

defined( 'ABSPATH' ) || exit;

/**
 * Authorizes AJAX handlers that return order data during pay-for-order flows.
 *
 * @since 5.3.1
 */
final class Order_Ajax_Authorization {

	/**
	 * Generic error message for any failed pay-for-order authorization (avoid response differentiation).
	 *
	 * @since 5.3.1
	 *
	 * @return string
	 */
	public static function get_invalid_order_message() {
		return esc_html__( 'Invalid order.', 'woocommerce-square' );
	}

	/**
	 * Order key from AJAX POST, or from the pay-for-order URL query string on full page requests.
	 *
	 * @since 5.3.1
	 *
	 * @return string
	 */
	public static function get_request_order_key() {
		if ( isset( $_POST['order_key'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			return sanitize_text_field( wp_unslash( $_POST['order_key'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		}

		if ( isset( $_GET['key'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return sanitize_text_field( wp_unslash( $_GET['key'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		}

		return '';
	}

	/**
	 * Order key for frontend scripts on the order-pay page.
	 *
	 * @since 5.3.1
	 *
	 * @return string
	 */
	public static function get_order_key_for_frontend_localization() {
		if ( ! is_checkout() || ! is_wc_endpoint_url( 'order-pay' ) ) {
			return '';
		}

		if ( ! isset( $_GET['key'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return '';
		}

		return sanitize_text_field( wp_unslash( $_GET['key'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
	}

	/**
	 * Whether the current request may load the given order in pay-for-order AJAX context.
	 *
	 * @since 5.3.1
	 *
	 * @param \WC_Order|false $order Order object or false.
	 * @return bool
	 */
	public static function is_authorized_for_pay_for_order( $order ) {
		if ( ! is_a( $order, \WC_Order::class ) ) {
			return false;
		}

		$order_key = self::get_request_order_key();

		if ( ! $order->key_is_valid( $order_key ) ) {
			return false;
		}

		$order_customer_id = (int) $order->get_user_id();

		// Guest orders (user_id 0) only need a valid key.
		if ( ! $order_customer_id ) {
			return true;
		}

		// Logged-in order owner.
		if ( get_current_user_id() === $order_customer_id ) {
			return true;
		}

		// Respect the WordPress capability system. Allows site-level user_has_cap filters
		// to grant pay_for_order (e.g. a snippet that authorises guests via the order key).
		// Note: AJAX requests do not include $_GET['key']; filters that verify the order key
		// should read it from $_POST['order_key'] instead, which is always present here.
		return current_user_can( 'pay_for_order', $order->get_id() );
	}

	/**
	 * Order ID for payment line-item builders: non-zero only when pay-for-order context applies and authorization passes.
	 *
	 * On normal checkout AJAX, a forged `order_id` must be ignored (returns 0). When the pay-for-order endpoint
	 * is active or `is_pay_for_order_page` is posted as true, returns the sanitized ID for the order being paid.
	 * Uses `is_authorized_for_pay_for_order()` to check if the order is authorized to be paid.
	 *
	 * @since 5.3.1
	 *
	 * @param int $order_id Order ID from POST or query.
	 * @return int
	 */
	public static function trusted_line_items_order_id( $order_id ) {
		$order_id = (int) $order_id;
		if ( $order_id <= 0 ) {
			return 0;
		}

		$on_order_pay_endpoint = is_wc_endpoint_url( 'order-pay' );

		$post_claims_pay_for_order = false;
		if ( isset( $_POST['is_pay_for_order_page'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			$post_claims_pay_for_order = 'true' === sanitize_text_field( wp_unslash( $_POST['is_pay_for_order_page'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		} elseif ( isset( $_POST['is_pay_order'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing
			$post_claims_pay_for_order = 'true' === sanitize_key( wp_unslash( $_POST['is_pay_order'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Missing
		}

		if ( ! $on_order_pay_endpoint && ! $post_claims_pay_for_order ) {
			return 0;
		}

		$order = wc_get_order( $order_id );
		if ( ! self::is_authorized_for_pay_for_order( $order ) ) {
			return 0;
		}

		return $order_id;
	}
}
