<?php

namespace Burst\Traits;

use Burst\Admin\Share\Share;
use Burst\Frontend\Ip\Ip;
use Burst\Frontend\Share\Share_Expired;
use function Burst\burst_loader;
use function burst_is_logged_in_rest;

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

/**
 * Trait admin helper
 *
 * @since   3.0
 */
trait Admin_Helper {
	use Helper;

	/**
	 * Explicit no-op for upgrade versions without Pro-specific data changes.
	 */
	private function mark_noop_upgrade( string $target_version, string $previous_version ): void {
		do_action( 'burst_noop_upgrade', $target_version, $previous_version );
	}

	/**
	 * Check if user has Burst view permissions
	 *
	 * @param \WP_REST_Request|null $request The dispatched REST request, when available. Passed by REST permission callbacks so shared-link scope is resolved from the route WordPress actually dispatches, not from spoofable request params.
	 * @return boolean true or false
	 */
	protected function user_can_view( ?\WP_REST_Request $request = null ): bool {
		if ( isset( burst_loader()->user_can_view ) ) {
			return burst_loader()->user_can_view;
		}

		if ( $this->is_mainwp_request() && current_user_can( 'view_burst_statistics' ) ) {
			return burst_loader()->user_can_view = true;
		}

		if ( ! is_user_logged_in() ) {
			return burst_loader()->user_can_view = false;
		}

		if ( ! current_user_can( 'view_burst_statistics' ) ) {
			return burst_loader()->user_can_view = false;
		}

		// For shared links, only allow access when the current shared-dashboard tab
		// being accessed is allowed for the active share token.
		if ( self::is_shareable_link_viewer() ) {
			$token = burst_loader()->admin->share->tokens->get_current_token();

			if ( empty( $token ) ) {
				return burst_loader()->user_can_view = false;
			} else {
				// Do not cache the result for shared links since it depends on the endpoint path,
				// which could change during a batch REST request.
				return burst_loader()->admin->share->routing->current_shared_request_tab_is_allowed( $request );
			}
		}

		return burst_loader()->user_can_view = true;
	}

	/**
	 * Check if user has Burst view sales permissions
	 *
	 * @param \WP_REST_Request|null $request The dispatched REST request, when available. Passed by REST permission callbacks so shared-link scope is resolved from the route WordPress actually dispatches, not from spoofable request params.
	 * @return boolean true or false
	 */
	protected function user_can_view_sales( ?\WP_REST_Request $request = null ): bool {
		if ( isset( burst_loader()->user_can_view_sales ) ) {
			return burst_loader()->user_can_view_sales;
		}

		if ( $this->is_mainwp_request() && current_user_can( 'view_burst_statistics' ) ) {
			return burst_loader()->user_can_view_sales = true;
		}

		if ( ! is_user_logged_in() ) {
			return burst_loader()->user_can_view_sales = false;
		}

		// For shared links, only allow ecommerce access when the resolved endpoint
		// tab is allowed and that tab is a sales-capable tab.
		if ( self::is_shareable_link_viewer() ) {

			$token = burst_loader()->admin->share->tokens->get_current_token();

			if ( empty( $token ) ) {
				return burst_loader()->user_can_view_sales = false;
			} else {
				return burst_loader()->admin->share->routing->current_shared_request_tab_is_allowed( $request );
			}
		}

		if ( ! current_user_can( 'view_sales_burst_statistics' ) ) {
			return burst_loader()->user_can_view_sales = false;
		}

		return burst_loader()->user_can_view_sales = true;
	}

	/**
	 * Verify if this is an authenticated rest request for Burst
	 */
	protected function is_logged_in_rest(): bool {
		if ( isset( burst_loader()->is_logged_in_rest ) ) {
			return burst_loader()->is_logged_in_rest;
		}

		burst_loader()->is_logged_in_rest = burst_is_logged_in_rest();
		return burst_loader()->is_logged_in_rest;
	}

	/**
	 * Check if we're on the Burst page
	 */
	protected function is_burst_page(): bool {
		if ( $this->is_logged_in_rest() ) {
			return true;
		}

		if ( ! isset( $_SERVER['QUERY_STRING'] ) ) {
			return false;
		}

		parse_str( sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) ), $params );
		if ( array_key_exists( 'page', $params ) && ( $params['page'] === 'burst' ) ) {
			return true;
		}

		return false;
	}

	/**
	 * Create a website URL with optional parameters.
	 *               Example usage:
	 *               utm_content=page-analytics -> specifies that the user is interacting with the page analytics feature.
	 *               utm_source=download-button -> indicates that the click originated from the download button.
	 */
	protected function get_website_url( string $url = '/', array $params = [] ): string {
		$version    = defined( 'BURST_PRO' ) ? 'pro' : 'free';
		$version_nr = defined( 'BURST_VERSION' ) ? BURST_VERSION : '0';

		// strip debug time from version nr.
		$default_params = [
			'utm_campaign' => 'burst-' . $version . '-' . $version_nr,
		];

		$params              = wp_parse_args( $params, $default_params );
		$plugin_installed_by = get_site_option( 'teamupdraft_installation_source_burst-statistics', '' );
		if ( ! empty( $plugin_installed_by ) ) {
			$params['utm_source'] = 'onboarding-' . $plugin_installed_by;
		}

		// remove slash prepending the $url.
		$url = ltrim( $url, '/' );

		return add_query_arg( $params, 'https://burst-statistics.com/' . trailingslashit( $url ) );
	}

	/**
	 * Validate a share token
	 */
	public static function validate_share_token( string $token ): bool {
		if ( ! preg_match( '/^[a-f0-9]{32}$/i', $token ) ) {
			return false;
		}

		$existing_tokens = get_option( 'burst_share_tokens', [] );
		$valid           = false;
		$current_time    = time();
		foreach ( $existing_tokens as $key => $token_data ) {
			if ( $token_data['expires'] !== 0 && $token_data['expires'] < $current_time ) {
				// Token expired, remove it.
				unset( $existing_tokens[ $key ] );
				continue;
			}
			if ( $token_data['token'] === $token ) {
				$valid = true;
				break;
			}
		}
		$request_count = (int) get_transient( "burst_shared_link_request_count_$token" );
		++$request_count;
		set_transient( "burst_shared_link_request_count_$token", $request_count, MINUTE_IN_SECONDS );

		if ( $request_count > apply_filters( 'burst_max_shared_link_requests', 100 ) ) {
			// Exceeded max requests for this shared link.
			self::error_log( "Shared link token $token has exceeded max requests: $request_count" );
			return false;
		}

		// Update the option to remove expired tokens.
		update_option( 'burst_share_tokens', $existing_tokens );
		return $valid;
	}

	/**
	 * Checks if the user has admin access to the Burst plugin.
	 */
	protected function has_admin_access( bool $allow_track_only = false ): bool {
		if ( isset( burst_loader()->has_admin_access ) ) {
			return burst_loader()->has_admin_access;
		}

		if ( ! $allow_track_only && BURST_TRACK_ONLY ) {
			return false;
		}

		// Check fast-paths that don't require user/caps.
		if ( wp_doing_cron() || ( defined( 'WP_CLI' ) && WP_CLI ) || burst_is_logged_in_rest() ) {
			return burst_loader()->has_admin_access = true;
		}

		// The share token is a nonce in itself with an expiry.
        // phpcs:ignore
		if ( isset( $_GET['burst_share_token'] ) && self::validate_share_token( wp_unslash( $_GET['burst_share_token'] ) ) ) {
			return burst_loader()->has_admin_access = true;
		}

		// Only check caps in admin; avoids loading user on frontend.
		if ( is_admin() ) {
			// Avoids double calls; still loads user once if needed.
			if ( is_user_logged_in() && current_user_can( 'view_burst_statistics' ) ) {
				return burst_loader()->has_admin_access = true;
			}
		}

		if ( $this->is_mainwp_request() ) {
			return burst_loader()->has_admin_access = true;
		}

		if (
			self::is_burst_rest_request_path()
			&& self::is_http_basic_auth_request()
			&& self::is_confirmed_application_password_auth()
		) {
			return burst_loader()->has_admin_access = true;
		}

		return burst_loader()->has_admin_access = false;
	}

	/**
	 * Check if the current user has the 'burst_viewer' role.
	 */
	private static function is_shareable_link_viewer(): bool {
		if ( isset( burst_loader()->is_shareable_link_viewer ) ) {
			return burst_loader()->is_shareable_link_viewer;
		}
		$user = wp_get_current_user();
		return burst_loader()->is_shareable_link_viewer = in_array( 'burst_viewer', $user->roles, true );
	}

	/**
	 * Get share link permissions
	 *
	 * @return array <string, bool> Associative array of share link permissions.
	 */
	private static function get_share_link_permissions(): array {
		// if the current user is NOT a shareable link viewer, it is a normal user with full permissions.
		$is_shareable_link_viewer = self::is_shareable_link_viewer();
		return apply_filters(
			'burst_share_link_permissions',
			[
				'can_change_date'          => ! $is_shareable_link_viewer,
				'can_filter'               => ! $is_shareable_link_viewer,
				'is_shareable_link_viewer' => $is_shareable_link_viewer,
			]
		);
	}

	/**
	 * Prepare localized settings data to expose to JavaScript.
	 *
	 * @param array $js_data Array of loaded translations.
	 * @return array{
	 *     burst_version: string,
	 *     is_pro: bool,
	 *     plugin_url: string,
	 *     installed_by: string,
	 *     site_url: string,
	 *     admin_ajax_url: string,
	 *     dashboard_url: string,
	 *     network_link: string,
	 *     nonce: string,
	 *     burst_nonce: string,
	 *     current_ip: string,
	 *     user_roles: array<string, string>,
	 *     view_sales_burst_statistics: bool,
	 *     manage_burst_statistics: bool,
	 *     can_install_plugins: bool,
	 *     is_shareable_link_viewer: bool,
	 *     json_translations: list<array<string, mixed>>,
	 *     date_format: string,
	 *     gmt_offset: float|int|string,
	 *     burst_activation_time: int,
	 *     date_ranges: array<int, string>,
	 *     tour_shown: int
	 * }
	 */
	protected function localized_settings( array $js_data ): array {
		$user_can_install = current_user_can( 'install_plugins' );

		$goal_count = 0;
		global $wpdb;
		$table_name   = $wpdb->prefix . 'burst_goals';
		$table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $table_name ) ) === $table_name;

		if ( $table_exists ) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			$goal_count = (int) $wpdb->get_var( "SELECT COUNT(*) FROM {$table_name} WHERE status = 'active'" );
		}

		$is_pro_valid = burst_license_is_valid();
		$goal_limit   = $is_pro_valid ? -1 : \Burst\Frontend\Goals\Goal::LIMIT_FREE;

		return apply_filters(
			'burst_localize_script',
			[
				// Core plugin information.
				'burst_version'                        => BURST_VERSION,
				'is_pro'                               => defined( 'BURST_PRO' ),
				'goal_count'                           => $goal_count,
				'goal_limit'                           => $goal_limit,
				'plugin_url'                           => BURST_URL,
				'installed_by'                         => get_site_option( 'teamupdraft_installation_source_burst-statistics', '' ),

				// URLs and endpoints.
				'rest_url'                             => get_rest_url(),
				'site_url'                             => defined( 'BURST_HEADLESS_DOMAIN' ) ? esc_url_raw( BURST_HEADLESS_DOMAIN ) : get_site_url(),
				'admin_ajax_url'                       => add_query_arg( [ 'action' => 'burst_rest_api_fallback' ], admin_url( 'admin-ajax.php' ) ),
				'dashboard_url'                        => $this->admin_url( 'burst' ),
				'network_link'                         => network_site_url( 'plugins.php' ),

				// Security and authentication.
				'nonce'                                => wp_create_nonce( 'wp_rest' ),
				'burst_nonce'                          => wp_create_nonce( 'burst_nonce' ),
				'current_ip'                           => Ip::get_ip_address(),

				// User permissions and capabilities.
				'user_roles'                           => $this->get_user_roles(),
				'view_sales_burst_statistics'          => $this->user_can_view_sales(),
				'manage_burst_statistics'              => $this->user_can_manage(),
				'can_install_plugins'                  => $user_can_install,
				'share_link_permissions'               => self::get_share_link_permissions(),

				// Localization and internationalization.
				'json_translations'                    => $js_data['json_translations'],
				'locale'                               => str_replace( '_', '-', function_exists( 'determine_locale' ) ? determine_locale() : get_locale() ),
				'date_format'                          => get_option( 'date_format' ),
				'gmt_offset'                           => get_option( 'gmt_offset' ),
				'burst_activation_time'                => (int) get_option( 'burst_activation_time', 1640995200 ),

				// Configuration and options.
				'date_ranges'                          => $this->get_date_ranges(),
				'time_format'                          => get_option( 'time_format' ),
				// Date picker's starting date.
				'burst_date_picker_start_date'         => (int) get_option( 'burst_activation_time', 1640995200 ),
				'external_links_first_cycle_completed' => (int) get_option( 'burst_external_links_last_completed', 0 ) > 0,
			]
		);
	}

	/**
	 * Get admin url. We don't use a different URL for multisite, as there is no network settings page.
	 */
	protected function admin_url( string $page = '' ): string {
		if ( isset( burst_loader()->admin_url ) ) {
			$url = burst_loader()->admin_url;
		} else {
			$url                      = admin_url( 'admin.php' );
			burst_loader()->admin_url = $url;
		}

		if ( ! empty( $page ) ) {
			$url = add_query_arg( 'page', $page, $url );
		}
		return $url;
	}

	/**
	 * Get user roles for the settings page in Burst.
	 *
	 * @return array<string, string> Associative array of role slugs and their translated names.
	 */
	protected function get_user_roles(): array {
		if ( ! $this->user_can_manage() ) {
			return [];
		}

		global $wp_roles;

		return $wp_roles->get_names();
	}

	/**
	 * Check if user has Burst manage permissions
	 *
	 * @return boolean true or false
	 */
	protected function user_can_manage(): bool {
		// Check if we already have a cached result.
		if ( isset( burst_loader()->user_can_manage ) ) {
			return burst_loader()->user_can_manage;
		}

		if ( $this->is_mainwp_request() && current_user_can( 'manage_burst_statistics' ) ) {
			return burst_loader()->user_can_manage = true;
		}

		// Allow access during cron jobs and WP-CLI.
		$is_wp_cli = ( defined( 'WP_CLI' ) && WP_CLI );
		if ( wp_doing_cron() || $is_wp_cli ) {
			burst_loader()->user_can_manage = true;
			return true;
		}

		// Check if user is logged in.
		if ( ! is_user_logged_in() ) {
			burst_loader()->user_can_manage = false;
			return false;
		}

		// Check if user has the required capability.
		if ( ! current_user_can( 'manage_burst_statistics' ) ) {
			burst_loader()->user_can_manage = false;
			return false;
		}

		burst_loader()->user_can_manage = true;
		return true;
	}

	/**
	 * Get possible date ranges for the date picker.
	 *
	 * @return array<int, string> List of available date range keys.
	 */
	protected function get_date_ranges(): array {
		return apply_filters(
			'burst_date_ranges',
			[
				'today',
				'yesterday',
				'last-7-days',
				'last-30-days',
				'last-90-days',
				'last-month',
				'last-year',
				'week-to-date',
				'month-to-date',
				'year-to-date',
			]
		);
	}

	/**
	 * Add some additional sanitizing.
	 * https://developer.wordpress.org/news/2023/08/understand-and-use-wordpress-nonces-properly/#verifying-the-nonce
	 *
	 * @param string|null $nonce  The nonce value to verify.
	 * @param string      $action The nonce action string.
	 * @return bool Whether the nonce is valid.
	 */
	protected function verify_nonce( ?string $nonce, string $action ): bool {
		// Application Passwords authenticate via HTTP Basic Auth, making CSRF nonces redundant.
		// Scope the skip to the actual auth mechanism of *this* request: the `did_action` flag
		// is global per request, so on its own it would also skip nonce checks in cookie-auth
		// paths that happen after an earlier app-password authentication in the same request.
		if ( self::is_http_basic_auth_request() && (bool) did_action( 'application_password_did_authenticate' ) ) {
			return true;
		}
		if ( empty( $nonce ) ) {
			return false;
		}
		$valid = wp_verify_nonce( sanitize_text_field( wp_unslash( $nonce ) ), $action );
		return apply_filters( 'burst_verify_nonce', $valid, $nonce, $action );
	}

	/**
	 * Whether the current request carries an HTTP Basic Authorization header.
	 *
	 * Used to confirm that the *current* request is being authenticated by credentials
	 * (e.g. an Application Password) rather than by cookies — so a CSRF nonce is not
	 * required to prove user intent.
	 */
	private static function is_http_basic_auth_request(): bool {
		// unslashed and sanitized later in this function.
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash
		$raw = $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? '';
		if ( ! is_string( $raw ) || $raw === '' ) {
			return false;
		}
		$header = sanitize_text_field( wp_unslash( $raw ) );
		return stripos( $header, 'basic ' ) === 0;
	}

	/**
	 * Whether the current request targets a Burst REST API endpoint.
	 *
	 * This is used to avoid running Burst-specific auth probing on unrelated
	 * REST routes (e.g. WooCommerce), which can taint global REST auth status.
	 */
	private static function is_burst_rest_request_path(): bool {
		// unslashed and sanitized later in this function.
		// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash
		$raw_uri = $_SERVER['REQUEST_URI'] ?? '';
		if ( ! is_string( $raw_uri ) || $raw_uri === '' ) {
			return false;
		}

		$uri = sanitize_url( wp_unslash( $raw_uri ) );

		// Parse the path and query from URI.
		$parsed_url = wp_parse_url( $uri );
		$path       = $parsed_url['path'] ?? '';
		$query      = $parsed_url['query'] ?? '';

		$burst_namespaces = [ 'burst/v1', 'wp-abilities/v1' ];

		// 1. Check if it's a pretty permalink REST request.
		$rest_prefix = 'wp-json';
		if ( function_exists( 'rest_get_url_prefix' ) ) {
			$rest_prefix = rest_get_url_prefix();
		}

		foreach ( $burst_namespaces as $namespace ) {
			if ( $rest_prefix !== '' && strpos( $path, '/' . $rest_prefix . '/' . $namespace ) !== false ) {
				return true;
			}
			if ( strpos( $path, '/wp-json/' . $namespace ) !== false ) {
				return true;
			}
		}

		// 2. Check if it's a query parameter rest route request (for non-pretty permalinks).
		if ( $query !== '' ) {
			wp_parse_str( $query, $query_args );
			$rest_route = $query_args['rest_route'] ?? '';
			if ( is_string( $rest_route ) && $rest_route !== '' ) {
				foreach ( $burst_namespaces as $namespace ) {
					if ( strpos( $rest_route, '/' . $namespace ) === 0 ) {
						return true;
					}
				}
			}
		}

		return false;
	}

	/**
	 * Confirm that the current Basic Auth request is authenticated via Application Passwords
	 * AND that the authenticated user is allowed to view Burst statistics.
	 *
	 * At early bootstrap points the `application_password_did_authenticate` action may
	 * not have fired yet, so we also perform an explicit validation fallback. In both
	 * paths the user must hold the `view_burst_statistics` capability before access is granted.
	 */
	private static function is_confirmed_application_password_auth(): bool {
		if ( ! self::is_burst_rest_request_path() ) {
			return false;
		}

		// Application Password auth only confirms *which* user is making the request; it does not
		// verify that user has any Burst capability. Granting Burst admin access purely on a valid
		// app password would let any logged-in user (e.g. a subscriber) be treated as a trusted
		// admin/REST caller. Require the Burst view capability so this check also enforces
		// authorization, not just authentication.
		if ( (bool) did_action( 'application_password_did_authenticate' ) ) {
			return current_user_can( 'view_burst_statistics' );
		}

		if ( ! isset( $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'] ) ) {
			return false;
		}

		if ( ! function_exists( 'wp_validate_application_password' ) ) {
			return false;
		}

		add_filter( 'application_password_is_api_request', '__return_true', 99 );
		$validated_user_id = wp_validate_application_password( false );
		remove_filter( 'application_password_is_api_request', '__return_true', 99 );

		if ( empty( $validated_user_id ) ) {
			return false;
		}

		wp_set_current_user( (int) $validated_user_id );
		return current_user_can( 'view_burst_statistics' );
	}

	/**
	 * We use this custom sprintf for outputting translatable strings. This function only works with %s
	 * This function wraps the sprintf and will prevent fatal errors.
	 */
	protected function sprintf(): string {
		$args   = func_get_args();
		$format = $args[0];
		$passed = array_slice( $args, 1 );

		// Find all numbered placeholders (%1$s, %2$d, etc).
		preg_match_all( '/%(\d+)\$/', $format, $matches );

		if ( ! empty( $matches[1] ) ) {
			$max_index    = max( $matches[1] );
			$passed_count = count( $passed );

			// If we have enough args for the highest placeholder index, run sprintf.
			if ( $passed_count >= $max_index ) {
				return sprintf( ...$args );
			}

			return $format . ' (Translation error)';
		}

		// Fallback for old-style %s %d etc.
		$expected = preg_match_all( '/%(?!%)[a-zA-Z]/', $format );
		if ( $expected === count( $passed ) ) {
			return sprintf( ...$args );
		}

		return $format . ' (Translation error)';
	}

	/**
	 * WordPress doesn't allow for translation of chunks resulting of code splitting.
	 * Several workarounds have popped up in JetPack and WooCommerce: https://developer.wordpress.com/2022/01/06/wordpress-plugin-i18n-webpack-and-composer/
	 * Below is mainly based on the WooCommerce solution, which seems to be the most simple approach. Simplicity is king here.
	 *
	 * @param string $dir Directory path relative to BURST_PATH.
	 * @return array{
	 *     json_translations: mixed,
	 *     js_file: string,
	 *     dependencies: list<string>,
	 *     version: string
	 * }
	 */
	protected function get_chunk_translations( string $dir ): array {
		$default           = [
			'json_translations' => [],
			'js_file'           => '',
			'dependencies'      => [],
			'version'           => '',
		];
		$text_domain       = 'burst-statistics';
		$languages_dir     = defined( 'BURST_PRO' ) ? BURST_PATH . 'languages' : WP_CONTENT_DIR . '/languages/plugins';
		$json_translations = [];
		$locale            = determine_locale();
		$languages         = [];

		if ( is_dir( $languages_dir ) ) {
			// Get all JSON files matching text domain & locale.
			foreach ( glob( "$languages_dir/{$text_domain}-{$locale}-*.json" ) as $language_file ) {
				$languages[] = basename( $language_file );
			}
		}

		foreach ( $languages as $src ) {
			$hash = str_replace( [ $text_domain . '-', $locale . '-', '.json' ], '', $src );
			wp_register_script( $hash, plugins_url( $src, __FILE__ ), [], true, true );
			$locale_data = load_script_textdomain( $hash, $text_domain, $languages_dir );
			wp_deregister_script( $hash );

			if ( ! empty( $locale_data ) ) {
				$json_translations[] = $locale_data;
			}
		}
		$js_files       = glob( BURST_PATH . $dir . '/index*.js' );
		$asset_files    = glob( BURST_PATH . $dir . '/index*.asset.php' );
		$js_filename    = ! empty( $js_files ) ? basename( $js_files[0] ) : '';
		$asset_filename = ! empty( $asset_files ) ? basename( $asset_files[0] ) : '';
		if ( ! file_exists( BURST_PATH . $dir . '/' . $asset_filename ) ) {
			return $default;
		}
		$asset_file = require BURST_PATH . $dir . '/' . $asset_filename;

		if ( empty( $js_filename ) ) {
			return $default;
		}

		return [
			'json_translations' => $json_translations,
			'js_file'           => $js_filename,
			'dependencies'      => $asset_file['dependencies'],
			'version'           => $asset_file['version'],
		];
	}

	/**
	 * Check if this is a MainWP request.
	 *
	 * @return bool True if authenticated via signature/Application Password.
	 */
	protected function is_mainwp_request(): bool {
		if ( isset( burst_loader()->is_mainwp_request ) ) {
			return burst_loader()->is_mainwp_request;
		}

		if ( isset( $_SERVER['HTTP_X_BURSTMAINWP'] ) && $_SERVER['HTTP_X_BURSTMAINWP'] === '1' ) {
			$mainwp_proxy = new \Burst\Frontend\MainWP_Proxy();
			if ( $mainwp_proxy->is_mainwp_authenticated() || $mainwp_proxy->is_mainwp_signed_request() ) {
				return burst_loader()->is_mainwp_request = true;
			}
		}

		return burst_loader()->is_mainwp_request = false;
	}
}
