<?php
/**
 * Custom login URL handling with wp-login.php blocking.
 *
 * @package ZA_Creative_Login_Shield
 */

defined( 'ABSPATH' ) || exit;

/**
 * Handles custom login URL slug, blocks wp-login.php access, and manages rewrite rules.
 */
class ZACLS_Login_URL {

	/**
	 * Custom login slug.
	 *
	 * @var string
	 */
	private $slug;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->slug = get_option( 'zacls_custom_login_slug', 'be-login' );
		$this->register_hooks();
	}

	/**
	 * Register hooks.
	 */
	private function register_hooks() {
		add_action( 'init', array( $this, 'rewrite_rules' ) );
		add_action( 'init', array( $this, 'maybe_block_default_login' ), 1 );
		add_filter( 'query_vars', array( $this, 'add_query_vars' ) );
		add_action( 'template_redirect', array( $this, 'handle_login_page' ) );
		add_filter( 'login_url', array( $this, 'custom_login_url' ), 10, 3 );
		add_filter( 'site_url', array( $this, 'filter_login_url' ), 10, 3 );
		add_action( 'wp_logout', array( $this, 'on_logout_redirect' ) );
		add_filter( 'logout_url', array( $this, 'custom_logout_url' ), 10, 2 );
	}

	/**
	 * Add rewrite rule for custom login URL.
	 */
	public function rewrite_rules() {
		add_rewrite_rule(
			$this->slug . '/?$',
			'index.php?zacls_login_page=1',
			'top'
		);

		if ( 'yes' === get_option( 'zacls_flush_rewrite', 'no' ) ) {
			flush_rewrite_rules();
			update_option( 'zacls_flush_rewrite', 'no' );
		}
	}

	/**
	 * Add custom query var.
	 *
	 * @param array $vars Existing query vars.
	 * @return array
	 */
	public function add_query_vars( $vars ) {
		$vars[] = 'zacls_login_page';
		return $vars;
	}

	/**
	 * Handle custom login page template.
	 */
	public function handle_login_page() {
		if ( get_query_var( 'zacls_login_page' ) ) {
			// Ensure database schema is current before any login attempts are recorded.
			if ( class_exists( 'ZACLS_Install', true ) ) {
				ZACLS_Install::maybe_upgrade();
			}
			$this->render_login_page();
			exit;
		}
	}

	/**
	 * Filter the login URL to use custom slug.
	 *
	 * @param string $login_url   The login URL.
	 * @param string $redirect    Redirect URL.
	 * @param bool   $force_reauth Whether to force reauthentication.
	 * @return string
	 */
	public function custom_login_url( $login_url, $redirect, $force_reauth ) {
		if ( empty( $this->slug ) ) {
			return $login_url;
		}

		$new_url = trailingslashit( home_url( $this->slug ) );

		if ( ! empty( $redirect ) ) {
			$new_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $new_url );
		}
		if ( $force_reauth ) {
			$new_url = add_query_arg( 'reauth', '1', $new_url );
		}

		return $new_url;
	}

	/**
	 * Filter logout URL to use custom slug.
	 *
	 * @param string $logout_url The logout URL.
	 * @param string $redirect   Redirect URL.
	 * @return string
	 */
	public function custom_logout_url( $logout_url, $redirect ) {
		if ( empty( $this->slug ) ) {
			return $logout_url;
		}

		$new_url = trailingslashit( home_url( $this->slug ) ) . '?action=logout';

		if ( ! empty( $redirect ) ) {
			$new_url = add_query_arg( 'redirect_to', urlencode( $redirect ), $new_url );
		}

		return $new_url;
	}

	/**
	 * Replace wp-login.php in site URLs.
	 *
	 * @param string $url    The URL.
	 * @param string $path   The path.
	 * @param string $scheme The scheme.
	 * @return string
	 */
	public function filter_login_url( $url, $path, $scheme ) {
		if ( false === strpos( $url, 'wp-login.php' ) ) {
			return $url;
		}

		if ( false !== strpos( $url, 'wp-login.php?action=postpass' ) ) {
			return $url;
		}

		if ( empty( $this->slug ) ) {
			return $url;
		}

		$parsed = wp_parse_url( $url );
		$query  = isset( $parsed['query'] ) ? '?' . $parsed['query'] : '';

		return trailingslashit( home_url( $this->slug ) ) . $query;
	}

	/**
	 * Block direct access to wp-login.php and wp-admin.
	 */
	public function maybe_block_default_login() {
		if ( 'yes' !== get_option( 'zacls_block_wp_login', 'no' ) ) {
			return;
		}

		$request_uri = isset( $_SERVER['REQUEST_URI'] )
			? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) )
			: '';

		if ( false !== strpos( $request_uri, 'wp-login.php' ) ) {
			$action = isset( $_GET['action'] ) // phpcs:ignore WordPress.Security.NonceVerification
				? sanitize_text_field( wp_unslash( $_GET['action'] ) ) // phpcs:ignore WordPress.Security.NonceVerification
				: '';

			if ( ! in_array( $action, array( 'postpass', 'logout' ), true ) && ! is_user_logged_in() ) {
				$this->redirect_unauthorized();
			}
		}

		if ( false !== strpos( $request_uri, '/wp-admin/' ) && ! defined( 'DOING_AJAX' ) ) {
			if ( ! is_user_logged_in() && false === strpos( $request_uri, 'admin-ajax.php' ) ) {
				$this->redirect_unauthorized();
			}
		}
	}

	/**
	 * Redirect unauthorized access.
	 */
	private function redirect_unauthorized() {
		$redirect_to = get_option( 'zacls_redirect_unauthorized', 'homepage' );

		if ( '404' === $redirect_to ) {
			global $wp_query;
			$wp_query->set_404();
			status_header( 404 );
			nocache_headers();
			include get_404_template();
			exit;
		}

		wp_safe_redirect( home_url() );
		exit;
	}

	/**
	 * Render the custom login page.
	 */
	private function render_login_page() {
		// Login page — nonce not applicable. Input sanitized with esc_url_raw.
		$redirect_to = isset( $_GET['redirect_to'] ) // phpcs:ignore WordPress.Security.NonceVerification
			? esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) // phpcs:ignore WordPress.Security.NonceVerification
			: '';

		$is_2fa_required = isset( $_GET['zacls_2fa'] ) ? sanitize_text_field( wp_unslash( $_GET['zacls_2fa'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification
		if ( 'required' === $is_2fa_required ) {
			$this->load_login_template( '', true, $redirect_to );
			return;
		}

		if ( isset( $_POST['log'] ) && isset( $_POST['pwd'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
			if ( ! isset( $_POST['_zacls_login_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_zacls_login_nonce'] ) ), 'zacls_login_nonce' ) ) { // phpcs:ignore WordPress.Security.NonceVerification
				$error = esc_html__( 'Security check failed. Please try again.', 'za-creative-login-shield' );
			} else {
				$credentials = array(
					'user_login'    => sanitize_user( wp_unslash( $_POST['log'] ) ), // phpcs:ignore WordPress.Security.NonceVerification
					// Password must not be sanitized — wp_signon() handles validation securely.
					'user_password' => wp_unslash( $_POST['pwd'] ), // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification
					'remember'      => isset( $_POST['rememberme'] ), // phpcs:ignore WordPress.Security.NonceVerification
				);
				$user        = wp_signon( $credentials, is_ssl() );

				if ( is_wp_error( $user ) ) {
					$error = $user->get_error_message();
				} else {
					wp_safe_redirect( ! empty( $redirect_to ) ? $redirect_to : admin_url() );
					exit;
				}
			}
		}

		$this->load_login_template( isset( $error ) ? $error : '', false, $redirect_to );
	}

	/**
	 * Load the login page template.
	 *
	 * @param string $error       Error message.
	 * @param bool   $show_2fa    Whether to show 2FA form.
	 * @param string $redirect_to Redirect URL.
	 */
	private function load_login_template( $error = '', $show_2fa = false, $redirect_to = '' ) {
		$title = get_bloginfo( 'name' ) . ' - ' . esc_html__( 'Login', 'za-creative-login-shield' );
		?>
		<!DOCTYPE html>
		<html <?php language_attributes(); ?>>
		<head>
		<meta charset="<?php bloginfo( 'charset' ); ?>">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">
		<title><?php echo esc_html( $title ); ?></title>
		<?php
		wp_enqueue_style(
			'zacls-login',
			ZACLS_PLUGIN_URL . 'assets/css/login.css',
			array(),
			ZACLS_VERSION
		);
		do_action( 'zacls_login_enqueue_scripts' );
		wp_head();
		?>
		</head>
		<body class="login wp-core-ui">
		<?php
		if ( ! empty( $error ) ) {
			echo '<div id="login-error" class="message error">' . esc_html( $error ) . '</div>';
		}

		echo '<div id="login" class="zacls-login-wrapper">';
		echo '<div class="zacls-login-header">';
		if ( has_custom_logo() ) {
			the_custom_logo();
		} else {
			echo '<h1 class="zacls-login-logo">' . esc_html( get_bloginfo( 'name' ) ) . '</h1>';
		}
		echo '</div>';

		if ( $show_2fa ) {
			ZACLS_TwoFA::render_2fa_login_form();
		} else {
			?>
			<form name="loginform" id="loginform" action="<?php echo esc_url( site_url( $this->slug, 'login' ) ); ?>" method="post">
				<?php wp_nonce_field( 'zacls_login_nonce', '_zacls_login_nonce' ); ?>
				<p>
					<label for="user_login"><?php esc_html_e( 'Username or Email', 'za-creative-login-shield' ); ?></label>
					<input type="text" name="log" id="user_login" class="input" value="" size="20" autocapitalize="off" autocomplete="username" required>
				</p>
				<p>
					<label for="user_pass"><?php esc_html_e( 'Password', 'za-creative-login-shield' ); ?></label>
					<input type="password" name="pwd" id="user_pass" class="input" value="" size="20" autocomplete="current-password" required>
				</p>
				<?php if ( ! empty( $redirect_to ) ) : ?>
					<input type="hidden" name="redirect_to" value="<?php echo esc_url( $redirect_to ); ?>">
				<?php endif; ?>
				<p class="rememberme">
					<label>
						<input name="rememberme" type="checkbox" id="rememberme" value="forever">
						<?php esc_html_e( 'Remember Me', 'za-creative-login-shield' ); ?>
					</label>
				</p>
				<p class="submit">
					<input type="submit" name="wp-submit" id="wp-submit" class="button button-primary button-large" value="<?php esc_attr_e( 'Log In', 'za-creative-login-shield' ); ?>">
				</p>
			</form>
			<?php
			do_action( 'zacls_login_form_extra' );
		}

		echo '<div class="zacls-login-footer" style="text-align:center;margin-top:16px;">';
		echo '<a href="' . esc_url( wp_lostpassword_url() ) . '">' . esc_html__( 'Lost your password?', 'za-creative-login-shield' ) . '</a>';
		echo '</div></div>';

		wp_footer();
		echo '</body></html>';
	}

	/**
	 * Redirect to custom login page after logout.
	 */
	public function on_logout_redirect() {
		if ( ! empty( $this->slug ) ) {
			wp_safe_redirect( trailingslashit( home_url( $this->slug ) ) );
			exit;
		}
	}
}
