<?php
/**
 * Elite Licenser integration for WP PowerSuite.
 *
 * Faithful readable port of AppsBD W_P_Power_Suite_Premium_Base validation,
 * encrypted response caching, soft remote rechecks, and deactivation behavior.
 *
 * @package WP_PowerSuite
 * @since 1.0.0
 */

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

/**
 * @since 1.0.0
 */
class W_P_Power_Suite_Premium_Base {

	private const ENCRYPT_KEY    = 'FA03DBD2C835B950';
	private const PRODUCT_ID     = '4';
	private const PRODUCT_BASE   = 'WP_PowerSuite_Premium';
	private const SERVER_HOST    = 'https://wppowersuite.com/wp-json/licensor/';
	private const LIC_KEY_OPTION          = 'wp_powersuite_lic_key';
	private const REMOTE_REQUEST_TIMEOUT  = 15;

	/**
	 * Resolve the Elite Licenser API endpoint (Settings → API End Point in Elite Licenser).
	 */
	public static function get_server_host(): string {
		if ( defined( 'WP_POWERSUITE_LICENSE_SERVER' ) && is_string( WP_POWERSUITE_LICENSE_SERVER ) && WP_POWERSUITE_LICENSE_SERVER !== '' ) {
			return trailingslashit( WP_POWERSUITE_LICENSE_SERVER );
		}

		return (string) apply_filters( 'wp_powersuite_license_server', self::SERVER_HOST );
	}

	/**
	 * Request encryption key from the Elite Licenser product settings.
	 */
	private static function get_encrypt_key(): string {
		if ( defined( 'WP_POWERSUITE_LICENSE_ENCRYPT_KEY' ) && is_string( WP_POWERSUITE_LICENSE_ENCRYPT_KEY ) && WP_POWERSUITE_LICENSE_ENCRYPT_KEY !== '' ) {
			return WP_POWERSUITE_LICENSE_ENCRYPT_KEY;
		}

		return (string) apply_filters( 'wp_powersuite_license_encrypt_key', self::ENCRYPT_KEY );
	}

	/**
	 * Elite Licenser product ID.
	 */
	public static function get_product_id(): string {
		if ( defined( 'WP_POWERSUITE_LICENSE_PRODUCT_ID' ) && (string) WP_POWERSUITE_LICENSE_PRODUCT_ID !== '' ) {
			return (string) WP_POWERSUITE_LICENSE_PRODUCT_ID;
		}

		return (string) apply_filters( 'wp_powersuite_license_product_id', self::PRODUCT_ID );
	}

	/** @var callable[] */
	private static array $on_delete_callbacks = [];

	/** @var self|null */
	private static ?self $instance = null;

	/** @var object|null|false Decrypted register cache for this request (false = not loaded). */
	private $register_response_cache = false;

	/**
	 * Whether a same-site license HTTP request is already in flight.
	 *
	 * Used only as a safety latch if a filter re-enters license transport.
	 * Elite Licenser API handlers call echo+die, so in-process rest_do_request()
	 * must never be used — it dumps ciphertext into admin HTML and aborts the page.
	 */
	private static bool $same_site_http_in_progress = false;

	private string $plugin_file   = '';
	private string $email_address = '';
	private string $version       = '1.0.0';

	/**
	 * Build the domain-scoped license option name.
	 */
	public static function get_lic_key_param( string $lic_key_name ): string {
		$raw_url = preg_replace( '#^https?://#', '', self::get_raw_domain() );

		return $lic_key_name . '_s' . hash( 'crc32b', $raw_url );
	}

	/**
	 * Site URL sent to Elite Licenser for activation binding.
	 */
	public static function get_raw_domain(): string {
		if ( function_exists( 'site_url' ) ) {
			return (string) site_url();
		}

		if ( function_exists( 'home_url' ) ) {
			return (string) home_url( '/' );
		}

		return '';
	}

	/**
	 * Validate license with the remote Elite Licenser server.
	 *
	 * @param bool $force_remote When true, contact the server even if the local cache window has not elapsed.
	 * @param bool $strict_remote When true with force_remote, do not fall back to local cache on server errors.
	 */
	public static function check_wp_plugin( string $license_key, string $license_email, &$message, &$response, string $plugin_file, bool $force_remote = false, bool $strict_remote = false ): bool {
		$instance = self::get_instance( $plugin_file );
		$instance->email_address = $license_email;

		return $instance->check_wp_plugin_internal( $license_key, $message, $response, $force_remote, $strict_remote );
	}

	/**
	 * Whether a license response object has passed its expiry date.
	 */
	public static function response_is_expired( ?object $response ): bool {
		if ( ! is_object( $response ) ) {
			return false;
		}

		$file = defined( 'WP_POWERSUITE_PLUGIN_FILE' ) ? WP_POWERSUITE_PLUGIN_FILE : '';
		if ( $file === '' ) {
			return false;
		}

		return self::get_instance( $file )->is_expired( $response );
	}

	/**
	 * Whether a license-server rejection message indicates expiry (not revocation).
	 */
	public static function message_indicates_expiration( string $message ): bool {
		$message = strtolower( trim( $message ) );
		if ( $message === '' ) {
			return false;
		}

		foreach ( [ 'revoked', 'ban', 'blocked', 'not found', 'wrong key', 'incorrect key', 'does not exist', 'deactivated', 'no activation', 'deleted' ] as $needle ) {
			if ( strpos( $message, $needle ) !== false ) {
				return false;
			}
		}

		foreach ( [ 'renew', 'expired', 'expiration', 'subscription ended', 'expire' ] as $needle ) {
			if ( strpos( $message, $needle ) !== false ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Persist a previously activated license response for local entitlement.
	 */
	public static function persist_register_info( string $plugin_file, object $response ): void {
		if ( empty( $response->license_key ) ) {
			return;
		}

		$instance               = self::get_instance( $plugin_file );
		$response->is_valid     = true;
		$response->next_request = strtotime( '+7 days' );
		unset( $response->tried );
		$instance->save_wp_response( $response );
		delete_transient( self::PRODUCT_BASE . '_up' );
	}

	/**
	 * Deactivate license for the current domain on the remote server.
	 */
	public static function remove_license_key( string $plugin_file, &$message ): bool {
		$instance = self::get_instance( $plugin_file );

		return $instance->remove_wp_plugin_license( $message );
	}

	/**
	 * Read the encrypted Elite Licenser cached response for this site/product.
	 */
	public static function get_register_info( ?string $plugin_file = null ): ?object {
		$file = $plugin_file ?? ( defined( 'WP_POWERSUITE_PLUGIN_FILE' ) ? WP_POWERSUITE_PLUGIN_FILE : '' );
		if ( $file === '' ) {
			return null;
		}

		return self::get_instance( $file )->get_old_wp_response();
	}

	/**
	 * Clear the encrypted Elite Licenser response cache (forces remote revalidation).
	 */
	public static function clear_cached_response( ?string $plugin_file = null ): void {
		$file = $plugin_file ?? ( defined( 'WP_POWERSUITE_PLUGIN_FILE' ) ? WP_POWERSUITE_PLUGIN_FILE : '' );
		if ( $file === '' ) {
			return;
		}

		self::get_instance( $file )->remove_old_wp_response( false );
	}

	/**
	 * Build renew URLs from the original Elite Licenser helper.
	 */
	public static function get_renew_link( object $response_obj, string $type = 's' ): string {
		return WPPS_Elite_Licenser_Client::get_renew_link( $response_obj, $type );
	}

	/**
	 * Elite Licenser product base slug (update transient prefix).
	 */
	public static function get_product_base(): string {
		return self::PRODUCT_BASE;
	}

	/**
	 * Installed plugin version sent to the update endpoint.
	 */
	public static function get_installed_version( ?string $plugin_file = null ): string {
		$file = $plugin_file ?? ( defined( 'WP_POWERSUITE_PLUGIN_FILE' ) ? WP_POWERSUITE_PLUGIN_FILE : '' );
		if ( $file === '' ) {
			return defined( 'WP_POWERSUITE_VERSION' ) ? (string) WP_POWERSUITE_VERSION : '1.0.0';
		}

		return self::get_instance( $file )->resolve_plugin_version( $file );
	}

	/**
	 * Encrypt arbitrary cache payload (Elite Licenser update transient).
	 */
	public static function encrypt_for_cache( string $plain_text ) {
		$file = defined( 'WP_POWERSUITE_PLUGIN_FILE' ) ? WP_POWERSUITE_PLUGIN_FILE : '';

		return self::get_instance( $file )->encrypt( $plain_text, '' );
	}

	/**
	 * Decrypt arbitrary cache payload (Elite Licenser update transient).
	 */
	public static function decrypt_for_cache( string $encrypted ) {
		$file = defined( 'WP_POWERSUITE_PLUGIN_FILE' ) ? WP_POWERSUITE_PLUGIN_FILE : '';

		return self::get_instance( $file )->decrypt( $encrypted, '' );
	}

	/**
	 * Decrypt an Elite Licenser update API body when Encrypt Update Response is enabled.
	 */
	public static function decrypt_update_body( string $encrypted ) {
		$file = defined( 'WP_POWERSUITE_PLUGIN_FILE' ) ? WP_POWERSUITE_PLUGIN_FILE : '';

		return self::get_instance( $file )->decrypt( $encrypted, '' );
	}

	public static function add_on_delete( callable $callback ): void {
		self::$on_delete_callbacks[] = $callback;
	}

	public static function run_on_delete_callbacks(): void {
		foreach ( self::$on_delete_callbacks as $callback ) {
			if ( is_callable( $callback ) ) {
				$callback();
			}
		}
	}

	/**
	 * Register remote revoke handler used by Elite Licenser server callbacks.
	 */
	public static function register_remote_handler(): void {
		// Do not intercept same-site licensor HTTP via rest_do_request().
		// Elite Licenser responses call echo+die; in-process dispatch leaks the
		// encrypted body into admin HTML and aborts the page (e.g. Dashboard).

		if ( ! function_exists( 'add_action' ) || has_action( 'init', [ self::class, 'handle_remote_init' ] ) ) {
			return;
		}

		add_action( 'init', [ self::class, 'handle_remote_init' ], 0 );
	}

	/**
	 * Whether this WordPress install is also the configured Elite Licenser host.
	 *
	 * Used for vendor-site admin UX only. This is NOT a license bypass —
	 * entitlement still requires a real activation. Same-site API calls use
	 * normal HTTP so Elite Licenser's echo+die stays isolated to the subrequest.
	 */
	public static function is_running_on_license_server(): bool {
		$instance = self::get_instance( defined( 'WP_POWERSUITE_PLUGIN_FILE' ) ? WP_POWERSUITE_PLUGIN_FILE : '' );

		return $instance->is_same_site_license_server();
	}

	public static function handle_remote_init(): void {
		$plugin_file = defined( 'WP_POWERSUITE_PLUGIN_FILE' ) ? WP_POWERSUITE_PLUGIN_FILE : '';
		if ( $plugin_file === '' ) {
			return;
		}

		$instance = self::get_instance( $plugin_file );
		$handler  = hash( 'crc32b', self::get_product_id() . self::get_encrypt_key() . $instance->get_domain() ) . '_handle';
		if ( ! isset( $_GET['action'] ) || sanitize_key( wp_unslash( $_GET['action'] ) ) !== $handler ) {
			return;
		}

		// Cache clear / status callbacks only — never mutate plugins from this
		// unauthenticated surface (action hash is derivable from shipped product constants).
		$instance->handle_server_request();
		exit;
	}

	private function __construct( string $plugin_file ) {
		$this->plugin_file = $plugin_file;
		$this->version     = $this->resolve_plugin_version( $plugin_file );
	}

	private static function get_instance( string $plugin_file ): self {
		if ( self::$instance === null || ( $plugin_file !== '' && self::$instance->plugin_file !== $plugin_file ) ) {
			self::$instance = new self( $plugin_file );
		}

		return self::$instance;
	}

	private function check_wp_plugin_internal( string $purchase_key, &$error, &$response_obj, bool $force_remote = false, bool $strict_remote = false ): bool {
		$error        = '';
		$response_obj = null;

		if ( $purchase_key === '' ) {
			$this->remove_old_wp_response();
			return false;
		}

		$old_response = $this->get_old_wp_response();

		if (
			! $force_remote
			&& is_object( $old_response )
			&& ! empty( $old_response->is_valid )
			&& ! empty( $old_response->next_request )
			&& (int) $old_response->next_request > time()
			&& ! empty( $old_response->license_key )
			&& hash_equals( (string) $old_response->license_key, $purchase_key )
		) {
			$response_obj = clone $old_response;
			unset( $response_obj->next_request, $response_obj->tried );
			return true;
		}

		$param    = $this->get_param( $purchase_key );
		$response = $this->request( 'product/active/' . self::get_product_id(), $param );

		if ( empty( $response->is_request_error ) ) {
			if ( empty( $response->code ) ) {
				if ( ! empty( $response->status ) ) {
					if ( ! empty( $response->data ) && is_string( $response->data ) ) {
						$serial      = $this->decrypt_license_payload( (string) $response->data, (string) $param->domain );
						$license_obj = $this->parse_license_object( $serial );
						if ( $this->license_object_is_valid( $license_obj ) ) {
							$response_obj                    = new \stdClass();
							$response_obj->is_valid          = true;
							$response_obj->next_request      = $this->calculate_next_request( $license_obj );
							$response_obj->expire_date       = isset( $license_obj->expire_date ) ? (string) $license_obj->expire_date : '';
							$response_obj->support_end       = isset( $license_obj->support_end ) ? (string) $license_obj->support_end : '';
							$response_obj->license_title     = isset( $license_obj->license_title ) ? (string) $license_obj->license_title : '';
							$response_obj->customer_name     = $this->extract_license_scalar( $license_obj, [ 'customer_name', 'buyer_name', 'name' ] );
							$response_obj->customer_email    = $this->extract_license_scalar( $license_obj, [ 'customer_email', 'email', 'lic_email', 'buyer_email' ] );
							$response_obj->plan              = $this->extract_license_scalar( $license_obj, [ 'plan', 'license_plan', 'license_type' ] );
							$response_obj->extra_param       = $this->extract_extra_param( $license_obj );
							$response_obj->license_key       = $purchase_key;
							$response_obj->msg               = isset( $response->msg ) ? (string) $response->msg : '';
							$response_obj->renew_link        = ! empty( $license_obj->renew_link ) ? (string) $license_obj->renew_link : '';
							$response_obj->expire_renew_link = self::get_renew_link( $response_obj, 'l' );
							$response_obj->support_renew_link = self::get_renew_link( $response_obj, 's' );

							$this->save_wp_response( $response_obj );
							delete_transient( self::PRODUCT_BASE . '_up' );

							unset( $response_obj->next_request, $response_obj->tried );
							return true;
						}

						$this->clear_cache_on_rejection( $old_response, $purchase_key, ! empty( $response->msg ) ? (string) $response->msg : __( 'License could not be validated.', 'wp-powersuite' ) );
						$error = ! empty( $response->msg ) ? (string) $response->msg : __( 'License could not be validated.', 'wp-powersuite' );
						return false;
					}

					$this->clear_cache_on_rejection( $old_response, $purchase_key, __( 'License server returned invalid data.', 'wp-powersuite' ) );
					$error = __( 'License server returned invalid data.', 'wp-powersuite' );
					return false;
				}

				$this->clear_cache_on_rejection( $old_response, $purchase_key, ! empty( $response->msg ) ? (string) $response->msg : __( 'License could not be validated.', 'wp-powersuite' ) );
				$error = ! empty( $response->msg ) ? (string) $response->msg : __( 'License could not be validated.', 'wp-powersuite' );
				return false;
			}

			$this->clear_cache_on_rejection( $old_response, $purchase_key, ! empty( $response->message ) ? (string) $response->message : __( 'License could not be validated.', 'wp-powersuite' ) );
			$error = ! empty( $response->message ) ? (string) $response->message : __( 'License could not be validated.', 'wp-powersuite' );
			return false;
		}

		if ( ! $strict_remote ) {
			if ( $this->check_old_tied( $old_response, $response_obj ) ) {
				return true;
			}

			if ( $this->restore_entitled_cache( $old_response, $purchase_key, $response_obj ) ) {
				return true;
			}
		}

		$error = ! empty( $response->msg ) ? (string) $response->msg : __( 'Could not reach the license server.', 'wp-powersuite' );
		return false;
	}

	private function remove_wp_plugin_license( &$message ): bool {
		$message      = '';
		$old_response = $this->get_old_wp_response();
		$remote_ok    = false;

		if ( is_object( $old_response ) && ! empty( $old_response->is_valid ) && ! empty( $old_response->license_key ) ) {
			$param    = $this->get_param( (string) $old_response->license_key );
			$response = $this->request( 'product/deactive/' . self::get_product_id(), $param );

			if ( empty( $response->code ) && ! empty( $response->status ) ) {
				$message   = ! empty( $response->msg ) ? (string) $response->msg : __( 'License deactivated.', 'wp-powersuite' );
				$remote_ok = true;
			} else {
				$remote_error = ! empty( $response->message )
					? (string) $response->message
					: ( ! empty( $response->msg ) ? (string) $response->msg : '' );
				$message      = $remote_error !== ''
					? $remote_error
					: __( 'License could not be deactivated on the license server.', 'wp-powersuite' );
			}
		}

		$this->remove_old_wp_response( false );
		delete_transient( self::PRODUCT_BASE . '_up' );

		if ( ! $remote_ok && $message !== '' && is_object( $old_response ) ) {
			$message = __( 'License was removed from this site. The license server could not confirm deactivation (it may already be removed there).', 'wp-powersuite' );
		} elseif ( $message === '' ) {
			$message = __( 'License deactivated.', 'wp-powersuite' );
		}

		return true;
	}

	private function get_param( string $purchase_key ): object {
		return (object) [
			'license_key'  => $purchase_key,
			'email'        => $this->email_address,
			'domain'       => $this->get_domain(),
			'app_version'  => $this->version,
			'product_id'   => self::get_product_id(),
			'product_base' => self::PRODUCT_BASE,
		];
	}

	private function get_domain(): string {
		return self::get_raw_domain();
	}

	private function get_key_name(): string {
		return hash( 'crc32b', $this->get_domain() . $this->plugin_file . self::get_product_id() . self::PRODUCT_BASE . self::get_encrypt_key() . 'LIC' );
	}

	private function save_wp_response( object $response ): void {
		$key  = $this->get_key_name();
		$data = $this->encrypt( serialize( $response ), $this->get_domain() );
		update_option( $key, $data, false );
		$this->register_response_cache = false;
	}

	private function get_old_wp_response(): ?object {
		if ( $this->register_response_cache !== false ) {
			return is_object( $this->register_response_cache ) ? $this->register_response_cache : null;
		}

		$key      = $this->get_key_name();
		$stored   = get_option( $key, null );
		if ( ! is_string( $stored ) || $stored === '' ) {
			$this->register_response_cache = null;
			return null;
		}

		$plain = $this->decrypt( $stored, $this->get_domain() );
		if ( $plain === false ) {
			$this->register_response_cache = null;
			return null;
		}

		$response = @unserialize( $plain, [ 'allowed_classes' => [ 'stdClass' ] ] );

		$this->register_response_cache = is_object( $response ) ? $response : null;

		return is_object( $response ) ? $response : null;
	}

	/**
	 * Clear the local activation cache on explicit server rejection unless the site was
	 * previously entitled and the rejection indicates expiry (grandfathered premium).
	 */
	private function clear_cache_on_rejection( ?object $old_response, string $purchase_key, string $message ): void {
		if (
			is_object( $old_response )
			&& ! empty( $old_response->is_valid )
			&& ! empty( $old_response->license_key )
			&& hash_equals( (string) $old_response->license_key, $purchase_key )
			&& ( self::message_indicates_expiration( $message ) || $this->is_expired( $old_response ) )
		) {
			return;
		}

		$this->remove_old_wp_response( false );
	}

	/**
	 * Remove the encrypted Elite Licenser response cache.
	 *
	 * @param bool $purge_credentials When true, also run on-delete callbacks that remove saved license credentials.
	 */
	private function remove_old_wp_response( bool $purge_credentials = true ): bool {
		$this->register_response_cache = false;
		$key        = $this->get_key_name();
		$is_deleted = delete_option( $key );

		if ( $purge_credentials ) {
			foreach ( self::$on_delete_callbacks as $callback ) {
				if ( is_callable( $callback ) ) {
					$callback();
				}
			}
		}

		return (bool) $is_deleted;
	}

	/**
	 * Trust the local activation cache when the license server cannot be reached.
	 */
	private function check_old_tied( ?object $old_response, ?object &$response_obj ): bool {
		if (
			! is_object( $old_response )
			|| empty( $old_response->is_valid )
			|| empty( $old_response->license_key )
		) {
			return false;
		}

		$old_response->next_request = strtotime( '+24 hours' );
		unset( $old_response->tried );
		$response_obj = clone $old_response;
		unset( $response_obj->next_request, $response_obj->tried );
		$this->save_wp_response( $old_response );

		return true;
	}

	/**
	 * Keep premium access for previously activated licenses (including expired subscriptions).
	 */
	private function restore_entitled_cache( ?object $old_response, string $purchase_key, ?object &$response_obj ): bool {
		if (
			! is_object( $old_response )
			|| empty( $old_response->is_valid )
			|| empty( $old_response->license_key )
			|| ! hash_equals( (string) $old_response->license_key, $purchase_key )
		) {
			return false;
		}

		$response_obj = clone $old_response;
		unset( $response_obj->next_request, $response_obj->tried );

		return true;
	}

	private function is_expired( object $response ): bool {
		if ( empty( $response->expire_date ) ) {
			return false;
		}

		$expire_str = strtolower( trim( (string) $response->expire_date ) );
		if ( in_array( $expire_str, [ 'no expiry', 'unlimited' ], true ) ) {
			return false;
		}

		$expire_ts = strtotime( (string) $response->expire_date );
		return $expire_ts !== false && $expire_ts < time();
	}

	private function calculate_next_request( object $license_obj ): int {
		$duration = isset( $license_obj->request_duration ) ? (int) $license_obj->request_duration : 168;
		if ( $duration > 0 ) {
			return strtotime( '+ ' . $duration . ' hour' );
		}

		return strtotime( '+7 days' );
	}

	private function request( string $relative_url, object $data ): object {
		$payload = wp_json_encode( $data );
		if ( ! is_string( $payload ) ) {
			return (object) [
				'status'           => false,
				'msg'              => __( 'Could not encode the license request.', 'wp-powersuite' ),
				'data'             => null,
				'is_request_error' => true,
			];
		}

		$body = $this->encrypt( $payload );
		if ( ! is_string( $body ) || $body === '' ) {
			return (object) [
				'status'           => false,
				'msg'              => __( 'Could not encrypt the license request.', 'wp-powersuite' ),
				'data'             => null,
				'is_request_error' => true,
			];
		}

		$url  = trailingslashit( self::get_server_host() ) . ltrim( $relative_url, '/' );
		$args = [
			'method'      => 'POST',
			'timeout'     => self::REMOTE_REQUEST_TIMEOUT,
			'redirection' => 5,
			'httpversion' => '1.0',
			'blocking'    => true,
			'sslverify'   => true,
			'headers'     => [],
			'body'        => $body,
			'cookies'     => [],
		];

		// Always use real HTTP — including on the vendor host. Elite Licenser
		// API handlers echo+die; in-process rest_do_request() would dump the
		// encrypted body into admin HTML and abort the page.
		$http_response = $this->http_request_with_ssl_fallback( $url, $args );
		if ( is_wp_error( $http_response ) ) {
			return (object) [
				'status'           => false,
				'msg'              => $http_response->get_error_message(),
				'data'             => null,
				'is_request_error' => true,
			];
		}

		$code     = (int) wp_remote_retrieve_response_code( $http_response );
		$raw_body = (string) wp_remote_retrieve_body( $http_response );
		if ( $code !== 200 || $raw_body === '' || $raw_body === 'GET404' ) {
			return (object) [
				'status'           => false,
				'msg'              => __( 'License server returned an empty response.', 'wp-powersuite' ),
				'data'             => null,
				'is_request_error' => true,
			];
		}

		return $this->process_response( $raw_body );
	}

	/**
	 * Perform an HTTP request for license diagnostics only.
	 *
	 * WP_POWERSUITE_LICENSE_ALLOW_INSECURE_SSL may retry with sslverify=false for
	 * license activate/check endpoints. It must never be used for update metadata
	 * or package downloads (see WPPS_License_Updater / WPPS_Package_Integrity).
	 *
	 * @param string               $url  Absolute URL.
	 * @param array<string,mixed>  $args wp_remote_* arguments (must include method).
	 * @return array|\WP_Error
	 */
	private function http_request_with_ssl_fallback( string $url, array $args ) {
		if ( self::$same_site_http_in_progress && $this->is_same_site_license_server() ) {
			return new \WP_Error( 'wp_powersuite_same_site_license_recursion', __( 'Internal license validation is already running.', 'wp-powersuite' ) );
		}

		$on_vendor = $this->is_same_site_license_server();
		if ( $on_vendor ) {
			self::$same_site_http_in_progress = true;
		}

		try {
			$method = strtoupper( (string) ( $args['method'] ?? 'GET' ) );
			// Never disable TLS for URLs that look like update/package endpoints.
			$path = (string) ( wp_parse_url( $url, PHP_URL_PATH ) ?? '' );
			$is_update_path = str_contains( $path, '/product/update' ) || str_contains( $path, '/download' );

			if ( $method === 'POST' ) {
				$response = wp_remote_post( $url, $args );
			} else {
				$response = wp_remote_get( $url, $args );
			}

			if (
				! $is_update_path
				&& is_wp_error( $response )
				&& defined( 'WP_POWERSUITE_LICENSE_ALLOW_INSECURE_SSL' )
				&& WP_POWERSUITE_LICENSE_ALLOW_INSECURE_SSL
			) {
				$args['sslverify'] = false;
				if ( $method === 'POST' ) {
					$response = wp_remote_post( $url, $args );
				} else {
					$response = wp_remote_get( $url, $args );
				}
			}

			return $response;
		} finally {
			if ( $on_vendor ) {
				self::$same_site_http_in_progress = false;
			}
		}
	}

	private function is_same_site_license_server(): bool {
		if ( defined( 'WP_POWERSUITE_SAME_SITE_LICENSE' ) && WP_POWERSUITE_SAME_SITE_LICENSE ) {
			return true;
		}

		$server_host = $this->normalize_host( wp_parse_url( self::get_server_host(), PHP_URL_HOST ) );
		$site_host   = $this->normalize_host( wp_parse_url( site_url(), PHP_URL_HOST ) );

		if ( $server_host === '' || $server_host !== $site_host ) {
			return false;
		}

		/**
		 * Filter whether this install is treated as the vendor/license-server host.
		 *
		 * Affects admin labeling only. Does not grant entitlement or change
		 * customer validation. License API calls always use real HTTP.
		 *
		 * @param bool $is_same_site Whether this site hosts Elite Licenser for this product.
		 */
		return (bool) apply_filters( 'wp_powersuite_is_same_site_license_server', true );
	}

	private function normalize_host( $host ): string {
		if ( ! is_string( $host ) ) {
			return '';
		}

		$host = strtolower( trim( $host ) );
		if ( strpos( $host, 'www.' ) === 0 ) {
			$host = substr( $host, 4 );
		}

		return $host;
	}

	private function process_response( string $payload ): object {
		$plain = $this->decrypt( $payload );
		if ( $plain === false ) {
			return (object) [
				'status' => false,
				'msg'    => __( 'License server returned an unexpected response.', 'wp-powersuite' ),
				'data'   => null,
			];
		}

		$decoded = json_decode( $plain );
		if ( is_object( $decoded ) ) {
			return $decoded;
		}

		return (object) [
			'status' => false,
			'msg'    => __( 'License server returned an unexpected response.', 'wp-powersuite' ),
			'data'   => null,
		];
	}

	private function encrypt( string $plain_text, string $password = '' ) {
		if ( $password === '' ) {
			$password = self::get_encrypt_key();
		}

		$plain_text = (string) wp_rand( 10, 99 ) . $plain_text . (string) wp_rand( 10, 99 );
		$key        = substr( hash( 'sha256', $password, true ), 0, 32 );
		$iv         = substr( strtoupper( md5( $password ) ), 0, 16 );
		$encrypted  = openssl_encrypt( $plain_text, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv );

		if ( ! is_string( $encrypted ) || $encrypted === '' ) {
			return false;
		}

		return base64_encode( $encrypted );
	}

	private function decrypt( string $encrypted, string $password = '' ) {
		if ( $password === '' ) {
			$password = self::get_encrypt_key();
		}

		$binary = base64_decode( $encrypted, true );
		if ( $binary === false || $binary === '' ) {
			return false;
		}

		$key       = substr( hash( 'sha256', $password, true ), 0, 32 );
		$iv        = substr( strtoupper( md5( $password ) ), 0, 16 );
		$plaintext = openssl_decrypt( $binary, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv );

		if ( ! is_string( $plaintext ) || strlen( $plaintext ) < 4 ) {
			return false;
		}

		return substr( $plaintext, 2, -2 );
	}

	/**
	 * Decrypt inner license payload, trying common site URL variants.
	 *
	 * @return string|false
	 */
	private function decrypt_license_payload( string $encrypted, string $primary_domain ) {
		foreach ( $this->domain_decrypt_candidates( $primary_domain ) as $domain ) {
			$plain = $this->decrypt( $encrypted, $domain );
			if ( is_string( $plain ) && $plain !== '' ) {
				return $plain;
			}
		}

		return false;
	}

	/**
	 * @return string[]
	 */
	private function domain_decrypt_candidates( string $primary_domain ): array {
		$candidates = [ $primary_domain ];

		if ( function_exists( 'home_url' ) ) {
			$candidates[] = (string) home_url( '/' );
			$candidates[] = (string) home_url();
		}

		if ( function_exists( 'site_url' ) ) {
			$candidates[] = (string) site_url( '/' );
			$candidates[] = (string) site_url();
		}

		$normalized = [];
		foreach ( $candidates as $candidate ) {
			$candidate = trim( (string) $candidate );
			if ( $candidate === '' ) {
				continue;
			}

			$normalized[] = $candidate;
			$normalized[] = untrailingslashit( $candidate );
			$normalized[] = trailingslashit( untrailingslashit( $candidate ) );
		}

		return array_values( array_unique( $normalized ) );
	}

	/**
	 * Parse decrypted license payload from Elite Licenser.
	 *
	 * @param string|false $serial
	 */
	private function parse_license_object( $serial ): ?object {
		if ( ! is_string( $serial ) || $serial === '' ) {
			return null;
		}

		$license_obj = @unserialize( $serial, [ 'allowed_classes' => [ 'stdClass' ] ] );

		return is_object( $license_obj ) ? $license_obj : null;
	}

	/**
	 * Whether decrypted license payload confirms validity (strict: is_valid only).
	 */
	private function license_object_is_valid( ?object $license_obj ): bool {
		if ( ! is_object( $license_obj ) || ! isset( $license_obj->is_valid ) ) {
			return false;
		}

		$value = $license_obj->is_valid;
		if ( is_bool( $value ) ) {
			return $value;
		}

		if ( is_numeric( $value ) ) {
			return (int) $value === 1;
		}

		return false;
	}

	/**
	 * Elite Licenser "Extra Param" from the license type (see license-type docs).
	 */
	private function extract_extra_param( object $license_obj ): string {
		return $this->extract_license_scalar( $license_obj, [ 'extra_param', 'extra', 'lic_extra_param' ] );
	}

	/**
	 * Read the first populated scalar field from an Elite Licenser license object.
	 *
	 * @param list<string> $keys Candidate property names.
	 */
	private function extract_license_scalar( object $license_obj, array $keys ): string {
		foreach ( $keys as $key ) {
			if ( ! empty( $license_obj->{$key} ) && is_scalar( $license_obj->{$key} ) ) {
				$value = (string) $license_obj->{$key };
				if ( in_array( $key, [ 'customer_email', 'email', 'lic_email', 'buyer_email' ], true ) ) {
					return sanitize_email( $value );
				}

				return sanitize_text_field( $value );
			}
		}

		return '';
	}

	private function encrypt_obj( object $obj ): string {
		return (string) $this->encrypt( serialize( $obj ) );
	}

	private function handle_server_request(): void {
		$type = isset( $_GET['type'] ) ? strtolower( sanitize_key( wp_unslash( $_GET['type'] ) ) ) : '';

		// Rate-limit predictable action hashes (rl/rc clear local license cache).
		if ( in_array( $type, [ 'rl', 'rc', 'dl' ], true ) && ! $this->allow_remote_license_callback() ) {
			status_header( 429 );
			exit;
		}

		switch ( $type ) {
			case 'rl':
				// Unauthenticated surface: clear update cache only. Never purge saved
				// license credentials — action hashes are derivable from ZIP constants.
				delete_transient( self::PRODUCT_BASE . '_up' );
				$this->remove_old_wp_response( false );
				$obj          = new \stdClass();
				$obj->product = self::get_product_id();
				$obj->status  = true;
				echo $this->encrypt_obj( $obj ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- encrypted binary response for Elite Licenser.
				return;

			case 'rc':
				// Same as rl: cache clear without credential deletion.
				delete_transient( self::PRODUCT_BASE . '_up' );
				$this->remove_old_wp_response( false );
				$obj          = new \stdClass();
				$obj->product = self::get_product_id();
				$obj->status  = true;
				echo $this->encrypt_obj( $obj ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- encrypted binary response for Elite Licenser.
				return;

			case 'dl':
				// Intentionally a no-op: remote deactivate/delete via this unauthenticated
				// callback was removed. Action hashes are derivable from shipped product
				// constants, so mutating plugins here would be privilege escalation.
				$obj          = new \stdClass();
				$obj->product = self::get_product_id();
				$obj->status  = false;
				$obj->message = 'remote_delete_disabled';
				echo $this->encrypt_obj( $obj ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- encrypted binary response for Elite Licenser.
				return;
		}
	}

	/**
	 * Allow at most a few remote license callbacks per IP per minute.
	 */
	private function allow_remote_license_callback(): bool {
		$ip  = isset( $_SERVER['REMOTE_ADDR'] )
			? sanitize_text_field( wp_unslash( (string) $_SERVER['REMOTE_ADDR'] ) )
			: '';
		$key = 'wpps_lic_cb_' . md5( $ip !== '' ? $ip : 'unknown' );
		$n   = (int) get_transient( $key );
		if ( $n >= 10 ) {
			return false;
		}
		set_transient( $key, $n + 1, MINUTE_IN_SECONDS );

		return true;
	}

	private function resolve_plugin_version( string $plugin_file ): string {
		if ( defined( 'WP_POWERSUITE_VERSION' ) ) {
			return (string) WP_POWERSUITE_VERSION;
		}

		if ( ! function_exists( 'get_plugin_data' ) ) {
			$plugin_admin = ABSPATH . 'wp-admin/includes/plugin.php';
			if ( is_readable( $plugin_admin ) ) {
				require_once $plugin_admin;
			}
		}

		if ( function_exists( 'get_plugin_data' ) && is_readable( $plugin_file ) ) {
			$data = get_plugin_data( $plugin_file, false, false );
			if ( ! empty( $data['Version'] ) ) {
				return (string) $data['Version'];
			}
		}

		return '1.0.0';
	}
}
