<?php
/**
 * Completes a PayPal connection from the onboarding return redirect.
 *
 * @since   1.1.0
 * @package DonationRouter
 */

declare( strict_types=1 );
namespace DonationRouter;

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

/**
 * On the accounts page load, reads merchantIdInPayPal + our ador_state from the
 * PayPal onboarding redirect, merges the merchant ID, and completes the save via
 * PayPal_Connect_Handler. A poll endpoint covers the case where the sendBeacon
 * codes arrive after the redirect.
 *
 * @since 1.1.0
 */
class PayPal_Connect_Redirect_Handler {

	/**
	 * AJAX action for the completion poll.
	 *
	 * @since 1.1.0
	 *
	 * @var string
	 */
	public const ACTION_COMPLETE = 'ador_connect_complete';

	/**
	 * Connect handler owning the pending record + completion routine.
	 *
	 * @since 1.1.0
	 *
	 * @var PayPal_Connect_Handler
	 */
	private PayPal_Connect_Handler $connect;

	/**
	 * Initialise dependencies.
	 *
	 * @since 1.1.0
	 *
	 * @param PayPal_Connect_Handler $connect Connect handler.
	 */
	public function __construct( PayPal_Connect_Handler $connect ) {
		$this->connect = $connect;
	}

	/**
	 * Register hooks.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function register_hooks(): void {
		add_action( 'admin_init', array( $this, 'maybe_complete' ) );
		add_action( 'wp_ajax_' . self::ACTION_COMPLETE, array( $this, 'handle_complete' ) );
	}

	/**
	 * On page load, if this is the onboarding return, merge the merchant ID and
	 * complete — redirecting on success, showing an interstitial while codes are
	 * still in flight, or an error notice on failure.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function maybe_complete(): void {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- ador_state is an unguessable server-issued token matched against a live pending record.
		if ( ! isset( $_GET['ador_state'], $_GET['merchantIdInPayPal'] ) ) {
			return;
		}
		if ( ! current_user_can( Admin_Settings::CAPABILITY ) ) {
			return;
		}
		$state       = sanitize_text_field( wp_unslash( (string) $_GET['ador_state'] ) );
		$merchant_id = sanitize_text_field( wp_unslash( (string) $_GET['merchantIdInPayPal'] ) );
		// phpcs:enable WordPress.Security.NonceVerification.Recommended

		if ( '' === $state || ! $this->connect->store_merchant( $state, $merchant_id ) ) {
			return; // No live pending record — ignore.
		}

		$result = $this->connect->complete_onboarding( $state );

		if ( 'done' === $result['status'] ) {
			$this->notice( 'success', __( 'PayPal account connected.', 'alamin-donation-router' ) );
			wp_safe_redirect( (string) ( $result['redirect'] ?? $this->connect->accounts_url() ) );
			exit;
		}

		if ( 'error' === $result['status'] ) {
			$this->notice( 'error', (string) ( $result['message'] ?? '' ) );
			wp_safe_redirect( $this->connect->accounts_url() );
			exit;
		}

		$this->render_interstitial( $state );
		exit;
	}

	/**
	 * AJAX: re-attempt completion while the beacon codes catch up.
	 *
	 * @since 1.1.0
	 *
	 * @return void
	 */
	public function handle_complete(): void {
		if ( ! current_user_can( Admin_Settings::CAPABILITY ) ) {
			wp_send_json_error(
				array(
					'status'  => 'error',
					'message' => __( 'Permission denied.', 'alamin-donation-router' ),
				),
				403
			);
		}
		check_ajax_referer( PayPal_Connect_Handler::NONCE_ACTION, 'nonce' );

		// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- verified above.
		$state  = sanitize_text_field( wp_unslash( (string) ( $_GET['state'] ?? '' ) ) );
		$result = $this->connect->complete_onboarding( $state );

		if ( 'done' === $result['status'] ) {
			$this->notice( 'success', __( 'PayPal account connected.', 'alamin-donation-router' ) );
		}

		wp_send_json_success( $result );
	}

	/**
	 * Store an admin notice for the next page load.
	 *
	 * @since 1.1.0
	 *
	 * @param string $type    'success' | 'error'.
	 * @param string $message Notice text.
	 *
	 * @return void
	 */
	private function notice( string $type, string $message ): void {
		set_transient(
			'ador_admin_notice_' . get_current_user_id(),
			array(
				'type'    => $type,
				'message' => $message,
			),
			60
		);
	}

	/**
	 * Render a minimal page that polls until the beacon codes arrive.
	 *
	 * @since 1.1.0
	 *
	 * @param string $state State token.
	 *
	 * @return void
	 */
	private function render_interstitial( string $state ): void {
		$poll_url = add_query_arg(
			array(
				'action' => self::ACTION_COMPLETE,
				'state'  => $state,
				'nonce'  => wp_create_nonce( PayPal_Connect_Handler::NONCE_ACTION ),
			),
			admin_url( 'admin-ajax.php' )
		);

		$heading = __( 'Completing your PayPal connection…', 'alamin-donation-router' );
		$fail    = __( 'This is taking longer than expected. Please try connecting again.', 'alamin-donation-router' );

		echo '<!doctype html><meta charset="utf-8"><title>' . esc_html( $heading ) . '</title>';
		echo '<body style="font-family:sans-serif;padding:40px"><p id="ador-msg">' . esc_html( $heading ) . '</p>';
		printf(
			'<script>(function(){var u=%s,f=%s,n=0;function p(){fetch(u,{credentials:"same-origin"}).then(function(r){return r.json()}).then(function(res){var d=res&&res.data;if(d&&d.status==="done"){window.location=d.redirect;return}if(d&&d.status==="error"){document.getElementById("ador-msg").textContent=d.message;return}if(++n>18){document.getElementById("ador-msg").textContent=f;return}setTimeout(p,800)}).catch(function(){if(++n>18){document.getElementById("ador-msg").textContent=f}else{setTimeout(p,800)}})}p()})();</script>',
			wp_json_encode( $poll_url ),
			wp_json_encode( $fail )
		);
		echo '</body>';
	}
}
