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

namespace HCaptcha\Admin;

use HCaptcha\Helpers\Request;
use HCaptcha\Settings\General;

/**
 * Class WhatsNew.
 *
 * Show a What's New popup in the admin.
 */
class WhatsNew extends NotificationsBase {

	/**
	 * Handle for assets.
	 */
	private const HANDLE = 'hcaptcha-whats-new';

	/**
	 * Script localization object.
	 */
	private const OBJECT = 'HCaptchaWhatsNewObject';

	/**
	 * Mark shown ajax action.
	 */
	private const MARK_SHOWN_ACTION = 'hcaptcha-mark-shown';

	/**
	 * Render popup ajax action.
	 */
	private const RENDER_POPUP_ACTION = 'hcaptcha-render-whats-new-popup';

	/**
	 * Settings key for a last shown What's New version.
	 */
	private const WHATS_NEW_KEY = 'whats_new_last_shown_version';

	/**
	 * Method prefix.
	 */
	private const PREFIX = 'whats_new_';

	/**
	 * Class is allowed to show the popup.
	 *
	 * @var bool
	 */
	protected bool $allowed = false;

	/**
	 * Constructor.
	 *
	 * Initializes the class and hooks.
	 */
	public function __construct() {
		$this->init();
	}

	/**
	 * Initializes the class by setting up hooks.
	 *
	 * @return void
	 */
	public function init(): void {
		$settings = hcaptcha()->settings();

		if ( 'completed' !== ( $settings ? $settings->get( OnboardingWizard::OPTION_NAME ) : '' ) ) {
			// Do not show the What's New popup if the onboarding wizard is not completed.
			return;
		}

		$this->init_hooks();
	}

	/**
	 * Init class hooks.
	 *
	 * @return void
	 */
	private function init_hooks(): void {
		add_action( 'kagg_settings_tab', [ $this, 'action_settings_tab' ] );
		add_action( 'admin_print_footer_scripts', [ $this, 'enqueue_assets' ], 9 );
		add_action( 'admin_footer', [ $this, 'maybe_show_popup' ] );
		add_action( 'wp_ajax_' . self::MARK_SHOWN_ACTION, [ $this, 'mark_shown' ] );
		add_action( 'wp_ajax_' . self::RENDER_POPUP_ACTION, [ $this, 'render_popup_ajax' ] );
		add_filter( 'update_footer', [ $this, 'update_footer' ], 1010 );
	}

	/**
	 * Settings tab action.
	 *
	 * @return void
	 */
	public function action_settings_tab(): void {
		$this->allowed = true;
	}

	/**
	 * Enqueue assets.
	 *
	 * @return void
	 */
	public function enqueue_assets(): void {
		if ( ! $this->allowed ) {
			return;
		}

		$min = hcap_min_suffix();

		wp_enqueue_style(
			self::HANDLE,
			constant( 'HCAPTCHA_URL' ) . "/assets/css/whats-new$min.css",
			[],
			constant( 'HCAPTCHA_VERSION' )
		);
		wp_enqueue_script(
			self::HANDLE,
			constant( 'HCAPTCHA_URL' ) . "/assets/js/whats-new$min.js",
			[ 'jquery' ],
			constant( 'HCAPTCHA_VERSION' ),
			true
		);
		wp_localize_script(
			self::HANDLE,
			self::OBJECT,
			[
				'ajaxUrl'           => admin_url( 'admin-ajax.php' ),
				'markShownAction'   => self::MARK_SHOWN_ACTION,
				'markShownNonce'    => wp_create_nonce( self::MARK_SHOWN_ACTION ),
				'renderPopupAction' => self::RENDER_POPUP_ACTION,
				'renderPopupNonce'  => wp_create_nonce( self::RENDER_POPUP_ACTION ),
			]
		);
	}

	/**
	 * Maybe show popup.
	 *
	 * @return void
	 */
	public function maybe_show_popup(): void {
		if ( ! $this->allowed ) {
			return;
		}

		$current       = explode( '-', constant( 'HCAPTCHA_VERSION' ) )[0];
		$settings      = hcaptcha()->settings();
		$whats_new_key = $settings ? $settings->get( self::WHATS_NEW_KEY ) : '';
		$shown         = $whats_new_key;
		$versions      = $this->get_versions();
		$method        = '';

		foreach ( $versions as $version ) {
			// Find the first news version that is less or equal to the current version.
			if ( version_compare( $version, $current, '<=' ) ) {
				$method = $this->version_to_method( $version );

				break;
			}
		}

		$display = version_compare( $shown, $this->method_to_version( $method ), '<' );

		$this->render_popup( $method, $display );
	}

	/**
	 * Ajax action to mark content as shown.
	 *
	 * @return void
	 */
	public function mark_shown(): void {
		// Run a security check.
		if ( ! check_ajax_referer( self::MARK_SHOWN_ACTION, 'nonce', false ) ) {
			wp_send_json_error( esc_html__( 'Your session has expired. Please reload the page.', 'hcaptcha-for-forms-and-more' ) );
		}

		// Check for permissions.
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( esc_html__( 'You are not allowed to perform this action.', 'hcaptcha-for-forms-and-more' ) );
		}

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

		$this->update_whats_new( $version );
		wp_send_json_success();
	}

	/**
	 * Ajax action to render a popup for a selected version.
	 *
	 * @return void
	 */
	public function render_popup_ajax(): void {
		// Run a security check.
		if ( ! check_ajax_referer( self::RENDER_POPUP_ACTION, 'nonce', false ) ) {
			wp_send_json_error( esc_html__( 'Your session has expired. Please reload the page.', 'hcaptcha-for-forms-and-more' ) );
		}

		// Check for permissions.
		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error( esc_html__( 'You are not allowed to perform this action.', 'hcaptcha-for-forms-and-more' ) );
		}

		$version = Request::filter_input( INPUT_POST, 'version' );
		$version = $this->normalize_version( $version );
		$method  = $this->version_to_method( $version );

		if ( ! $version || ! method_exists( $this, $method ) ) {
			wp_send_json_error( esc_html__( 'The requested version is not available.', 'hcaptcha-for-forms-and-more' ) );
		}

		ob_start();

		$this->render_popup( $method, true );

		$html = ob_get_clean();

		wp_send_json_success( [ 'html' => $html ] );
	}

	/**
	 * Show a new features link in the update footer.
	 *
	 * @param string|mixed $content The content that will be printed.
	 *
	 * @return string|mixed
	 */
	public function update_footer( $content ) {
		if ( ! $this->allowed ) {
			return $content;
		}

		$link = sprintf(
			'<a href="#" id="hcaptcha-whats-new-link" rel="noopener noreferrer">%1$s</a>',
			__( 'See the new features!', 'hcaptcha-for-forms-and-more' )
		);

		return $content . ' - ' . $link;
	}

	/**
	 * Render popup.
	 *
	 * @param string $method  Popup method.
	 * @param bool   $display Display popup.
	 *
	 * @return void
	 */
	protected function render_popup( string $method, bool $display ): void {
		if ( ! method_exists( $this, $method ) ) {
			return;
		}

		$display_attr = $display ? 'flex' : 'none';
		$version      = $this->method_to_version( $method );

		?>
		<div
				id="hcaptcha-whats-new-modal" class="hcaptcha-whats-new-modal"
				style="display: <?php echo esc_attr( $display_attr ); ?>;">
			<div class="hcaptcha-whats-new-modal-bg"></div>
			<div class="hcaptcha-whats-new-modal-popup">
				<button id="hcaptcha-whats-new-close" class="hcaptcha-whats-new-close"></button>
				<div class="hcaptcha-whats-new-header">
					<div class="hcaptcha-whats-new-icon">
						<img
								src="<?php echo esc_url( HCAPTCHA_URL . '/assets/images/hcaptcha-icon-animated.svg' ); ?>"
								alt="Icon">
					</div>
					<div class="hcaptcha-whats-new-title">
						<h1>
							<?php esc_html_e( "What's New in hCaptcha", 'hcaptcha-for-forms-and-more' ); ?>
							<?php $this->render_version_selector( $version ); ?>
						</h1>
					</div>
				</div>
				<div class="hcaptcha-whats-new-content">
					<?php $this->$method(); ?>
				</div>
			</div>
		</div>
		<div id="hcaptcha-lightbox-modal">
			<img id="hcaptcha-lightbox-img" src="" alt="lightbox-image">
		</div>
		<?php
	}

	/**
	 * Render a version selector.
	 *
	 * @param string $current_version Current version.
	 *
	 * @return void
	 */
	private function render_version_selector( string $current_version ): void {
		$versions = $this->get_versions();

		?>
		<span class="hcaptcha-whats-new-version-control">
			<span id="hcaptcha-whats-new-version"><?php echo esc_html( $current_version ); ?></span>
			<button
					type="button"
					class="hcaptcha-whats-new-version-toggle"
					aria-label="<?php esc_attr_e( "Select What's New version", 'hcaptcha-for-forms-and-more' ); ?>"
					aria-haspopup="true"
					aria-expanded="false"
					aria-controls="hcaptcha-whats-new-versions"></button>
			<ul id="hcaptcha-whats-new-versions" class="hcaptcha-whats-new-version-list" hidden>
				<?php foreach ( $versions as $version ) : ?>
					<li class="<?php echo esc_attr( $version === $current_version ? 'is-current' : '' ); ?>">
						<a
								href="#"
								class="hcaptcha-whats-new-version-link"
								data-version="<?php echo esc_attr( $version ); ?>">
							<?php echo esc_html( $version ); ?>
						</a>
					</li>
				<?php endforeach; ?>
			</ul>
		</span>
		<?php
	}

	/**
	 * What's New 4.13.0 content.
	 *
	 * @return void
	 * @noinspection HtmlUnknownTarget
	 * @noinspection PhpUnused
	 */
	protected function whats_new_4_13_0(): void {
		$urls = $this->prepare_urls();

		$block1 = [
			'type'    => 'center',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'Site Content Protection', 'hcaptcha-for-forms-and-more' ),
			'message' => sprintf(
				'<p>%1$s</p><p>%2$s</p>',
				sprintf(
				/* translators: 1: Pro link. */
					__( 'Protect selected site URLs from bots with hCaptcha. Works best with %1$s 99.9%% passive mode.', 'hcaptcha-for-forms-and-more' ),
					sprintf(
						'<a href="%1$s" target="_blank">%2$s</a>',
						$urls['dashboard'],
						__( 'Pro', 'hcaptcha-for-forms-and-more' )
					)
				),
				__( 'Set up protected URLs to prevent these pages from being accessed by bots.', 'hcaptcha-for-forms-and-more' )
			),
			'button'  => [
				'url'  => $urls['protect_content'],
				'text' => __( 'Protect Content', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['protect_content_demo'],
				'lightbox' => true,
			],
		];

		$block2 = [
			'type'    => 'center',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'Friction-Free “No CAPTCHA” & 99.9% Passive Modes', 'hcaptcha-for-forms-and-more' ),
			'message' =>
				sprintf(
				/* translators: 1: Pro link, 2: size select link. */
					__( '%1$s and use %2$s. The hCaptcha widget will not appear, and the Challenge popup will be shown only to bots.', 'hcaptcha-for-forms-and-more' ),
					sprintf(
						'<a href="%1$s" target="_blank">%2$s</a>',
						$urls['dashboard'],
						__( 'Upgrade to Pro', 'hcaptcha-for-forms-and-more' )
					),
					sprintf(
						'<a href="%1$s" target="_blank">%2$s</a>',
						$urls['size'],
						__( 'Invisible Size', 'hcaptcha-for-forms-and-more' )
					)
				),
			'button'  => [
				'url'  => $urls['dashboard'],
				'text' => __( 'Upgrade to Pro', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['passive_mode_demo'],
				'lightbox' => true,
			],
		];

		$this->show_block( $block1 );
		$this->show_block( $block2 );
	}

	/**
	 * What's New 4.18.0 content.
	 *
	 * @return void
	 * @noinspection HtmlUnknownTarget
	 * @noinspection PhpUnused
	 */
	protected function whats_new_4_18_0(): void {
		$urls = $this->prepare_urls();

		$block = [
			'type'    => 'center',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'Honeypot and Minimum Submit Time', 'hcaptcha-for-forms-and-more' ),
			'message' => sprintf(
				'<p>%1$s</p><p>%2$s</p><p>%3$s</p>',
				sprintf(
				/* translators: 1: Pro link. */
					__( 'Added a hidden %1$s field for bot detection before processing hCaptcha.', 'hcaptcha-for-forms-and-more' ),
					sprintf(
						'<a href="%1$s" target="_blank">%2$s</a>',
						$urls['honeypot'],
						__( 'honeypot', 'hcaptcha-for-forms-and-more' )
					)
				),
				sprintf(
				/* translators: 1: Pro link. */
					__( 'Added minimum form %1$s for bot detection before processing hCaptcha.', 'hcaptcha-for-forms-and-more' ),
					sprintf(
						'<a href="%1$s" target="_blank">%2$s</a>',
						$urls['token'],
						__( 'submit time', 'hcaptcha-for-forms-and-more' )
					)
				),
				__( 'Currently supported for WordPress Core, Protect Content feature, and all integrations having more than 100,000 installs: Avada theme, Blocksy, Brevo, CoBlocks, Contact Form 7, Divi Builder, Divi theme, Download Manager, Elementor, Essential Addons for Elementor, Essential Blocks, Extra theme, Fluent Forms, Formidable Forms, Forminator, GiveWP Form, Gravity Forms, Jetpack, Kadence, MailPoet, Mailchimp, Ninja Forms, Otter, Password Protected, Protect Content feature, Spectra, Ultimate Addons for Elementor, WPForms, WooCommerce, and Wordfence.', 'hcaptcha-for-forms-and-more' )
			),
			'button'  => [
				'url'  => $urls['honeypot'],
				'text' => __( 'Turn on honeypot', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['honeypot_demo'],
				'lightbox' => true,
			],
		];

		$this->show_block( $block );
	}

	/**
	 * What's New 4.20.0 content.
	 *
	 * @return void
	 * @noinspection HtmlUnknownTarget
	 * @noinspection PhpUnused
	 */
	protected function whats_new_4_20_0(): void {
		$urls = $this->prepare_urls();

		$block = [
			'type'    => 'center',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'Onboarding Wizard', 'hcaptcha-for-forms-and-more' ),
			'message' => sprintf(
				'<p>%1$s</p><p>%2$s</p>',
				__( 'Added an onboarding wizard for new users.', 'hcaptcha-for-forms-and-more' ),
				__( 'You can restart it anytime from the plugin-generated onboarding link.', 'hcaptcha-for-forms-and-more' )
			),
			'button'  => [
				'url'  => $urls['onboarding'],
				'text' => __( 'Restart wizard', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['onboarding_demo'],
				'lightbox' => true,
			],
		];

		$this->show_block( $block );
	}

	/**
	 * What's New 4.21.0 content.
	 *
	 * @return void
	 * @noinspection HtmlUnknownTarget
	 * @noinspection PhpUnused
	 */
	protected function whats_new_4_21_0(): void {
		$urls = $this->prepare_urls();

		$block = [
			'type'    => 'left',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'AI-Ready Security Actions', 'hcaptcha-for-forms-and-more' ),
			'message' => sprintf(
				'<p>%1$s</p><p>%2$s</p><p>%3$s</p><ul><li>%4$s</li><li>%5$s</li></ul>',
				__( 'hCaptcha for WordPress now exposes selected security capabilities via the WordPress Abilities API — a machine-readable interface designed for automation tools and AI agents.', 'hcaptcha-for-forms-and-more' ),
				__( 'This enables programmatic threat monitoring and response workflows without relying on custom REST endpoints or UI automation.', 'hcaptcha-for-forms-and-more' ),
				__( 'Two initial abilities are included:', 'hcaptcha-for-forms-and-more' ),
				__( 'Threat snapshot (aggregated metrics and top offenders)', 'hcaptcha-for-forms-and-more' ),
				__( 'Privacy-safe blocking based on hashed offender identifiers', 'hcaptcha-for-forms-and-more' )
			),
			'button'  => [
				'url'  => $urls['ai_abilities'],
				'text' => __( 'Read documentation', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['ai_abilities_img'],
				'lightbox' => true,
			],
		];

		$this->show_block( $block );
	}

	/**
	 * What's New 4.24.0 content.
	 *
	 * @return void
	 * @noinspection HtmlUnknownTarget
	 * @noinspection PhpUnused
	 */
	protected function whats_new_4_24_0(): void {
		$urls = $this->prepare_urls();

		$block_4_24_0 = [
			'type'    => 'left',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'Country Access Control', 'hcaptcha-for-forms-and-more' ),
			'message' => sprintf(
				'<p>%1$s</p><p>%2$s</p><p>%3$s</p>',
				__( 'You can now control where hCaptcha protection applies using country-level allowlist and denylist rules.', 'hcaptcha-for-forms-and-more' ),
				__( 'Allowlisted countries can bypass hCaptcha, while denylisted countries can be blocked from submitting protected forms.', 'hcaptcha-for-forms-and-more' ),
				__( 'Use this to tailor protection by region and reduce abuse from unwanted traffic sources.', 'hcaptcha-for-forms-and-more' )
			),
			'button'  => [
				'url'  => $urls['country_access'],
				'text' => __( 'Open general settings', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['country_access_img'],
				'lightbox' => false,
			],
		];

		$block_4_23_0 = [
			'type'    => 'right',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'Settings Export and Import', 'hcaptcha-for-forms-and-more' ),
			'message' => sprintf(
				'<p>%1$s</p><p>%2$s</p><p>%3$s</p>',
				__( 'You can now export and import hCaptcha settings directly from the admin Tools page for easier backup and migration.', 'hcaptcha-for-forms-and-more' ),
				__( 'The same workflow is available in WP-CLI via the <code>wp hcaptcha export</code> and <code>wp hcaptcha import</code> commands.', 'hcaptcha-for-forms-and-more' ),
				__( 'Automation tools and AI agents can also export and import settings using the WordPress Abilities API.', 'hcaptcha-for-forms-and-more' )
			),
			'button'  => [
				'url'  => $urls['tools'],
				'text' => __( 'Open tools', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['export_import_img'],
				'lightbox' => true,
			],
		];

		$this->show_block( $block_4_24_0 );
		$this->show_block( $block_4_23_0 );
	}

	/**
	 * What's New 4.25.0 content.
	 *
	 * @return void
	 * @noinspection HtmlUnknownTarget
	 * @noinspection PhpUnused
	 */
	protected function whats_new_4_25_0(): void {
		$urls = $this->prepare_urls();

		$block_4_25_0 = [
			'type'    => 'left',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'Migration Wizard', 'hcaptcha-for-forms-and-more' ),
			'message' => sprintf(
				'<p>%1$s</p><p>%2$s</p><p>%3$s</p>',
				__( 'Switching from reCAPTCHA or Turnstile? The new Migration Wizard makes it effortless.', 'hcaptcha-for-forms-and-more' ),
				__( 'It automatically scans your site for existing CAPTCHA plugins, detects the surfaces they protect, and enables hCaptcha on those surfaces with a single click.', 'hcaptcha-for-forms-and-more' ),
				__( 'Find the Migration Wizard on the Tools page and migrate in seconds.', 'hcaptcha-for-forms-and-more' )
			),
			'button'  => [
				'url'  => $urls['migration_wizard'],
				'text' => __( 'Open Migration Wizard', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['migration_wizard_img'],
				'lightbox' => true,
			],
		];

		$this->show_block( $block_4_25_0 );
	}

	/**
	 * What's New 5.0.0 content.
	 *
	 * @return void
	 * @noinspection HtmlUnknownTarget
	 * @noinspection PhpUnused
	 */
	protected function whats_new_5_0_0(): void {
		$urls = $this->prepare_urls();

		$block_trusted_ip_headers = [
			'type'    => 'left',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'Trusted IP Headers', 'hcaptcha-for-forms-and-more' ),
			'message' => sprintf(
				'<p>%1$s</p><p>%2$s</p><p>%3$s</p>',
				__( 'hCaptcha now includes a dedicated setting for choosing which IP headers your site should use when it runs behind a proxy, CDN, or load balancer.', 'hcaptcha-for-forms-and-more' ),
				__( 'By default, hCaptcha continues to use REMOTE_ADDR. When Cloudflare is detected, the settings page can suggest CF-Connecting-IP for sites that use Cloudflare proxying.', 'hcaptcha-for-forms-and-more' ),
				__( 'If you already customized IP headers with the hcap_trusted_address_headers filter, your configuration is migrated automatically during upgrade.', 'hcaptcha-for-forms-and-more' )
			),
			'button'  => [
				'url'  => $urls['trusted_ip_headers'],
				'text' => __( 'Open Access Control', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['trusted_ip_headers_img'],
				'lightbox' => true,
			],
		];

		$block_forms_events_trash = [
			'type'    => 'right',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'Trash for Forms and Events', 'hcaptcha-for-forms-and-more' ),
			'message' => sprintf(
				'<p>%1$s</p><p>%2$s</p><p>%3$s</p>',
				__( 'Forms and Events statistics now include Trash, giving admins more flexibility when cleaning up old records.', 'hcaptcha-for-forms-and-more' ),
				__( 'Deleted items can be restored or permanently deleted, and Trash is cleaned up automatically after 30 days.', 'hcaptcha-for-forms-and-more' ),
				__( 'Existing statistics tables are updated during upgrade.', 'hcaptcha-for-forms-and-more' )
			),
			'button'  => [
				'url'  => $urls['forms_events_trash'],
				'text' => __( 'Open statistics', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['forms_events_trash_img'],
				'lightbox' => true,
			],
		];

		$block_paypal_payments = [
			'type'    => 'left',
			'badge'   => __( 'New Feature', 'hcaptcha-for-forms-and-more' ),
			'title'   => __( 'WooCommerce PayPal Payments', 'hcaptcha-for-forms-and-more' ),
			'message' => sprintf(
				'<p>%1$s</p><p>%2$s</p>',
				__( 'hCaptcha now supports WooCommerce PayPal Payments express checkout flows on product, cart, mini-cart, and checkout pages.', 'hcaptcha-for-forms-and-more' ),
				__( 'The integration works with PayPal order creation so express checkout can use hCaptcha while keeping the buyer flow familiar.', 'hcaptcha-for-forms-and-more' )
			),
			'button'  => [
				'url'  => $urls['paypal_payments'],
				'text' => __( 'Open integrations', 'hcaptcha-for-forms-and-more' ),
			],
			'image'   => [
				'url'      => $urls['paypal_payments_img'],
				'lightbox' => true,
			],
		];

		$this->show_block( $block_trusted_ip_headers );
		$this->show_block( $block_forms_events_trash );
		$this->show_block( $block_paypal_payments );
	}

	/**
	 * Show block.
	 *
	 * @param array $block Block.
	 *
	 * @return void
	 */
	private function show_block( array $block ): void {
		$badge = $block['badge'] ?? '';

		if ( $badge ) {
			ob_start();

			?>
			<div class="hcaptcha-whats-new-badge">
				<?php echo esc_html( $badge ); ?>
			</div>
			<?php

			$badge = ob_get_clean();
		}

		?>
		<div class="hcaptcha-whats-new-block <?php echo esc_attr( $block['type'] ); ?>">
			<div class="hcaptcha-whats-new-text">
				<?php echo wp_kses_post( $badge ); ?>
				<h2>
					<?php echo esc_html( $block['title'] ); ?>
				</h2>
				<div class="hcaptcha-whats-new-message">
					<?php echo wp_kses_post( $block['message'] ); ?>
				</div>
				<div class="hcaptcha-whats-new-button">
					<a
							href="<?php echo esc_url( $block['button']['url'] ); ?>" class="button button-primary"
							target="_blank">
						<?php echo esc_html( $block['button']['text'] ); ?>
					</a>
				</div>
			</div>
			<div class="hcaptcha-whats-new-image">
				<?php if ( ! empty( $block['image']['url'] ) ) : ?>
					<?php if ( ! empty( $block['image']['lightbox'] ) ) : ?>
						<a href="<?php echo esc_url( $block['image']['url'] ); ?>" class="hcaptcha-lightbox">
							<img src="<?php echo esc_url( $block['image']['url'] ); ?>" alt="What's New block image">
						</a>
					<?php else : // @codeCoverageIgnoreStart ?>
						<img src="<?php echo esc_url( $block['image']['url'] ); ?>" alt="What's New block image">
					<?php endif; // @codeCoverageIgnoreEnd ?>
				<?php endif; ?>
			</div>
		</div>
		<?php
	}

	/**
	 * Update shown `What's New` version.
	 *
	 * @param string $version Version.
	 */
	protected function update_whats_new( string $version ): void {
		if ( ! $this->is_valid_version( $version ) ) {
			return;
		}

		$settings = hcaptcha()->settings();
		$tab      = $settings ? $settings->get_tab( General::class ) : null;

		if ( ! $tab ) {
			// @codeCoverageIgnoreStart
			return;
			// @codeCoverageIgnoreEnd
		}

		$tab->update_option( self::WHATS_NEW_KEY, $version );
	}

	/**
	 * Check if a version is valid.
	 *
	 * @param string $version Version.
	 *
	 * @return bool
	 */
	private function is_valid_version( string $version ): bool {
		return '' !== $this->normalize_version( $version );
	}

	/**
	 * Convert method name to version.
	 *
	 * @param string $method Method name.
	 *
	 * @return string
	 */
	private function method_to_version( string $method ): string {
		return str_replace( [ self::PREFIX, '_' ], [ '', '.' ], $method );
	}

	/**
	 * Get What's New versions listed in this class.
	 *
	 * @return string[]
	 */
	private function get_versions(): array {
		$prefix   = self::PREFIX;
		$methods  = array_filter(
			get_class_methods( $this ),
			static function ( $method ) use ( $prefix ) {
				return 0 === strpos( $method, $prefix );
			}
		);
		$versions = array_map(
			function ( $method ) {
				return $this->method_to_version( $method );
			},
			$methods
		);

		usort( $versions, 'version_compare' );

		return array_reverse( $versions );
	}

	/**
	 * Convert a version to a method name.
	 *
	 * @param string $version Version.
	 *
	 * @return string
	 */
	protected function version_to_method( string $version ): string {
		return self::PREFIX . str_replace( '.', '_', $version );
	}

	/**
	 * Normalize version string for version_compare().
	 *
	 * Accept:
	 *  - 8
	 *  - 8.1
	 *  - 8.1.2
	 *  - 8.1.2-RC1
	 *  - 8.1.2-dev
	 *
	 * Return an empty string if the string format is invalid.
	 *
	 * @param string $version Version string.
	 *
	 * @return string
	 */
	private function normalize_version( string $version ): string {
		$version = trim( $version );

		// ^ start with digits: X[.Y[.Z[.W]]]
		// optional pre-release suffix: -?(alpha|beta|RC|rc|dev)\d*
		$pattern = '/^\d+(?:\.\d+){0,3}(?:-?(?:alpha|beta|rc|RC|dev)\d*)?$/';

		if ( ! preg_match( $pattern, $version, $m ) ) {
			return '';
		}

		// Normalize RC case.
		return str_replace( 'rc', 'RC', $m[0] );
	}
}
