<?php
/**
 * Hide Login Page Module
 *
 * Change the login URL and block access to wp-login.php.
 *
 * @package BlaminhorEssentials
 */

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Hide Login Page module class
 */
class Blaminhor_Essentials_Module_Hide_Login extends Blaminhor_Essentials_Module {

	/**
	 * Module ID
	 *
	 * @var string
	 */
	protected $id = 'hide-login';

	/**
	 * Module name
	 *
	 * @var string
	 */
	protected $name = 'Hide Login Page';

	/**
	 * Reserved slugs that cannot be used as login slug
	 *
	 * @var array
	 */
	private $reserved_slugs = array(
		'wp-admin',
		'wp-content',
		'wp-includes',
		'wp-json',
		'wp-login',
		'wp-login.php',
		'wp-register.php',
		'wp-signup.php',
		'feed',
		'rss',
		'rss2',
		'atom',
		'rdf',
		'xmlrpc.php',
		'readme.html',
		'favicon.ico',
		'sitemap',
		'robots.txt',
	);

	/**
	 * Initialize module
	 */
	protected function init() {
		$this->name        = __( 'Hide Login Page', 'blaminhor-essentials' );
		$this->description = __( 'Change the login URL and block access to wp-login.php.', 'blaminhor-essentials' );
	}

	/**
	 * Get default settings
	 *
	 * @return array
	 */
	protected function get_default_settings() {
		return array(
			'enabled'       => false,
			'login_slug'    => 'login',
			'redirect_slug' => '404',
		);
	}

	/**
	 * Register hooks
	 */
	protected function register_hooks() {
		// Always handle settings.
		add_action( 'admin_init', array( $this, 'handle_settings_submission' ) );

		// Only apply protection if enabled.
		if ( ! $this->get_setting( 'enabled', false ) ) {
			return;
		}

		$login_slug = $this->get_setting( 'login_slug', 'login' );
		if ( empty( $login_slug ) ) {
			return;
		}

		// Intercept requests early.
		add_action( 'init', array( $this, 'handle_login_redirect' ), 1 );

		// Tell the page cache to skip our custom login slug — defense-in-depth
		// in case any cache layer runs before our `init` interceptor.
		add_filter( 'blaminhor_essentials_cache_skip', array( $this, 'cache_skip_login_slug' ), 10, 2 );

		// Filter URLs.
		add_filter( 'site_url', array( $this, 'filter_site_url' ), 10, 3 );
		add_filter( 'login_url', array( $this, 'filter_login_url' ), 10, 3 );
		add_filter( 'logout_url', array( $this, 'filter_logout_url' ), 10, 2 );
		add_filter( 'lostpassword_url', array( $this, 'filter_lostpassword_url' ), 10, 2 );
		add_filter( 'wp_redirect', array( $this, 'filter_wp_redirect' ), 10, 1 );
	}

	/**
	 * Handle login page redirects
	 */
	public function handle_login_redirect() {
		// Skip AJAX, cron, CLI, REST API, XML-RPC.
		if ( wp_doing_ajax() || wp_doing_cron() ) {
			return;
		}

		if ( defined( 'WP_CLI' ) && WP_CLI ) {
			return;
		}

		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return;
		}

		if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
			return;
		}

		$request_uri  = isset( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
		$request_path = wp_parse_url( $request_uri, PHP_URL_PATH );
		$request_path = rtrim( $request_path, '/' );

		$login_slug    = $this->get_setting( 'login_slug', 'login' );
		$redirect_slug = $this->get_setting( 'redirect_slug', '404' );
		$site_path     = wp_parse_url( home_url(), PHP_URL_PATH );
		$site_path     = $site_path ? rtrim( $site_path, '/' ) : '';

		// Check if accessing wp-login.php.
		if ( $site_path . '/wp-login.php' === $request_path || '/wp-login.php' === $request_path ) {
			// Allow logged-in users to access wp-login.php (for logout, profile, etc.).
			if ( is_user_logged_in() ) {
				return;
			}

			// Redirect non-logged-in users.
			$redirect_url = home_url( $redirect_slug );
			wp_safe_redirect( $redirect_url, 302 );
			exit;
		}

		// Check if accessing /wp-admin/ without being logged in.
		$wp_admin_path = $site_path . '/wp-admin';
		if ( strpos( $request_path, $wp_admin_path ) === 0 || strpos( $request_path, '/wp-admin' ) === 0 ) {
			// Allow admin-ajax.php and admin-post.php.
			if ( strpos( $request_path, 'admin-ajax.php' ) !== false || strpos( $request_path, 'admin-post.php' ) !== false ) {
				return;
			}

			if ( ! is_user_logged_in() ) {
				$redirect_url = home_url( $redirect_slug );
				wp_safe_redirect( $redirect_url, 302 );
				exit;
			}
		}

		// Check if accessing the custom login slug.
		$custom_login_path = $site_path . '/' . $login_slug;
		if ( $request_path === $custom_login_path ) {
			// Serve the login page.
			// phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited -- Required to make wp-login.php work from custom URL.
			$GLOBALS['pagenow'] = 'wp-login.php';

			// Pre-initialise variables that wp-login.php's login_form() template
			// reads but only sets in some action branches. Loading wp-login.php
			// via require_once (rather than as the entry script) skips the
			// outermost init block, so we have to seed them ourselves to avoid
			// "Undefined variable" warnings on lines 1515 / 1599 / 1621.
			// phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
			$user_login = '';
			$error      = '';
			// phpcs:enable WordPress.WP.GlobalVariablesOverride.Prohibited

			require_once ABSPATH . 'wp-login.php';
			exit;
		}
	}

	/**
	 * Filter site_url to replace wp-login.php with custom slug
	 *
	 * @param string      $url    The complete site URL.
	 * @param string      $path   Path relative to the site URL.
	 * @param string|null $scheme The scheme to use.
	 * @return string
	 */
	/**
	 * Tell the page cache to bypass the custom login slug.
	 *
	 * @param bool  $skip   Existing skip decision.
	 * @param array $server $_SERVER copy.
	 * @return bool
	 */
	public function cache_skip_login_slug( $skip, $server ) {
		if ( $skip ) {
			return $skip;
		}
		$login_slug = $this->get_setting( 'login_slug', 'login' );
		if ( empty( $login_slug ) ) {
			return $skip;
		}
		$uri = isset( $server['REQUEST_URI'] ) ? $server['REQUEST_URI'] : '';
		$path = wp_parse_url( $uri, PHP_URL_PATH );
		$path = $path ? rtrim( $path, '/' ) : '';
		$site_path = wp_parse_url( home_url(), PHP_URL_PATH );
		$site_path = $site_path ? rtrim( $site_path, '/' ) : '';
		return ( $site_path . '/' . $login_slug === $path );
	}

	public function filter_site_url( $url, $path, $scheme ) {
		if ( 'login' !== $scheme && 'login_post' !== $scheme ) {
			return $url;
		}

		return $this->replace_login_url( $url );
	}

	/**
	 * Filter login_url
	 *
	 * @param string $url      The login URL.
	 * @param string $redirect The path to redirect to on login.
	 * @param bool   $force_reauth Whether to force reauthorization.
	 * @return string
	 */
	public function filter_login_url( $url, $redirect = '', $force_reauth = false ) {
		return $this->replace_login_url( $url );
	}

	/**
	 * Filter logout_url
	 *
	 * @param string $url      The logout URL.
	 * @param string $redirect The path to redirect to on logout.
	 * @return string
	 */
	public function filter_logout_url( $url, $redirect = '' ) {
		return $this->replace_login_url( $url );
	}

	/**
	 * Filter lostpassword_url
	 *
	 * @param string $url      The lost password URL.
	 * @param string $redirect The path to redirect to on login.
	 * @return string
	 */
	public function filter_lostpassword_url( $url, $redirect = '' ) {
		return $this->replace_login_url( $url );
	}

	/**
	 * Filter wp_redirect to catch redirections to wp-login.php
	 *
	 * @param string $location Redirect location.
	 * @return string
	 */
	public function filter_wp_redirect( $location ) {
		if ( strpos( $location, 'wp-login.php' ) !== false ) {
			return $this->replace_login_url( $location );
		}

		return $location;
	}

	/**
	 * Replace wp-login.php in a URL with the custom login slug
	 *
	 * @param string $url The URL to modify.
	 * @return string
	 */
	private function replace_login_url( $url ) {
		$login_slug = $this->get_setting( 'login_slug', 'login' );

		// Parse the URL to extract query string.
		$parsed = wp_parse_url( $url );
		$query  = isset( $parsed['query'] ) ? '?' . $parsed['query'] : '';

		// Build new URL with custom slug.
		return home_url( $login_slug ) . '/' . $query;
	}

	/**
	 * Check if Fatal Error Recovery module is active
	 *
	 * @return bool
	 */
	private function is_recovery_module_active() {
		$active_modules = get_option( 'blaminhor_essentials_active_modules', array() );
		return in_array( 'fatal-error-recovery', $active_modules, true );
	}

	/**
	 * Render settings page
	 */
	public function render_settings_page() {
		$enabled        = $this->get_setting( 'enabled', false );
		$login_slug     = $this->get_setting( 'login_slug', 'login' );
		$redirect_slug  = $this->get_setting( 'redirect_slug', '404' );
		$recovery_active = $this->is_recovery_module_active();
		?>
		<div class="wrap blaminhor-essentials-wrap">
			<div class="blaminhor-essentials-header">
				<h1><?php esc_html_e( 'Hide Login Page', 'blaminhor-essentials' ); ?></h1>
				<p class="blaminhor-essentials-tagline"><?php esc_html_e( 'Change the login URL and block access to wp-login.php.', 'blaminhor-essentials' ); ?></p>
			</div>

			<div class="blaminhor-essentials-card">
				<div class="blaminhor-essentials-notice warning">
					<strong><span class="dashicons dashicons-warning ap-icon-mr"></span><?php esc_html_e( 'Warning', 'blaminhor-essentials' ); ?></strong>
					<p>
						<?php esc_html_e( 'Enabling this feature changes your login URL. If you forget the custom URL, you will not be able to access the login page. Bookmark the new URL before enabling.', 'blaminhor-essentials' ); ?>
					</p>
					<?php if ( ! $recovery_active ) : ?>
						<p>
							<strong><?php esc_html_e( 'Recommendation:', 'blaminhor-essentials' ); ?></strong>
							<?php
							printf(
								/* translators: %s: Link to modules page */
								esc_html__( 'Enable the %s module before activating Hide Login. It provides a recovery URL that works even if you forget the custom login URL.', 'blaminhor-essentials' ),
								'<a href="' . esc_url( admin_url( 'admin.php?page=blaminhor-essentials' ) ) . '"><strong>' . esc_html__( 'Fatal Error Recovery', 'blaminhor-essentials' ) . '</strong></a>'
							);
							?>
						</p>
					<?php else : ?>
						<p style="color: #1e4620;">
							<span class="dashicons dashicons-yes-alt ap-text-success"></span>
							<?php
							printf(
								/* translators: %s: Link to recovery module */
								esc_html__( 'The %s module is active. Save your recovery URL before enabling Hide Login.', 'blaminhor-essentials' ),
								'<a href="' . esc_url( admin_url( 'admin.php?page=blaminhor-essentials-fatal-error-recovery' ) ) . '">' . esc_html__( 'Fatal Error Recovery', 'blaminhor-essentials' ) . '</a>'
							);
							?>
						</p>
					<?php endif; ?>
				</div>

				<?php if ( $enabled ) : ?>
					<div class="ap-status-banner ap-status-success">
						<span class="dashicons dashicons-yes-alt"></span>
						<span>
							<?php
							printf(
								/* translators: %s: Custom login URL */
								esc_html__( 'Hide Login is active. Your login page is available at: %s', 'blaminhor-essentials' ),
								'<strong>' . esc_url( home_url( $login_slug ) ) . '/</strong>'
							);
							?>
						</span>
					</div>
				<?php else : ?>
					<div class="ap-status-banner ap-status-warning">
						<span class="dashicons dashicons-info"></span>
						<span><?php esc_html_e( 'Hide Login is not active.', 'blaminhor-essentials' ); ?></span>
					</div>
				<?php endif; ?>

				<form method="post">
					<?php $this->render_nonce_field(); ?>

					<h3><?php esc_html_e( 'Login Protection', 'blaminhor-essentials' ); ?></h3>

					<table class="form-table">
						<tr>
							<th scope="row"><?php esc_html_e( 'Enable Hide Login', 'blaminhor-essentials' ); ?></th>
							<td>
								<label>
									<input type="checkbox" name="enabled" value="1" <?php checked( $enabled ); ?>>
									<?php esc_html_e( 'Protects your site by changing the login URL and blocking access to wp-login.php and wp-admin for non-logged-in users.', 'blaminhor-essentials' ); ?>
								</label>
							</td>
						</tr>
						<tr>
							<th scope="row">
								<label for="login_slug"><?php esc_html_e( 'Login URL', 'blaminhor-essentials' ); ?></label>
							</th>
							<td>
								<code><?php echo esc_html( home_url() ); ?>/</code>
								<input type="text" name="login_slug" id="login_slug" value="<?php echo esc_attr( $login_slug ); ?>" class="regular-text ap-input-200">
								<code>/</code>
								<p class="description">
									<?php
									printf(
										/* translators: %s: Example URL */
										esc_html__( 'Your new login page will be: %s', 'blaminhor-essentials' ),
										'<strong>' . esc_html( home_url( $login_slug ) ) . '/</strong>'
									);
									?>
								</p>
							</td>
						</tr>
						<tr>
							<th scope="row">
								<label for="redirect_slug"><?php esc_html_e( 'Redirect URL', 'blaminhor-essentials' ); ?></label>
							</th>
							<td>
								<code><?php echo esc_html( home_url() ); ?>/</code>
								<input type="text" name="redirect_slug" id="redirect_slug" value="<?php echo esc_attr( $redirect_slug ); ?>" class="regular-text ap-input-200">
								<code>/</code>
								<p class="description"><?php esc_html_e( 'Redirects to this URL when someone tries to access wp-login.php or wp-admin without being logged in.', 'blaminhor-essentials' ); ?></p>
							</td>
						</tr>
					</table>

					<p class="submit">
						<button type="submit" class="button button-primary"><?php esc_html_e( 'Save Settings', 'blaminhor-essentials' ); ?></button>
					</p>
				</form>
			</div>
		</div>
		<?php
	}

	/**
	 * Process settings
	 *
	 * @param array $data Form data.
	 */
	protected function process_settings( $data ) {
		$login_slug    = isset( $data['login_slug'] ) ? sanitize_title( wp_unslash( $data['login_slug'] ) ) : 'login';
		$redirect_slug = isset( $data['redirect_slug'] ) ? sanitize_title( wp_unslash( $data['redirect_slug'] ) ) : '404';

		// Validate login slug is not empty.
		if ( empty( $login_slug ) ) {
			add_action( 'admin_notices', function () {
				blaminhor_essentials_admin_notice( __( 'The login slug cannot be empty.', 'blaminhor-essentials' ), 'error' );
			} );
			return;
		}

		// Validate login slug is not reserved.
		if ( in_array( $login_slug, $this->reserved_slugs, true ) ) {
			add_action( 'admin_notices', function () {
				blaminhor_essentials_admin_notice( __( 'This slug is reserved and cannot be used.', 'blaminhor-essentials' ), 'error' );
			} );
			return;
		}

		$settings = array(
			'enabled'       => isset( $data['enabled'] ),
			'login_slug'    => $login_slug,
			'redirect_slug' => $redirect_slug,
		);

		$this->save_settings( $settings );

		add_action( 'admin_notices', function () {
			blaminhor_essentials_admin_notice( __( 'Settings saved.', 'blaminhor-essentials' ), 'success' );
		} );
	}
}
