<?php
/**
 * The Checkout class file.
 *
 * @package hcaptcha-wp
 */

namespace HCaptcha\WC;

use HCaptcha\Helpers\API;
use HCaptcha\Helpers\HCaptcha;
use HCaptcha\Helpers\Request;
use HCaptcha\Helpers\Utils;
use WP_Error;
use WP_HTTP_Response;
use WP_REST_Request;
use WP_REST_Response;
use WP_Block;

/**
 * Class Checkout
 */
class Checkout {

	/**
	 * Nonce action.
	 */
	private const ACTION = 'hcaptcha_wc_checkout';

	/**
	 * Nonce name.
	 */
	private const NONCE = 'hcaptcha_wc_checkout_nonce';

	/**
	 * Script handle.
	 */
	private const HANDLE = 'hcaptcha-wc-checkout';

	/**
	 * Block script handle.
	 */
	private const BLOCK_HANDLE = 'hcaptcha-wc-block-checkout';

	/**
	 * The hCaptcha was added.
	 *
	 * @var bool
	 */
	private bool $captcha_added = false;

	/**
	 * The block hCaptcha was added.
	 *
	 * @var bool
	 */
	private bool $block_captcha_added = false;

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

	/**
	 * Init hooks.
	 *
	 * @return void
	 */
	private function init_hooks(): void {
		add_action( 'woocommerce_review_order_before_submit', [ $this, 'add_captcha' ] );
		add_filter( 'render_block', [ $this, 'add_block_captcha' ], 10, 3 );
		add_action( 'woocommerce_checkout_process', [ $this, 'verify' ] );
		add_filter( 'rest_request_before_callbacks', [ $this, 'verify_block' ], 10, 3 );
		add_action( 'wp_print_footer_scripts', [ $this, 'enqueue_scripts' ], 9 );
		add_filter( 'script_loader_tag', [ $this, 'add_type_module' ], 10, 3 );
	}

	/**
	 * Add captcha.
	 *
	 * @return void
	 */
	public function add_captcha(): void {
		$args = [
			'action' => self::ACTION,
			'name'   => self::NONCE,
			'id'     => [
				'source'  => HCaptcha::get_class_source( __CLASS__ ),
				'form_id' => 'checkout',
			],
		];

		HCaptcha::form_display( $args );

		$this->captcha_added = true;
	}

	/**
	 * Add captcha to the checkout block.
	 *
	 * @param string|mixed $block_content The block content.
	 * @param array        $block         The full block, including name and attributes.
	 * @param WP_Block     $instance      The block instance.
	 *
	 * @return string
	 *
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function add_block_captcha( $block_content, array $block, WP_Block $instance ): string {
		$block_content = (string) $block_content;

		if ( 'woocommerce/checkout' !== $block['blockName'] ) {
			return (string) $block_content;
		}

		$search = '<div data-block-name="woocommerce/checkout-actions-block" class="wp-block-woocommerce-checkout-actions-block"></div>';
		$args   = [
			'action' => self::ACTION,
			'name'   => self::NONCE,
			'id'     => [
				'source'  => HCaptcha::get_class_source( __CLASS__ ),
				'form_id' => 'checkout',
			],
		];

		$this->block_captcha_added = true;

		return str_replace( $search, HCaptcha::form( $args ) . $search, $block_content );
	}

	/**
	 * Verify checkout form.
	 *
	 * @return void
	 * @noinspection PhpUndefinedFunctionInspection
	 */
	public function verify(): void {
		$wc_ajax = Request::filter_input( INPUT_GET, 'wc-ajax' );
		$path    = Request::filter_input( INPUT_GET, 'path' );

		if ( 'wc_stripe_frontend_request' === $wc_ajax && '/wc-stripe/v1/checkout' === $path ) {
			return;
		}

		$payment_method = Request::filter_input( INPUT_POST, 'payment_method' );

		if ( $this->is_express_payment_method( $payment_method ) ) {
			return;
		}

		$error_message = API::verify_post( self::NONCE, self::ACTION );

		if ( null !== $error_message ) {
			wc_add_notice( $error_message, 'error' );
		}
	}

	/**
	 * Verify the checkout block.
	 *
	 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client.
	 *                                                                   Usually a WP_REST_Response or WP_Error.
	 * @param array                                            $handler  Route handler used for the request.
	 * @param WP_REST_Request                                  $request  Request used to generate the response.
	 *
	 * @return WP_REST_Response|WP_HTTP_Response|WP_Error|mixed
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function verify_block( $response, array $handler, WP_REST_Request $request ) {
		if ( '/wc/store/v1/checkout' !== $request->get_route() ) {
			return $response;
		}

		$method = $request->get_method();

		if ( 'POST' !== $method ) {
			return $response;
		}

		$body           = (string) $request->get_body();
		$body_arr       = Utils::json_decode_arr( $body );
		$payment_method = $body_arr['payment_method'] ?? '';

		if ( $this->is_express_payment_method( $payment_method ) ) {
			return $response;
		}

		$params         = $request->get_params();
		$widget_id_name = 'hcaptcha-widget-id';
		$response_name  = 'h-captcha-response';
		$hp_sig_name    = 'hcap_hp_sig';
		$token_name     = 'hcap_fst_token';
		$hp_name        = API::get_hp_name( $params );

		$_POST[ $widget_id_name ] = $request->get_param( $widget_id_name );
		$_POST[ $response_name ]  = $request->get_param( $response_name );
		$_POST[ $hp_sig_name ]    = $request->get_param( $hp_sig_name );
		$_POST[ $hp_name ]        = $request->get_param( $hp_name );
		$_POST[ $token_name ]     = $request->get_param( $token_name );

		$error_message = API::verify_request();

		if ( null === $error_message ) {
			return $response;
		}

		$code = array_search( $error_message, hcap_get_error_messages(), true ) ?: 'fail';

		return new WP_Error( $code, $error_message, 400 );
	}

	/**
	 * Check whether the payment method is an express payment method.
	 *
	 * @param string $payment_method Payment method.
	 *
	 * @return bool
	 * @noinspection PhpUndefinedFunctionInspection
	 */
	private function is_express_payment_method( string $payment_method ): bool {
		$express_payment_methods = [];

		foreach ( WC()->payment_gateways()->get_available_payment_gateways() as $gateway ) {
			if ( $gateway->supports( 'wc_stripe_banner_checkout' ) && $gateway->banner_checkout_enabled() ) {
				$express_payment_methods[] = $gateway->id;
			}
		}

		return in_array( $payment_method, $express_payment_methods, true );
	}

	/**
	 * Enqueue scripts.
	 *
	 * @return void
	 */
	public function enqueue_scripts(): void {
		$min = hcap_min_suffix();

		if ( $this->captcha_added ) {
			wp_enqueue_script(
				self::HANDLE,
				HCAPTCHA_URL . "/assets/js/hcaptcha-wc-checkout$min.js",
				[ 'jquery', 'hcaptcha' ],
				HCAPTCHA_VERSION,
				true
			);
		}

		if ( $this->block_captcha_added ) {
			wp_enqueue_script(
				self::BLOCK_HANDLE,
				HCAPTCHA_URL . "/assets/js/hcaptcha-wc-block-checkout$min.js",
				[ 'hcaptcha' ],
				HCAPTCHA_VERSION,
				true
			);
		}
	}

	/**
	 * Add the type="module" attribute to the script tag.
	 *
	 * @param string|mixed $tag    Script tag.
	 * @param string       $handle Script handle.
	 * @param string       $src    Script source.
	 *
	 * @return string
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function add_type_module( $tag, string $handle, string $src ): string {
		$tag = (string) $tag;

		if ( self::BLOCK_HANDLE !== $handle ) {
			return $tag;
		}

		return HCaptcha::add_type_module( $tag );
	}
}
