<?php
namespace PowerpackElements\Classes;

use PowerpackElements\Classes\PP_Admin_Settings;

/**
 * Handles logic for login and registration pages.
 *
 * @package PowerPack
 * @since 1.4.15
 */

/**
 * Exit if accessed directly.
 */
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * PP_Login_Register.
 */
final class PP_Login_Register {
	private static $cached_data = array();

	/**
	 * Initializing PowerPack maintenance mode.
	 *
	 * @since 1.4.15
	 */
	public static function init() {
		add_action( 'login_init', __CLASS__ . '::redirect' );
		add_action( 'template_redirect', __CLASS__ . '::logged_in_redirect' );
		add_filter( 'authenticate', __CLASS__ . '::auth_redirect', 10, 3 );
		add_action( 'wp_logout', __CLASS__ . '::logout_redirect' );
	}

	/**
	 * Get pages.
	 *
	 * Get all pages and create options for select field.
	 *
	 * @since 1.4.15
	 * @param string $selected  Selected page for the field.
	 * @return array $options   An array of pages.
	 */
	public static function get_pages( $selected = '' ) {
		if ( empty( self::$cached_data ) ) {
			$args = array(
				'post_type'         => 'page',
				'post_status'       => 'publish',
				'orderby'           => 'title',
				'order'             => 'ASC',
				'posts_per_page'    => '-1',
				'update_post_meta_cache' => false,
			);

			self::$cached_data = get_posts( $args );
		}

		$options = '<option value="">' . __( '-- Select --', 'powerpack' ) . '</option>';

		if ( count( self::$cached_data ) ) {
			foreach ( self::$cached_data as $post ) {
				$options .= '<option value="' . $post->ID . '" ' . selected( $selected, $post->ID, false ) . '>' . $post->post_title . '</option>';
			}
		} else {
			$options = '<option value="" disabled>' . __( 'No pages found!', 'powerpack' ) . '</option>';
		}

		return $options;
	}

	/**
	 * Page URL.
	 *
	 * Resolves a stored page ID to a permalink, refusing anything that would
	 * strand the visitor: a page that was trashed, unpublished or deleted after
	 * being selected sends them to a 404 with no route back to wp-login.php.
	 * Returning an empty string leaves WordPress in charge of the request.
	 *
	 * @since 3.0.0
	 * @param string|int $page_id  Stored page ID.
	 * @return string              Permalink, or an empty string if unusable.
	 */
	private static function get_page_url( $page_id ) {
		$page_id = absint( $page_id );

		if ( ! $page_id || 'publish' !== get_post_status( $page_id ) ) {
			return '';
		}

		$url = get_permalink( $page_id );

		return $url ? $url : '';
	}

	/**
	 * Redirect.
	 *
	 * Redirects wp-login.php to the custom login page or register page.
	 *
	 * Only the login and register actions are taken over — logout, password
	 * reset, post password and email confirmation stay on wp-login.php, where
	 * WordPress still owns the whole flow and we have no page to send them to.
	 *
	 * @since 1.4.15
	 * @return void
	 */
	public static function redirect() {
		if ( ! PP_Admin_Settings::get_option( 'pp_login_register_redirect', true ) ) {
			return;
		}

		/*
		 * Both forms post back to wp-login.php. Redirecting a POST would drop
		 * the submitted credentials, so signing in would become impossible —
		 * only ordinary page loads are rerouted.
		 */
		$method = isset( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ) ) ) : 'GET';

		if ( 'GET' !== $method ) {
			return;
		}

		// The re-auth modal inside wp-admin has nowhere else to render.
		if ( isset( $_REQUEST['interim-login'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			return;
		}

		$action = isset( $_GET['action'] ) ? sanitize_key( wp_unslash( $_GET['action'] ) ) : 'login'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended

		if ( 'register' === $action ) {
			$page_id = PP_Admin_Settings::get_option( 'pp_register_page', true );
		} elseif ( 'login' === $action ) {
			// A logged-in visitor hitting wp-login.php is asking to switch
			// accounts; WordPress handles that better than a login page would.
			if ( is_user_logged_in() ) {
				return;
			}

			$page_id = PP_Admin_Settings::get_option( 'pp_login_page', true );
		} else {
			return;
		}

		$redirect_to = self::get_page_url( $page_id );

		if ( ! $redirect_to ) {
			return;
		}

		/*
		 * Carry the original destination through so the Login Form widget can
		 * still send the user where they were headed. add_query_arg() does not
		 * encode values, hence the explicit rawurlencode().
		 */
		if ( ! empty( $_REQUEST['redirect_to'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			$destination = esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended

			if ( $destination ) {
				$redirect_to = add_query_arg( 'redirect_to', rawurlencode( $destination ), $redirect_to );
			}
		}

		wp_safe_redirect( $redirect_to );
		exit;
	}

	/**
	 * Logged-in redirect destination.
	 *
	 * @since 3.0.0
	 * @param string $target  Stored destination key.
	 * @return string         URL, or an empty string if it cannot be resolved.
	 */
	private static function get_logged_in_url( $target ) {
		switch ( $target ) {
			case 'home':
				return home_url( '/' );

			case 'dashboard':
				return admin_url();

			case 'page':
				return self::get_page_url( PP_Admin_Settings::get_option( 'pp_login_register_logged_in_page', true ) );
		}

		return '';
	}

	/**
	 * Logged-in redirect.
	 *
	 * Sends a visitor who is already signed in away from the login or register
	 * page, which has nothing left to offer them.
	 *
	 * @since 3.0.0
	 * @return void
	 */
	public static function logged_in_redirect() {
		$target = PP_Admin_Settings::get_option( 'pp_login_register_logged_in_redirect', true );

		if ( empty( $target ) || 'none' === $target ) {
			return;
		}

		if ( ! is_user_logged_in() || ! is_page() ) {
			return;
		}

		/*
		 * Building the login page means loading it while signed in. Redirecting
		 * the Elementor preview iframe or a draft preview would put the page
		 * permanently out of reach of the person who has to design it.
		 */
		if ( PP_Helper::is_edit_mode() || PP_Helper::is_preview_mode() || is_preview() ) {
			return;
		}

		$current = get_queried_object_id();

		$pages = [
			absint( PP_Admin_Settings::get_option( 'pp_login_page', true ) ),
			absint( PP_Admin_Settings::get_option( 'pp_register_page', true ) ),
		];

		if ( ! $current || ! in_array( $current, $pages, true ) ) {
			return;
		}

		$redirect_to = self::get_logged_in_url( $target );

		// A destination pointing back at the page we are on would loop.
		if ( ! $redirect_to || untrailingslashit( $redirect_to ) === untrailingslashit( (string) get_permalink( $current ) ) ) {
			return;
		}

		wp_safe_redirect( $redirect_to );
		exit;
	}

	/**
	 * Authentication redirect.
	 *
	 * Redirect to custom login page if username and password fields
	 * left empty.
	 *
	 * @since 1.4.15
	 * @param object $user      User object.
	 * @param string $username  User's login name.
	 * @param string $password  User's login password.
	 * @return object $user
	 */
	public static function auth_redirect( $user, $username, $password ) {
		if ( isset( $_REQUEST['interim-login'] ) ) {
			return $user;
		}

		if ( empty( $username ) || empty( $password ) ) {
			$id = PP_Admin_Settings::get_option( 'pp_login_page', true );

			if ( ! empty( $id ) ) {
				$login_page = get_permalink( $id );

				wp_redirect( $login_page );
				exit;
			}
		}

		return $user;
	}

	/**
	 * Logout redirect.
	 *
	 * Redirects to login page after succesful logout.
	 *
	 * @since 1.4.15
	 * @return void
	 */
	public static function logout_redirect() {
		$id = PP_Admin_Settings::get_option( 'pp_login_page', true );

		if ( ! empty( $id ) ) {
			$login_page = get_permalink( $id );

			wp_redirect( $login_page );
			exit;
		}
	}
}

// Initialize the class.
PP_Login_Register::init();
