<?php
/**
 * Load admin view for User WooCommerce Login Form.
 *
 * @package miniorange-order-notifications-woocommerce/handler/forms/
 */

namespace WCSMSOTP\Handler\Forms;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}
use WCSMSOTP\Helper\FormSessionVars;
use WCSMSOTP\Helper\MoConstants;
use WCSMSOTP\Helper\MoMessages;
use WCSMSOTP\Helper\MoFormDocs;
use WCSMSOTP\Helper\MoPHPSessions;
use WCSMSOTP\Helper\MoUtility;
use WCSMSOTP\Helper\SessionUtils;
use WCSMSOTP\Objects\FormHandler;
use WCSMSOTP\Objects\IFormHandler;
use WCSMSOTP\Objects\VerificationType;
use WCSMSOTP\Traits\Instance;
use ReflectionException;
use WP_Error;
use WP_User;

if ( ! class_exists( 'WPLoginForm' ) ) {
	/**
	 * This is the WordPress Login Form class. This class handles all the
	 * functionality related to WordPress Login. It extends the FormHandler
	 * and implements the IFormHandler class to implement some much needed functions.
	 */
	class WPLoginForm extends FormHandler implements IFormHandler {

		use Instance;

		/**
		 * Enable disable saving of phone numbers after verification
		 *
		 * @var string
		 */
		private $save_phone_numbers;

		/**
		 * Allow admins to bypass otp verification
		 *
		 * @var string
		 */
		private $by_pass_admin;

		/**
		 * Allow users to log in with their phone number
		 *
		 * @var String
		 */
		private $allow_login_through_phone;

		/**
		 * Skip Password Check and allow users to log
		 * in using OTP instead
		 *
		 * @var bool
		 */
		private $skip_password_check;

		/**
		 * The Username field label to be shown to the
		 * users.
		 *
		 * @var string
		 */
		private $user_label;

		/**
		 * Allow users to fallback to username + password
		 * if they don't wish to do login with OTP
		 *
		 * @var bool
		 */
		private $skip_pass_fallback;

		/**
		 * Create User Action Hook
		 *
		 * @var string
		 */
		private $create_user_action;

		/**
		 * Initializes values
		 */
		protected function __construct() {
			$this->is_login_or_social_form   = true;
			$this->is_ajax_form              = true;
			$this->form_session_var          = FormSessionVars::WP_LOGIN_REG_PHONE;
			$this->form_session_var2         = FormSessionVars::WP_DEFAULT_LOGIN;
			$this->phone_form_id             = '#mo_phone_number';
			$this->type_phone_tag            = 'mo_wp_login_phone_enable';
			$this->type_email_tag            = 'mo_wp_login_email_enable';
			$this->form_key                  = 'WP_DEFAULT_LOGIN';
			$this->form_name                 = mowc_( 'WooCommerce Login Form' );
			$this->is_form_enabled           = get_mo_wc_option( 'wp_login_enable' );
			$this->user_label                = get_mo_wc_option( 'wp_username_label_text' );
			$this->user_label                = $this->user_label ? mowc_( $this->user_label ) : mowc_( 'Username, E-mail or Phone No.' );
			$this->skip_password_check       = get_mo_wc_option( 'wp_login_skip_password' );
			$this->allow_login_through_phone = get_mo_wc_option( 'wp_login_allow_phone_login' );
			$this->skip_pass_fallback        = get_mo_wc_option( 'wp_login_skip_password_fallback' );
			$this->form_documents            = MoFormDocs::LOGIN_FORM;

			if ( $this->skip_password_check || $this->allow_login_through_phone ) {
				add_action( 'login_enqueue_scripts', array( $this, 'miniorange_register_login_script' ) );
				add_action( 'wp_enqueue_scripts', array( $this, 'miniorange_register_login_script' ) );
			}
			parent::__construct();
		}

		/**
		 * Function checks if form has been enabled by the admin and initializes
		 * all the class variables. This function also defines all the hooks to
		 * hook into to make OTP Verification possible.
		 */
		public function handle_form() {
			$this->otp_type            = get_mo_wc_option( 'wp_login_enable_type' );
			$this->phone_key           = get_mo_wc_option( 'wp_login_key' );
			$this->save_phone_numbers  = get_mo_wc_option( 'wp_login_register_phone' );
			$this->by_pass_admin       = get_mo_wc_option( 'wp_login_bypass_admin' );
			$this->restrict_duplicates = get_mo_wc_option( 'wp_login_restrict_duplicates' );
			add_filter( 'authenticate', array( $this, 'mo_handle_mo_wp_login' ), 99, 3 );

			add_action( 'wp_ajax_mo-admin-check', array( $this, 'isAdmin' ) );
			add_action( 'wp_ajax_nopriv_mo-admin-check', array( $this, 'isAdmin' ) );

			add_action( 'wp_ajax_mo_external_popup_option', array( $this, 'routeData' ) );
			add_action( 'wp_ajax_nopriv_mo_external_popup_option', array( $this, 'routeData' ) );
		}

		/**
		 * Function check if the user loggin in is admin.
		 */
		public function isAdmin() {
			if ( ! check_ajax_referer( $this->nonce, $this->nonce_key ) ) {
				wp_send_json(
					MoUtility::create_json(
						MoMessages::showMessage( MoMessages::UNKNOWN_ERROR ),
						MoConstants::ERROR_JSON_TYPE
					)
				);
				exit;
			}
			$post_data = MoUtility::mowc_sanitize_array( $_POST );

			$username = MoUtility::sanitize_check( 'username', $post_data );
			$user     = is_email( $username ) ? get_user_by( 'email', $username ) : get_user_by( 'login', $username );
			$const    = MoConstants::SUCCESS_JSON_TYPE;

			$const = $user ? ( in_array( 'administrator', $user->roles, true ) ? $const : 'error' ) : 'error';
			wp_send_json(
				MoUtility::create_json(
					MoMessages::showMessage( MoMessages::PHONE_EXISTS ),
					$const
				)
			);
		}

		/**
		 * This function checks what kind of OTP Verification needs to be done.
		 * and starts the otp verification process with appropriate parameters.
		 *
		 * @throws ReflectionException .
		 */
		public function routeData() {
			if ( ! array_key_exists( 'mo_external_popup_option', $_REQUEST ) ) {
				return;
			}
			$action = trim( sanitize_text_field( wp_unslash( $_REQUEST['mo_external_popup_option'] ) ) );
			if ( ! check_ajax_referer( 'mo_popup_options', 'mopopup_wpnonce', false ) ) {
				wp_send_json( MoUtility::create_json( 'Not a valid request !', MoConstants::ERROR_JSON_TYPE ) );
			}

			// Set filter to indicate this is an AJAX form request.
			add_filter( 'mowc_is_ajax_form', '__return_true', 999 );

			$post_data = MoUtility::mowc_sanitize_array( $_POST );

			switch ( $action ) {
				case 'miniorange-ajax-otp-generate':
					$this->mo_handle_wp_login_ajax_send_otp( $post_data );
					break;
				case 'miniorange-ajax-otp-validate':
					$this->mo_handle_wp_login_ajax_form_validate_action( $post_data );
					break;
				case 'mo_ajax_form_validate':
					$this->mo_handle_wp_login_create_user_action( $post_data );
					break;
			}
		}

		/**
		 * This function registers the js file for enabling OTP Verification
		 * for WP Login using AJAX calls.
		 */
		public function miniorange_register_login_script() {
			wp_register_script( 'mologin', MOV_WC_URL . 'includes/js/loginform.min.js', array( 'jquery' ), MOV_WC_VERSION, true );
			wp_localize_script(
				'mologin',
				'movarlogin',
				array(
					'userLabel'       => $this->allow_login_through_phone ? $this->user_label : null,
					'skipPwdCheck'    => $this->skip_password_check,
					'skipPwdFallback' => $this->skip_pass_fallback,
					'buttontext'      => mowc_( 'Login with OTP' ),
					'isAdminAction'   => 'mo-admin-check',
					'nonce'           => wp_create_nonce( $this->nonce ),
					'byPassAdmin'     => $this->by_pass_admin,
					'siteURL'         => mowc_wp_wc_ajax_url(),
				)
			);
			wp_enqueue_script( 'mologin' );
		}



		/**
		 * Function detects if the user trying to log in is an admin and detects
		 * if admin has set two factor bypass for Admins. Returns True or False
		 *
		 * @param WP_User $user             role or roles of the user trying to log in.
		 * @param bool    $skip_otp_process   skip validating OTP.
		 * @return bool
		 */
		private function byPassLogin( $user, $skip_otp_process ) {
			$user_meta = get_userdata( $user->data->ID );
			$user_role = $user_meta->roles;
			return ( in_array( 'administrator', $user_role, true ) && $this->by_pass_admin ) || $skip_otp_process;
		}


		/**
		 * This function is called after the OTP is verified to
		 * login the user into WordPress.
		 *
		 * @param array $post_data - $_POST.
		 */
		private function mo_handle_wp_login_create_user_action( $post_data ) {
			$verification_type = $this->get_verification_type();
			$is_validated      = SessionUtils::is_status_match( $this->form_session_var, self::VALIDATED, $verification_type );
			if ( ! $is_validated ) {
				return;
			}

			$get_user_from_post = function ( $post_data ) {
				$username = MoUtility::sanitize_check( 'log', $post_data );
				if ( ! $username ) {
					$array    = array_filter(
						$post_data,
						function ( $key ) {
							return strpos( $key, 'username' ) === 0;
						},
						ARRAY_FILTER_USE_KEY
					);
					$username = ! empty( $array ) ? array_shift( $array ) : $username;
				}
				$user = is_email( $username ) ? get_user_by( 'email', $username ) : get_user_by( 'login', $username );
				return $user;
			};

			$user = $get_user_from_post( $post_data );
			if ( ! $user ) {
				return;
			}
			$phone = isset( $post_data['mo_phone_number'] ) ? $this->check_phone_length( $post_data['mo_phone_number'] ) : '';
			update_user_meta( $user->data->ID, $this->phone_key, $phone );
			$this->login_wp_user( $user->data->user_login );
		}


		/**
		 * The function is called to login the user
		 *
		 * @param array $user_log - the username of the user logging in.
		 * @param array $extra_data - array of extra data related to the user.
		 */
		private function login_wp_user( $user_log, $extra_data = null ) {
			$user = is_email( $user_log ) ? get_user_by( 'email', $user_log ) : get_user_by( 'login', $user_log );
			if ( ! $user && $this->mo_allow_login_through_phone() ) {
				$user = $this->mo_get_user_from_phone_number( $phone_number );
			}
			if ( $user ) {
				wp_set_auth_cookie( $user->data->ID, true );
				$this->unset_otp_session_variables();
				// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- Firing core wp_login for compatibility with other plugins and WordPress.
				do_action( 'wp_login', $user->user_login, $user );
			}

			if ( 'redirect_to_the_page' === $this->redirect_after_login ) {
				$target = $this->redirect_to_page ? get_permalink( $this->redirect_to_page ) : site_url();
				if ( ! $target ) {
					$target = site_url();
				}
				wp_safe_redirect( $target );
				exit;
			} else {
				$redirect = MoUtility::is_blank( $extra_data ) ? site_url() : $extra_data;
				wp_safe_redirect( $redirect );
				exit;

			}
		}


		/**
		 * The function hooks into the authenticate hook of WordPress to
		 * start the OTP Verification process.
		 *
		 * @param array $user - the WordPress user data object containing all the user information.
		 * @param array $username - username of the user trying to log in.
		 * @param array $password - password of the user trying to log in.
		 * @return WP_Error|WP_User .
		 * @throws ReflectionException .
		 */
		public function mo_handle_mo_wp_login( $user, $username, $password ) {
			$post_data = MoUtility::mowc_sanitize_array( $_POST ); // phpcs:ignore WordPress.Security.NonceVerification.Missing -- No need for nonce verification as the function is called on third party plugin hook
			$req_data  = MoUtility::mowc_sanitize_array( $_REQUEST ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended -- No need for nonce verification as the function is called on third party plugin hook.

			if ( ! MoUtility::is_blank( $username ) ) {
				$user = $this->getUser( $username, $post_data, $password );
				if ( is_wp_error( $user ) ) {
					return $user;
				}
				$skip_otp_process = $this->skipOTPProcess( $password, $post_data, $user );
				if ( $this->byPassLogin( $user, $skip_otp_process ) ) {
					return $user;
				}

				apply_filters( 'mowc_master_otp_send_user', $user );
				$this->startOTPVerificationProcess( $user, $username, $password, $req_data );
			}
			return $user;
		}


		/**
		 * Function checks the type of verification enabled by the admins and then starts the appropriate
		 * OTP Verification.
		 *
		 * @param WP_User $user the user object of the user who needs to be logged in.
		 * @param string  $username the username provided by the user.
		 * @param string  $password the password provided by the user.
		 * @param array   $req_data $_REQUEST.
		 * @throws ReflectionException .
		 */
		private function startOTPVerificationProcess( $user, $username, $password, $req_data ) {
			$otp_type = $this->get_verification_type();
			if ( SessionUtils::is_status_match( $this->form_session_var, self::VALIDATED, $otp_type )
			|| SessionUtils::is_status_match( $this->form_session_var2, self::VALIDATED, $otp_type ) ) {
				return;
			}

			if ( VerificationType::PHONE === $otp_type ) {
				$phone_number = get_user_meta( $user->data->ID, $this->phone_key, true );
				$phone_number = $this->check_phone_length( $phone_number );
				$this->askPhoneAndStartVerification( $user, $this->phone_key, $username, $phone_number );
				$this->fetchPhoneAndStartVerification( $username, $password, $phone_number, $req_data );
			} elseif ( VerificationType::EMAIL === $otp_type ) {
				$email = $user->data->user_email;
				$this->startEmailVerification( $username, $email );
			}
		}


		/**
		 * This functions checks if user has enabled phone number as a valid username and fetches the user
		 * associated with the phone number. Checks if the skip Password is enabled with feedback to handle
		 * OTP login and normal login.
		 *
		 * @param string $username           the user's username.
		 * @param array  $post_data          $_POST.
		 * @param string $password           the users's password.
		 * @return WP_Error|WP_User
		 */
		private function getUser( $username, $post_data, $password = null ) {
			$user = is_email( $username ) ? get_user_by( 'email', $username ) : get_user_by( 'login', $username );
			if ( $this->allow_login_through_phone && MoUtility::validate_phone_number( $username ) ) {
				$username = MoUtility::process_phone_number( $username );
				$user     = $this->getUserFromPhoneNumber( $username );
			}
			if ( $user && ! $this->isLoginWithOTP( $post_data, $user->roles ) ) {
				$user = wp_authenticate_username_password( null, $user->data->user_login, $password );
			}
			return $user ? $user : new WP_Error( 'INVALID_USERNAME', mowc_( ' <b>ERROR:</b> Invalid UserName. ' ) );
		}


		/**
		 * This functions fetches the user associated with a phone number
		 *
		 * @param string $username  the user's username.
		 * @return bool|WP_User
		 */
		private function getUserFromPhoneNumber( $username ) {
			global $wpdb;
			$results = $wpdb->get_row( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery, Direct database call without caching detected -- DB Direct Query is necessary here.
				$wpdb->prepare(
					"SELECT `user_id` FROM `{$wpdb->prefix}usermeta`"
									. 'WHERE `meta_key` = %s AND `meta_value` = %s',
					array( $this->phone_key, $username )
				)
			);
			return ! MoUtility::is_blank( $results ) ? get_userdata( $results->user_id ) : false;
		}


		/**
		 * This functions is used to ask users the phone number and start the otp verification
		 * process.
		 *
		 * @param object $user the WordPress user data object containing all the user information.
		 * @param string $key the phone user_meta key which stores the user's phone number.
		 * @param string $username the user's username.
		 * @param string $phone_number the phone number entered by the user.
		 * @throws ReflectionException .
		 */
		private function askPhoneAndStartVerification( $user, $key, $username, $phone_number ) {
			if ( ! MoUtility::is_blank( $phone_number ) ) {
				return;
			}

			if ( ! $this->savePhoneNumbers() ) {
				miniorange_wc_site_otp_validation_form(
					null,
					null,
					null,
					MoMessages::showMessage( MoMessages::PHONE_NOT_FOUND ),
					null,
					null
				);
			} else {
				MoUtility::initialize_transaction( $this->form_session_var );
				$this->send_challenge(
					null,
					$user->data->user_login,
					null,
					null,
					'external',
					null,
					array(
						'data'    => array( 'user_login' => $username ),
						'message' => MoMessages::showMessage( MoMessages::REGISTER_PHONE_LOGIN ),
						'form'    => $key,
						'curl'    => MoUtility::current_page_url(),
					)
				);
			}
		}


		/**
		 * This functions is used to fetch the phone number from the database and start
		 * the OTP Verification process.
		 *
		 * @param array $username - the user's username.
		 * @param array $password - the password provided by the user.
		 * @param array $phone_number - phone number to send otp to.
		 * @param array $req_data - $_REQUEST.
		 * @throws ReflectionException .
		 */
		private function fetchPhoneAndStartVerification( $username, $password, $phone_number, $req_data ) {
			MoUtility::initialize_transaction( $this->form_session_var2 );
			MoPHPSessions::add_session_var( 'mowc_otp_form_context', $this->form_session_var2 );
			$redirect_to = isset( $req_data['redirect_to'] ) ? sanitize_text_field( $req_data['redirect_to'] ) : MoUtility::current_page_url();
			$this->send_challenge( $username, null, null, $phone_number, VerificationType::PHONE, $password, $redirect_to, false );
		}


		/**
		 * This functions is used to  start the otp verification process via email.
		 *
		 * @param array $username - the user's username.
		 * @param array $email - email to send otp to.
		 * @throws ReflectionException .
		 */
		private function startEmailVerification( $username, $email ) {
			MoUtility::initialize_transaction( $this->form_session_var2 );
			MoPHPSessions::add_session_var( 'mowc_otp_form_context', $this->form_session_var2 );
			$this->send_challenge( $username, $email, null, null, VerificationType::EMAIL );
		}


		/**
		 * This function is used to send the OTP to the user's phone number.
		 *
		 * @param array $post_data - $_POST.
		 */
		private function mo_handle_wp_login_ajax_send_otp( $post_data ) {
			$user_phone = isset( $post_data['user_phone'] ) ? trim( sanitize_text_field( $post_data['user_phone'] ) ) : '';
			MoUtility::initialize_transaction( $this->form_session_var );
			if ( $this->restrict_duplicates()
			&& ! MoUtility::is_blank( $this->getUserFromPhoneNumber( $user_phone ) ) ) {
				wp_send_json(
					MoUtility::create_json(
						MoMessages::showMessage( MoMessages::PHONE_EXISTS ),
						MoConstants::ERROR_JSON_TYPE
					)
				);
				return;
			}
			$this->send_challenge( 'ajax_phone', '', null, $user_phone, VerificationType::PHONE, null, $post_data );
		}


		/**
		 * This function is used to process the OTP entered by the user. Check
		 * if the phone number being sent is the same one OTP was sent to .
		 *
		 * @param array $post_data - $_POST.
		 */
		private function mo_handle_wp_login_ajax_form_validate_action( $post_data ) {
			if ( ! SessionUtils::is_otp_initialized( $this->form_session_var ) ) {
				return;
			}

			$phone      = MoPHPSessions::get_session_var( 'phone_number_mo' );
			$user_phone = isset( $post_data['user_phone'] ) ? $this->check_phone_length( $post_data['user_phone'] ) : '';
			if ( strcmp( $phone, $user_phone ) ) {
				wp_send_json(
					MoUtility::create_json(
						MoMessages::showMessage( MoMessages::PHONE_MISMATCH ),
						MoConstants::ERROR_JSON_TYPE
					)
				);
			} else {
				$verification_type = $this->get_verification_type();
				$this->validate_challenge( $verification_type );
			}
		}


		/**
		 * This function hooks into the otp_verification_failed hook. This function
		 * details what is done if the OTP verification fails.
		 *
		 * @param string $user_login the username posted by the user.
		 * @param string $user_email the email posted by the user.
		 * @param string $phone_number the phone number posted by the user.
		 * @param string $otp_type the verification type.
		 */
		public function handle_failed_verification( $user_login, $user_email, $phone_number, $otp_type ) {
			if ( SessionUtils::is_otp_initialized( $this->form_session_var ) ) {
				SessionUtils::add_status( $this->form_session_var, self::VERIFICATION_FAILED, $otp_type );
				wp_send_json( MoUtility::create_json( MoUtility::get_invalid_otp_method(), MoConstants::ERROR_JSON_TYPE ) );
			}

			if ( SessionUtils::is_otp_initialized( $this->form_session_var2 ) ) {
				miniorange_wc_site_otp_validation_form(
					$user_login,
					$user_email,
					$phone_number,
					MoUtility::get_invalid_otp_method(),
					'phone',
					false
				);
			}
		}


		/**
		 * This function hooks into the otp_verification_successful hook. This function is
		 * details what needs to be done if OTP Verification is successful.
		 *
		 * @param string $redirect_to the redirect to URL after new user registration.
		 * @param string $user_login the username posted by the user.
		 * @param string $user_email the email posted by the user.
		 * @param string $password the password posted by the user.
		 * @param string $phone_number the phone number posted by the user.
		 * @param string $extra_data any extra data posted by the user.
		 * @param string $otp_type the verification type.
		 */
		public function handle_post_verification( $redirect_to, $user_login, $user_email, $password, $phone_number, $extra_data, $otp_type ) {
			if ( ( ! isset( $_POST['mopopup_wpnonce'] ) || ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['mopopup_wpnonce'] ) ), 'mo_popup_options' ) ) ) ) {
				return;
			}
			$post_data = MoUtility::mowc_sanitize_array( $_POST );
			if ( SessionUtils::is_otp_initialized( $this->form_session_var ) ) {
				SessionUtils::add_status( $this->form_session_var, self::VALIDATED, $otp_type );
				wp_send_json( MoUtility::create_json( '', MoConstants::SUCCESS_JSON_TYPE ) );
			}

			// Popup flow (email or phone from login form): form_session_var2 or form context for session persistence.
			$is_wp_login_popup_flow = SessionUtils::is_otp_initialized( $this->form_session_var2 )
				|| ( MoPHPSessions::get_session_var( 'mowc_otp_form_context' ) === $this->form_session_var2 );

			if ( $is_wp_login_popup_flow ) {
				SessionUtils::add_status( $this->form_session_var2, self::VALIDATED, $otp_type );
				MoPHPSessions::unset_session( 'mowc_otp_form_context' );
				$username = MoUtility::is_blank( $user_login ) ? MoUtility::sanitize_check( 'log', $post_data ) : $user_login;
				$username = MoUtility::is_blank( $username ) ? MoUtility::sanitize_check( 'username', $post_data ) : $username;
				$this->login_wp_user( $username, $extra_data );
			}
		}


		/**
		 * Unset all the session variables so that a new form submission starts
		 * a fresh process of OTP verification.
		 */
		public function unset_otp_session_variables() {
			SessionUtils::unset_session( array( $this->tx_session_id, $this->form_session_var, $this->form_session_var2 ) );
			MoPHPSessions::unset_session( 'mowc_otp_form_context' );
		}


		/**
		 * This function is called by the filter mo_phone_dropdown_selector
		 * to return the Jquery selector of the phone field. The function will
		 * push the formID to the selector array if OTP Verification for the
		 * form has been enabled.
		 *
		 * @param  array $selector - the Jquery selector to be modified.
		 * @return array
		 */
		public function get_phone_number_selector( $selector ) {
			if ( $this->is_form_enabled() ) {
				array_push( $selector, $this->phone_form_id );
			}
			return $selector;
		}


		/**
		 * Checks if user has initiated login with OTP.
		 *
		 * @param array  $post_data - $_POST.
		 * @param string $user_roles to check the user roles.
		 * @return TRUE or FALSE
		 */
		private function isLoginWithOTP( $post_data, $user_roles = array() ) {
			$login_with_otp_text = mowc_( 'Login with OTP' );
			if ( in_array( 'administrator', $user_roles, true ) && $this->by_pass_admin ) {
				return false;
			}
			return MoUtility::sanitize_check( 'wp-submit', $post_data ) === $login_with_otp_text || MoUtility::sanitize_check( 'login', $post_data ) === $login_with_otp_text || MoUtility::sanitize_check( 'logintype', $post_data ) === $login_with_otp_text;
		}

		/**
		 * Check if the user needs to be validated via OTP. Makes sure to check if admin has
		 * allowed fallback. If so check if password is entered by the user. If password is entered
		 * then do not initiate OTP
		 *
		 * @param string $password  password entered by the user.
		 * @param array  $post_data - $_POST.
		 * @param object $user - roles of the user trying to log in.
		 * @return bool
		 */
		private function skipOTPProcess( $password, $post_data, $user ) {
			$user_meta = get_userdata( $user->data->ID );
			return $this->skip_password_check && $this->skip_pass_fallback && isset( $password ) && ! $this->isLoginWithOTP( $post_data, $user_meta->roles );
		}

		/**
		 * Function to check the length of the phone number
		 *
		 * @param array $phone - check the phone length.
		 */
		private function check_phone_length( $phone ) {
			$phone_check = MoUtility::process_phone_number( $phone );
			return strlen( $phone_check ) >= 5 ? $phone_check : '';
		}


		/**
		 * Handles saving all the WordPress Login Form related options by the admin.
		 */
		public function handle_form_options() {
			if ( ! MoUtility::are_form_options_being_saved( $this->get_form_option() ) ) {
				return;
			}

			$this->is_form_enabled           = $this->sanitize_form_post( 'wp_login_enable' );
			$this->save_phone_numbers        = $this->sanitize_form_post( 'wp_login_register_phone' );
			$this->by_pass_admin             = $this->sanitize_form_post( 'wp_login_bypass_admin' );
			$this->phone_key                 = $this->sanitize_form_post( 'wp_login_phone_field_key' );
			$this->allow_login_through_phone = $this->sanitize_form_post( 'wp_login_allow_phone_login' );
			$this->restrict_duplicates       = $this->sanitize_form_post( 'wp_login_restrict_duplicates' );
			$this->otp_type                  = $this->sanitize_form_post( 'wp_login_enable_type' );
			$this->skip_password_check       = $this->sanitize_form_post( 'wp_login_skip_password' );
			$this->user_label                = $this->sanitize_form_post( 'wp_username_label_text' );
			$this->skip_pass_fallback        = $this->sanitize_form_post( 'wp_login_skip_password_fallback' );

			update_mo_wc_option( 'wp_login_enable_type', $this->otp_type );
			update_mo_wc_option( 'wp_login_enable', $this->is_form_enabled );
			update_mo_wc_option( 'wp_login_register_phone', $this->save_phone_numbers );
			update_mo_wc_option( 'wp_login_bypass_admin', $this->by_pass_admin );
			update_mo_wc_option( 'wp_login_key', $this->phone_key );
			update_mo_wc_option( 'wp_login_allow_phone_login', $this->allow_login_through_phone );
			update_mo_wc_option( 'wp_login_restrict_duplicates', $this->restrict_duplicates );
			update_mo_wc_option( 'wp_login_skip_password', $this->skip_password_check && $this->is_form_enabled );
			update_mo_wc_option( 'wp_login_skip_password_fallback', $this->skip_pass_fallback );
			update_mo_wc_option( 'wp_username_label_text', $this->user_label );
		}


		/*
		|--------------------------------------------------------------------------------------------------------
		| Getters
		|--------------------------------------------------------------------------------------------------------
		*/
		/**
		 * Checks if admin has set the option to save the phone number in the database for each user.
		 *
		 * @return string
		 */
		public function savePhoneNumbers() {
			return $this->save_phone_numbers; }

		/**
		 * Checks if admin has set the option to bypass two factor for logged in users.
		 *
		 * @return string
		 */
		public function byPassCheckForAdmins() {
			return $this->by_pass_admin; }

		/**
		 * Checks if admin has set the option to allow phone number login
		 *
		 * @return String
		 */
		public function allowLoginThroughPhone() {
			return $this->allow_login_through_phone; }

		/**
		 * Checks if admin has set the option to allow login through username+otp
		 *
		 * @return bool|String
		 */
		public function getSkipPasswordCheck() {
			return $this->skip_password_check; }

		/**
		 * Gets the User Label Text to be shown on the Default Login Form
		 *
		 * @return string
		 */
		public function getUserLabel() {
			return mowc_( $this->user_label ); }

		/**
		 * Checks if admin has set the option to allow users to use username + password as well as username + otp
		 *
		 * @return bool
		 */
		public function getSkipPasswordCheckFallback() {
			return $this->skip_pass_fallback; }
	}
}
