<?php

namespace ChangeLoginURL;

use WP_PowerSuite\Core\AbstractModule;
use WP_PowerSuite\Core\ModuleManager;
use WP_PowerSuite\Core\OptionStoreModuleSettingsRepository;

class Module extends AbstractModule {

	private const MODULE_ID = 'change-login-url';
	private const MODULE_VERSION = '2.2.2';

	/** @var list<string> */
	private const RESERVED_SLUGS = [
		'wp-admin',
		'wp-login',
		'wp-login.php',
		'wp-content',
		'wp-includes',
		'wp-json',
		'xmlrpc.php',
		'admin',
		'dashboard',
		'feed',
		'rss',
		'robots.txt',
		'login',
	];

	/** @var list<string> */
	private const LOGGED_IN_CUSTOM_LOGIN_ACTIONS = [
		'logout', 'postpass', 'confirmaction', 'confirm_admin_email', 'rp', 'resetpass',
		'export_personal_data', 'erase_personal_data',
		'validate_2fa', 'wpps_2fa', 'wpps_2fa_cancel', 'switch_to_user', 'switch_to_olduser',
	];

	/** @var array<string, mixed>|null */
	private ?array $settings = null;

	private ?string $custom_slug_cache = null;

	private ?string $home_path_prefix_cache = null;

	private ?string $relative_request_path_cache = null;

	public function getId(): string {
		return self::MODULE_ID;
	}

	public function getName(): string {
		return __( 'Change Login URL', 'wp-powersuite' );
	}

	public function getVersion(): string {
		return self::MODULE_VERSION;
	}

	public function getDescription(): string {
		return __( 'Use a custom login URL instead of the default one bots love to hammer; bookmark your new address.', 'wp-powersuite' );
	}

	public function getContext(): string|array {
		return 'both';
	}

	public function getDependencies(): array {
		return [];
	}

	protected function registerHooks(): void {
		add_action( 'wp_powersuite_module_toggled', [ $this, 'onModuleToggled' ], 10, 3 );
		add_action( 'wp_powersuite_module_settings_saved', [ $this, 'onSettingsSaved' ], 10, 2 );
		add_action( 'wp', [ $this, 'handleCustomLoginUrl' ], 0 );
		add_action( 'template_redirect', [ $this, 'maybeRedirectGravityFormsPreview' ], 0 );
		add_action( 'init', [ $this, 'blockDefaultLoginUrls' ], 1 );

		add_filter( 'wp_powersuite_is_custom_login_url_request', [ $this, 'filterIsCustomLoginUrlRequest' ], 10, 1 );
		add_filter( 'wp_powersuite_skip_404_path', [ $this, 'filterSkip404Path' ], 10, 2 );

		add_filter( 'site_url', [ $this, 'filterLoginUrl' ], 10, 3 );
		add_filter( 'network_site_url', [ $this, 'filterLoginUrl' ], 10, 3 );
		add_filter( 'wp_redirect', [ $this, 'filterRedirectUrl' ], 10, 2 );
		add_filter( 'logout_url', [ $this, 'filterLogoutUrl' ], 10, 2 );
		add_filter( 'login_url', [ $this, 'filterLoginUrlDirect' ], 10, 3 );
		add_filter( 'lostpassword_url', [ $this, 'filterLostPasswordUrl' ], 10, 2 );
		add_filter( 'register_url', [ $this, 'filterRegisterUrl' ], 10, 1 );
		add_filter( 'retrieve_password_message', [ $this, 'filterRetrievePasswordMessage' ], 10, 4 );

		if ( class_exists( 'WooCommerce', false ) ) {
		add_filter( 'woocommerce_lostpassword_url', [ $this, 'filterWooCommerceLostPasswordUrl' ], 10, 2 );
		}
	}


	/**
	 * Whether the request URI targets the custom login slug (same rules as {@see handleCustomLoginUrl()}).
	 *
	 * Exposed via {@see filterIsCustomLoginUrlRequest()} for other modules (e.g. Turnstile script timing).
	 *
	 * @param string $request_uri Raw request URI (e.g. $_SERVER['REQUEST_URI']).
	 * @return bool
	 */
	private function matchesCustomLoginRequestUri( string $request_uri ): bool {
		if ( $this->isStaticAssetRequest( $request_uri ) ) {
			return false;
		}

		$parsed_url = wp_parse_url( $request_uri );
		if ( ! $parsed_url || ! isset( $parsed_url['path'] ) ) {
			return false;
		}

		$full_path = trim( $parsed_url['path'], '/' );
		$home_path = $this->getHomePathPrefix();
		$path      = ( $home_path !== '' && strpos( $full_path, $home_path ) === 0 )
			? trim( substr( $full_path, strlen( $home_path ) ), '/' )
			: $full_path;

		$custom_slug = $this->getSlug();

		return $path === $custom_slug || str_starts_with( $path, $custom_slug . '/' );
	}

	/**
	 * Let PowerSuite modules detect the masked wp-login route before template output.
	 *
	 * @param bool $is Prior value from other callbacks.
	 * @return bool
	 */
	public function filterIsCustomLoginUrlRequest( bool $is ): bool {
		if ( $is ) {
			return true;
		}
		if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
			return false;
		}

		return $this->matchesCustomLoginRequestUri( wp_unslash( $_SERVER['REQUEST_URI'] ) );
	}

	public function handleCustomLoginUrl(): void {
		static $handled = false;
		if ( $handled || is_admin() ) {
			return;
		}

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

		$request_uri = wp_unslash( $_SERVER['REQUEST_URI'] );

		if ( ! $this->matchesCustomLoginRequestUri( $request_uri ) ) {
			return;
		}

		$handled = true;

		$parsed_url = wp_parse_url( $request_uri );
		if ( ! $parsed_url || ! isset( $parsed_url['path'] ) ) {
			return;
		}

		$query_string = isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : '';
		$fragment     = isset( $parsed_url['fragment'] ) ? '#' . $parsed_url['fragment'] : '';

		$query_params = [];
		if ( isset( $parsed_url['query'] ) ) {
			parse_str( $parsed_url['query'], $query_params );
		}

		if ( is_user_logged_in() && $this->shouldRedirectLoggedInUserFromCustomLogin( $query_params ) ) {
			$target = apply_filters( 'wp_powersuite_change_login_logged_in_redirect', admin_url(), wp_get_current_user(), $query_params );
			$target = wp_validate_redirect( $target, admin_url() );
			wp_safe_redirect( $target );
			exit;
		}

		if ( isset( $query_params['loggedout'] ) && $query_params['loggedout'] === 'true' ) {
			$this->renderLoggedOutInterstitial();
		}

		$home_path  = $this->getHomePathPrefix();
		$login_path = $home_path !== '' ? '/' . $home_path . '/wp-login.php' : '/wp-login.php';

		$_SERVER['REQUEST_URI']     = $login_path . $query_string . $fragment;
		$_SERVER['PHP_SELF']        = '/wp-login.php';
		$_SERVER['SCRIPT_NAME']     = '/wp-login.php';
		$_SERVER['SCRIPT_FILENAME'] = ABSPATH . 'wp-login.php';

		if ( isset( $parsed_url['query'] ) ) {
			$_SERVER['QUERY_STRING'] = $parsed_url['query'];
			parse_str( $parsed_url['query'], $_GET );
			$_REQUEST = array_merge( $_REQUEST, $_GET );
		} else {
			unset( $_SERVER['QUERY_STRING'] );
		}

		$login_file = ABSPATH . 'wp-login.php';
		if ( ! is_readable( $login_file ) ) {
			wp_die( esc_html__( 'Login file not found.', 'wp-powersuite' ), esc_html__( 'Error', 'wp-powersuite' ), [ 'response' => 500 ] );
		}

		global $pagenow;
		$pagenow = 'wp-login.php';

		global $user_login, $error, $interim_login, $action, $redirect_to, $reauth;

		$user_login = $user_login ?? '';
		$error = $error ?? '';
		$interim_login = $interim_login ?? false;
		$action = $action ?? ( isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : 'login' );
		$redirect_to = $redirect_to ?? ( isset( $_GET['redirect_to'] ) ? esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) : '' );
		$reauth = $reauth ?? ( isset( $_GET['reauth'] ) ? (bool) $_GET['reauth'] : false );

		// Login-only modules are not loaded on normal frontend requests; bootstrap them here.
		ModuleManager::getInstance()->loadEnabledModulesForContext( 'login' );

		/**
		 * Fires before Change Login URL includes wp-login.php on the masked route.
		 * Other modules (e.g. Login Page Customizer, CAPTCHA) can prepare the login screen here.
		 */
		do_action( 'wpps_before_custom_login_render' );

		nocache_headers();
		ob_start();
		status_header( 200 );
		include $login_file;
		echo ob_get_clean();
		exit;
	}

	private function getSettings(): array {
		if ( $this->settings !== null ) {
			return $this->settings;
		}

		$this->settings = $this->moduleSettings()->all( $this->getDefaultSettings() );
		return $this->settings;
	}

	public function getDefaultSettings(): array {
		return [
			'login_slug' => 'login',
		];
	}

	public function blockDefaultLoginUrls(): void {
		global $pagenow;

		if ( isset( $_SERVER['REQUEST_URI'] ) && $this->isStaticAssetRequest( wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) {
			return;
		}

		if ( $this->isSystemRequestBypass() ) {
			return;
		}

		if ( is_user_logged_in() && is_admin() && ! wp_doing_ajax() && ( $pagenow ?? '' ) !== 'wp-login.php' ) {
			return;
		}

		if ( ! $this->isPathBlockingCandidate( $pagenow ?? '' ) ) {
			return;
		}

		$custom_slug = $this->getSlug();

		if ( ( $pagenow ?? '' ) === 'wp-login.php' ) {
			$this->serveBlocked404();
			exit;
		}

		if ( ! is_user_logged_in() ) {
			if ( $this->shouldBlockWpAdminRequest() ) {
				$this->serveBlocked404();
				exit;
			}
			if ( $this->shouldBlockCommonLoginAliases( $custom_slug ) ) {
				$this->serveBlocked404();
				exit;
			}
		}
	}

	public function maybeRedirectGravityFormsPreview(): void {
		if ( ! class_exists( 'GFForms', false ) ) {
			return;
		}
		if ( is_user_logged_in() || wp_doing_ajax() ) {
			return;
		}
		if ( ( $_SERVER['REQUEST_METHOD'] ?? '' ) !== 'GET' || ! isset( $_GET['gf_page'] ) ) {
			return;
		}

		$rel = $this->getRelativeRequestPath();
		$custom = $this->getSlug();
		if ( $rel !== '' && ( strcasecmp( $rel, $custom ) === 0 || str_starts_with( strtolower( $rel ), strtolower( $custom ) . '/' ) ) ) {
			return;
		}

		wp_safe_redirect( home_url( '/' ) );
		exit;
	}

	public function filterLostPasswordUrl( string $lostpassword_url, string $redirect ): string {
		if ( $this->shouldUsePublicPluginAccountUrls() ) {
			$public = $this->getPublicLostPasswordUrl( $redirect );
			if ( $public !== '' ) {
				return $public;
			}
		}

		$url = add_query_arg( 'action', 'lostpassword', $this->getFrontLoginBaseUrl() );
		return $this->appendValidatedRedirectArg( $url, $redirect );
	}

	public function filterRegisterUrl( string $registration_url ): string {
		if ( $this->shouldUsePublicPluginAccountUrls() ) {
			$public = $this->getPublicRegisterUrl();
			if ( $public !== '' ) {
				return $public;
			}
		}

		return add_query_arg( 'action', 'register', $this->getFrontLoginBaseUrl() );
	}

	public function filterWooCommerceLostPasswordUrl( string $url, string $redirect = '' ): string {
		$wc_url = $this->getWooCommerceLostPasswordEndpointUrl( $redirect );
		if ( $wc_url !== '' ) {
			return $wc_url;
		}

		if ( strpos( $url, 'wp-login.php' ) === false ) {
			return $url;
		}
		$parsed = wp_parse_url( $url );
		$args = [];
		if ( ! empty( $parsed['query'] ) ) {
			parse_str( $parsed['query'], $args );
		}
		$base = $this->getFrontLoginBaseUrl();
		$new = empty( $args ) ? $base : add_query_arg( $args, $base );
		return $this->appendValidatedRedirectArg( $new, $redirect );
	}

	public function filterRetrievePasswordMessage( string $message, string $key, string $user_login, \WP_User $user_data ): string {
		unset( $key, $user_login, $user_data );
		if ( strpos( $message, 'wp-login.php' ) === false ) {
			return $message;
		}
		return str_replace( 'wp-login.php', $this->getSlug(), $message );
	}

	/**
	 * @return list<string>
	 */
	public static function discouragedSlugs(): array {
		return self::RESERVED_SLUGS;
	}

	/**
	 * Sanitize settings on save (server-side slug validation).
	 *
	 * @param array<string, mixed> $settings Raw settings from the admin UI.
	 * @param array<string, mixed> $existing Previously stored settings.
	 * @return array{0: array<string, mixed>, 1: string|null} Tuple of sanitized settings and optional error message.
	 */
	public static function sanitizeSettings( array $settings, array $existing = [] ): array {
		$raw = isset( $settings['login_slug'] ) ? trim( (string) $settings['login_slug'] ) : '';
		if ( $raw === '' ) {
			$existing_slug = sanitize_key( (string) ( $existing['login_slug'] ?? '' ) );
			$raw           = $existing_slug !== '' ? $existing_slug : 'login';
		}

		if ( ! preg_match( '/^[a-z0-9-]+$/', $raw ) ) {
			return [
				[],
				__( 'Login slug may only contain lowercase letters, numbers, and hyphens.', 'wp-powersuite' ),
			];
		}

		$slug = sanitize_key( $raw );
		if ( $slug === '' || $slug !== $raw ) {
			return [
				[],
				__( 'Invalid login slug.', 'wp-powersuite' ),
			];
		}

		if ( in_array( $slug, self::RESERVED_SLUGS, true ) ) {
			$existing_slug = sanitize_key( (string) ( $existing['login_slug'] ?? '' ) );
			if ( $slug !== $existing_slug ) {
				return [
					[],
					__( 'That slug is reserved or conflicts with WordPress paths. Choose a unique phrase (e.g. your-site-crew).', 'wp-powersuite' ),
				];
			}
		}

		return [
			[ 'login_slug' => $slug ],
			null,
		];
	}

	/**
	 * Normalize a stored or submitted slug for display and runtime use.
	 */
	public static function normalizeLoginSlug( string $slug ): string {
		$slug = sanitize_key( $slug );

		return $slug !== '' ? $slug : 'login';
	}

	private function getSlug(): string {
		if ( $this->custom_slug_cache !== null ) {
			return $this->custom_slug_cache;
		}
		$this->custom_slug_cache = self::normalizeLoginSlug( (string) ( $this->getSettings()['login_slug'] ?? 'login' ) );
		return $this->custom_slug_cache;
	}

	private function getFrontLoginBaseUrl(): string {
		return home_url( '/' . $this->getSlug() );
	}

	private function getHomePathPrefix(): string {
		if ( $this->home_path_prefix_cache !== null ) {
			return $this->home_path_prefix_cache;
		}
		$parsed = wp_parse_url( home_url() );
		$this->home_path_prefix_cache = isset( $parsed['path'] ) ? trim( $parsed['path'], '/' ) : '';
		return $this->home_path_prefix_cache;
	}

	private function appendValidatedRedirectArg( string $url, string $redirect ): string {
		if ( $redirect === '' ) {
			return $url;
		}
		$validated = wp_validate_redirect( $redirect, false );
		if ( ! is_string( $validated ) || $validated === '' ) {
			return $url;
		}
		return add_query_arg( 'redirect_to', $validated, $url );
	}

	private function shouldUsePublicPluginAccountUrls(): bool {
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
			return false;
		}
		if ( wp_doing_cron() ) {
			return false;
		}
		if ( is_admin() && ! wp_doing_ajax() ) {
			return false;
		}
		return (bool) apply_filters( 'wp_powersuite_change_login_use_public_account_urls', true );
	}

	private function getPublicLostPasswordUrl( string $redirect ): string {
		$url = apply_filters( 'wp_powersuite_change_login_public_lostpassword_url', '', $redirect );
		if ( is_string( $url ) && $url !== '' ) {
			return $this->appendValidatedRedirectArg( $url, $redirect );
		}

		if ( function_exists( 'um_get_core_page' ) ) {
			$um = um_get_core_page( 'password-reset' );
			if ( is_string( $um ) && $um !== '' ) {
				return $this->appendValidatedRedirectArg( $um, $redirect );
			}
		}

		$mp = $this->getMemberPressForgotPasswordUrl();
		if ( $mp !== '' ) {
			return $this->appendValidatedRedirectArg( $mp, $redirect );
		}

		$pmpro = $this->getPaidMembershipsProLostPasswordUrl();
		if ( $pmpro !== '' ) {
			return $this->appendValidatedRedirectArg( $pmpro, $redirect );
		}

		if ( $this->isWooCommerceStorefrontLostPasswordContext() ) {
			$wc = $this->getWooCommerceLostPasswordEndpointUrl( $redirect );
			if ( $wc !== '' ) {
				return $wc;
			}
		}

		return '';
	}

	private function getPublicRegisterUrl(): string {
		$url = apply_filters( 'wp_powersuite_change_login_public_register_url', '' );
		if ( is_string( $url ) && $url !== '' ) {
			return $url;
		}

		if ( function_exists( 'um_get_core_page' ) ) {
			$um = um_get_core_page( 'register' );
			if ( is_string( $um ) && $um !== '' ) {
				return $um;
			}
		}

		if ( function_exists( 'pmpro_url' ) ) {
			$pm_login = pmpro_url( 'login' );
			if ( is_string( $pm_login ) && $pm_login !== '' ) {
				return add_query_arg( 'action', 'register', $pm_login );
			}
		}

		$wc = $this->getWooCommerceRegisterEndpointUrl();
		if ( $wc !== '' ) {
			return $wc;
		}

		if ( function_exists( 'bp_get_signup_page' ) ) {
			$bp = bp_get_signup_page();
			if ( is_numeric( $bp ) ) {
				$bp = get_permalink( (int) $bp );
			}
			if ( is_string( $bp ) && $bp !== '' ) {
				return $bp;
			}
		}

		return '';
	}

	private function getWooCommerceRegisterEndpointUrl(): string {
		if ( ! class_exists( 'WooCommerce', false ) || ! get_option( 'users_can_register' ) ) {
			return '';
		}
		if ( ! function_exists( 'wc_get_page_permalink' ) || ! function_exists( 'wc_get_page_id' ) ) {
			return '';
		}
		if ( wc_get_page_id( 'myaccount' ) <= 0 ) {
			return '';
		}
		return add_query_arg( 'action', 'register', wc_get_page_permalink( 'myaccount' ) );
	}

	private function getMemberPressForgotPasswordUrl(): string {
		if ( ! class_exists( 'MeprOptions', false ) || ! is_callable( [ 'MeprOptions', 'fetch' ] ) ) {
			return '';
		}
		$opts = \MeprOptions::fetch();
		if ( ! $opts || (int) $opts->login_page_id <= 0 ) {
			return '';
		}
		$login_url = $opts->login_page_url();
		if ( ! is_string( $login_url ) || $login_url === '' ) {
			return '';
		}
		$delim = str_contains( $login_url, '?' ) ? '&' : '?';
		return $login_url . $delim . 'action=forgot_password';
	}

	private function getPaidMembershipsProLostPasswordUrl(): string {
		if ( ! function_exists( 'pmpro_url' ) ) {
			return '';
		}
		$login = pmpro_url( 'login' );
		if ( ! is_string( $login ) || $login === '' ) {
			return '';
		}
		return add_query_arg( 'action', 'reset_pass', $login );
	}

	private function getWooCommerceLostPasswordEndpointUrl( string $redirect = '' ): string {
		if ( ! function_exists( 'wc_get_page_id' ) || ! function_exists( 'wc_get_page_permalink' ) ) {
			return '';
		}
		if ( wc_get_page_id( 'myaccount' ) <= 0 ) {
			return '';
		}

		if ( function_exists( 'wc_get_account_endpoint_url' ) ) {
			$url = wc_get_account_endpoint_url( 'lost-password' );
		} elseif ( function_exists( 'wc_get_endpoint_url' ) ) {
			$url = wc_get_endpoint_url( 'lost-password', '', wc_get_page_permalink( 'myaccount' ) );
		} else {
			return '';
		}

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

		return $this->appendValidatedRedirectArg( $url, $redirect );
	}

	private function isWooCommerceStorefrontLostPasswordContext(): bool {
		if ( ! class_exists( 'WooCommerce', false ) || ! function_exists( 'wc_get_page_id' ) ) {
			return false;
		}
		if ( wc_get_page_id( 'myaccount' ) <= 0 ) {
			return false;
		}
		if ( is_admin() && ! wp_doing_ajax() ) {
			return false;
		}

		if ( function_exists( 'is_account_page' ) && is_account_page() ) {
			return true;
		}
		if ( function_exists( 'is_cart' ) && is_cart() ) {
			return true;
		}
		if ( function_exists( 'is_checkout' ) && is_checkout() ) {
			return true;
		}
		if ( function_exists( 'is_woocommerce' ) && is_woocommerce() ) {
			return true;
		}

		return (bool) apply_filters( 'wp_powersuite_change_login_use_wc_lost_password', false );
	}

	private function getRelativeRequestPath(): string {
		if ( $this->relative_request_path_cache !== null ) {
			return $this->relative_request_path_cache;
		}
		if ( ! isset( $_SERVER['REQUEST_URI'] ) ) {
			$this->relative_request_path_cache = '';
			return '';
		}
		$path = wp_parse_url( wp_unslash( $_SERVER['REQUEST_URI'] ), PHP_URL_PATH );
		if ( ! is_string( $path ) ) {
			$this->relative_request_path_cache = '';
			return '';
		}
		$full_path = trim( $path, '/' );
		$home_path = $this->getHomePathPrefix();
		if ( $home_path !== '' && strpos( $full_path, $home_path ) === 0 ) {
			$this->relative_request_path_cache = trim( substr( $full_path, strlen( $home_path ) ), '/' );
			return $this->relative_request_path_cache;
		}
		$this->relative_request_path_cache = $full_path;
		return $this->relative_request_path_cache;
	}

	private function shouldRedirectLoggedInUserFromCustomLogin( array $query_params ): bool {
		if (
			isset( $query_params['wpps_lpc_preview'] )
			&& (string) $query_params['wpps_lpc_preview'] === '1'
			&& current_user_can( 'manage_options' )
		) {
			return false;
		}

		$action = isset( $query_params['action'] ) ? (string) $query_params['action'] : '';
		if ( in_array( $action, self::LOGGED_IN_CUSTOM_LOGIN_ACTIONS, true ) ) {
			return false;
		}
		if ( isset( $query_params['interim-login'] ) && (string) $query_params['interim-login'] === '1' ) {
			return false;
		}
		if ( isset( $query_params['checkemail'] ) && in_array( (string) $query_params['checkemail'], [ 'confirm', 'registered' ], true ) ) {
			return false;
		}
		return true;
	}

	private function shouldBlockWpAdminRequest(): bool {
		if ( $this->isExcludedWpAdminRequest() ) {
			return false;
		}

		$rel = strtolower( $this->getRelativeRequestPath() );

		return $rel === 'wp-admin' || str_starts_with( $rel, 'wp-admin/' );
	}

	private function shouldBlockCommonLoginAliases( string $custom_slug ): bool {
		$rel = $this->getRelativeRequestPath();
		if ( $rel === '' ) {
			return false;
		}
		$custom_lower = strtolower( $custom_slug );
		$segments = explode( '/', $rel );
		$first = strtolower( $segments[0] ?? '' );
		if ( $first === $custom_lower ) {
			return false;
		}
		if ( in_array( $first, [ 'wp-login', 'login.php' ], true ) ) {
			return true;
		}
		$second = strtolower( (string) ( $segments[1] ?? '' ) );
		if ( $first === 'admin' ) {
			return ( $segments[1] ?? '' ) === '' || str_contains( $second, '.php' );
		}
		if ( $first === 'login' ) {
			return ( $segments[1] ?? '' ) === '' || str_contains( $second, '.php' );
		}
		return false;
	}

	private function isSystemRequestBypass(): bool {
		if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
			return true;
		}
		if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
			return true;
		}
		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return true;
		}
		if ( defined( 'WP_CLI' ) && WP_CLI ) {
			return true;
		}
		return (bool) apply_filters( 'wp_powersuite_change_login_url_skip_path_block', false );
	}

	private function serveBlocked404(): void {
		wp_die(
			esc_html__( 'Page not found.', 'wp-powersuite' ),
			esc_html__( '404 - Page Not Found', 'wp-powersuite' ),
			[ 'response' => 404 ]
		);
	}

	private function renderLoggedOutInterstitial(): void {
		$login_url = esc_url( $this->getFrontLoginBaseUrl() );
		wp_die(
			'<p>' . esc_html__( 'You have successfully logged out.', 'wp-powersuite' ) . '</p>' .
			'<p><a href="' . $login_url . '">' . esc_html__( 'Sign in again', 'wp-powersuite' ) . '</a> | ' .
			'<a href="' . esc_url( home_url( '/' ) ) . '">' . esc_html__( 'Go back to site', 'wp-powersuite' ) . '</a></p>',
			esc_html__( 'Logged Out', 'wp-powersuite' ),
			[ 'response' => 200 ]
		);
	}

	private function isStaticAssetRequest( string $request_uri ): bool {
		$path = wp_parse_url( $request_uri, PHP_URL_PATH );
		if ( ! is_string( $path ) ) {
			return false;
		}
		$path = strtolower( $path );
		static $exts = [ '.css', '.js', '.woff', '.woff2', '.ttf', '.otf', '.eot', '.ico', '.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.map' ];
		foreach ( $exts as $ext ) {
			if ( substr( $path, -strlen( $ext ) ) === $ext ) {
				return true;
			}
		}
		return strpos( $path, '/wp-content/plugins/' ) !== false
			|| strpos( $path, '/wp-content/themes/' ) !== false
			|| strpos( $path, '/wp-includes/' ) !== false;
	}

	private function isExcludedWpAdminRequest(): bool {
		$rel = strtolower( $this->getRelativeRequestPath() );

		return str_contains( $rel, 'admin-ajax.php' )
			|| str_contains( $rel, 'async-upload.php' )
			|| str_contains( $rel, 'admin-post.php' );
	}

	/**
	 * Skip path blocking when the request clearly is not login or wp-admin related.
	 */
	private function isPathBlockingCandidate( string $pagenow ): bool {
		if ( $pagenow === 'wp-login.php' ) {
			return true;
		}

		$rel = strtolower( $this->getRelativeRequestPath() );
		if ( $rel === '' ) {
			return false;
		}

		$first = explode( '/', $rel )[0] ?? '';
		foreach ( [ 'wp-admin', 'wp-login', 'admin', 'login' ] as $prefix ) {
			if ( $first === $prefix || str_starts_with( $rel, $prefix . '/' ) ) {
				return true;
			}
		}

		$custom = strtolower( $this->getSlug() );

		return $first === $custom || str_starts_with( $rel, $custom . '/' );
	}

	public function filterLoginUrl( string $url, string $path, ?string $scheme ): string {
		if ( $path !== 'wp-login.php' && ! str_contains( $path, 'wp-login.php' ) ) {
			return $url;
		}

		$slug = $this->getSlug();
		if ( ! str_contains( $path, '?' ) ) {
			$new_path = str_replace( 'wp-login.php', $slug, $path );
			return home_url( '/' . $new_path, $scheme );
		}
		list( $path_base, $path_query ) = explode( '?', $path, 2 );
		$new_path = str_replace( 'wp-login.php', $slug, $path_base );
		return home_url( '/' . $new_path . '?' . $path_query, $scheme );
	}

	public function filterRedirectUrl( string $location, int $status ): string {
		unset( $status );
		if ( ! str_contains( $location, 'wp-login.php' ) ) {
			return $location;
		}

		$parsed_check = wp_parse_url( $location );
		$home_parsed = wp_parse_url( home_url() );
		if ( is_array( $parsed_check ) && ! empty( $parsed_check['host'] )
			&& is_array( $home_parsed ) && ! empty( $home_parsed['host'] )
			&& strtolower( (string) $parsed_check['host'] ) !== strtolower( (string) $home_parsed['host'] ) ) {
			return $location;
		}

		$custom_slug = $this->getSlug();
		$parsed_location = wp_parse_url( $location );
		if ( ! $parsed_location ) {
			return str_replace( 'wp-login.php', $custom_slug, $location );
		}

		$base_parsed = wp_parse_url( home_url() );
		$path = isset( $parsed_location['path'] ) ? str_replace( '/wp-login.php', '/' . $custom_slug, (string) $parsed_location['path'] ) : '';
		$scheme = isset( $parsed_location['scheme'] ) ? $parsed_location['scheme'] : ( $base_parsed['scheme'] ?? 'http' );
		$host = $parsed_location['host'] ?? ( $base_parsed['host'] ?? '' );
		$port = isset( $parsed_location['port'] ) ? ':' . $parsed_location['port'] : '';
		$query = isset( $parsed_location['query'] ) ? '?' . $parsed_location['query'] : '';
		$fragment = isset( $parsed_location['fragment'] ) ? '#' . $parsed_location['fragment'] : '';

		return $scheme . '://' . $host . $port . $path . $query . $fragment;
	}

	public function filterLogoutUrl( string $logout_url, string $redirect ): string {
		unset( $redirect );
		if ( ! str_contains( $logout_url, 'wp-login.php' ) ) {
			return $logout_url;
		}

		$custom_slug = $this->getSlug();
		$parsed_url = wp_parse_url( $logout_url );
		if ( ! $parsed_url ) {
			return str_replace( 'wp-login.php', $custom_slug, $logout_url );
		}

		$scheme = isset( $parsed_url['scheme'] ) ? $parsed_url['scheme'] . '://' : '';
		$user = isset( $parsed_url['user'] ) ? $parsed_url['user'] : '';
		$pass = isset( $parsed_url['pass'] ) ? ':' . $parsed_url['pass'] : '';
		$userinfo = ( $user !== '' || $pass !== '' ) ? $user . $pass . '@' : '';
		$host = isset( $parsed_url['host'] ) ? $parsed_url['host'] : '';
		$port = isset( $parsed_url['port'] ) ? ':' . $parsed_url['port'] : '';
		$path = isset( $parsed_url['path'] ) ? str_replace( 'wp-login.php', $custom_slug, (string) $parsed_url['path'] ) : '';
		$query = isset( $parsed_url['query'] ) ? '?' . $parsed_url['query'] : '';
		$fragment = isset( $parsed_url['fragment'] ) ? '#' . $parsed_url['fragment'] : '';

		return $scheme . $userinfo . $host . $port . $path . $query . $fragment;
	}

	public function filterLoginUrlDirect( string $login_url, string $redirect, bool $force_reauth ): string {
		if ( ! str_contains( $login_url, 'wp-login.php' ) ) {
			return $login_url;
		}

		$url = $this->getFrontLoginBaseUrl();
		$url = $this->appendValidatedRedirectArg( $url, $redirect );
		if ( $force_reauth ) {
			$url = add_query_arg( 'reauth', '1', $url );
		}
		return $url;
	}

	public function onModuleToggled( string $module_id, bool $enable, array $module_metadata ): void {
		unset( $module_metadata );
		if ( $module_id !== self::MODULE_ID ) {
			return;
		}
		self::refreshRoutingState();
	}

	/**
	 * @param array<string, mixed> $settings
	 */
	public function onSettingsSaved( string $module_id, array $settings ): void {
		unset( $settings );
		if ( $module_id !== self::MODULE_ID ) {
			return;
		}
		self::refreshRoutingState();
	}

	/**
	 * Bust cached slug/settings and rewrite rules after slug or module state changes.
	 */
	public static function refreshRoutingState(): void {
		OptionStoreModuleSettingsRepository::clearCache();
		flush_rewrite_rules( false );
	}

	/**
	 * Prevent catch-all 404 redirect modules from hijacking the custom login path.
	 *
	 * @param bool   $skip Skip this path.
	 * @param string $path Request path (may include leading slash).
	 */
	public function filterSkip404Path( bool $skip, string $path ): bool {
		if ( $skip ) {
			return true;
		}

		return $this->pathMatchesCustomLoginSlug( $path );
	}

	private function pathMatchesCustomLoginSlug( string $path ): bool {
		$full_path = trim( $path, '/' );
		if ( $full_path === '' ) {
			return false;
		}

		$home_path = $this->getHomePathPrefix();
		$relative  = ( $home_path !== '' && str_starts_with( $full_path, $home_path . '/' ) )
			? trim( substr( $full_path, strlen( $home_path ) ), '/' )
			: ( ( $home_path !== '' && $full_path === $home_path ) ? '' : $full_path );

		$slug = $this->getSlug();

		return $relative === $slug || str_starts_with( $relative, $slug . '/' );
	}


}
