<?php
/**
 * User Registration and Login class for SureForms
 *
 * @package sureforms-pro.
 * @since 1.8.0
 */

namespace SRFM_Pro\Inc\Business\User_Registration;

use SRFM\Inc\Helper;
use SRFM_Pro\Inc\Helper as Pro_Helper;
use SRFM_Pro\Inc\Traits\Get_Instance;

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

/**
 * User Registration and Login class for SureForms
 *
 * @since 1.8.0
 */
class Init {
	use Get_Instance;

	/**
	 * Associative array to keep the count of block that requires scripts to work.
	 *
	 * @var array<int>
	 * @since 1.8.0
	 */
	public static $script_dep_blocks = [
		'password' => 0,
		'link'     => 0,
	];

	/**
	 * Constructor
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function __construct() {
		add_filter( 'srfm_register_additional_blocks', [ $this, 'register_field' ] );
		add_filter( 'srfm_allowed_block_types', [ $this, 'allow_user_registration_blocks_in_sureforms' ], 10, 1 );
		add_filter( 'srfm_blocks', [ $this, 'allow_user_registration_blocks_in_sureforms' ] );
		add_filter( 'render_block', [ $this, 'generate_render_script' ], 10, 2 );
		add_action( 'enqueue_block_editor_assets', [ $this, 'enqueue_block_editor_assets' ] );
		add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_script' ] );
		add_filter( 'srfm_dynamic_validation_messages', [ $this, 'add_password_messages' ] );
		add_filter( 'srfm_general_dynamic_options_to_save', [ $this, 'add_password_validation_messages_to_save' ], 10, 2 );
		add_filter( 'srfm_default_dynamic_block_option', [ $this, 'add_password_default_dynamic_pro_block_values' ], 10, 2 );
		add_filter( 'srfm_pro_updater_callbacks', [ $this, 'add_password_dynamic_option_callback' ] );
		add_action( 'srfm_enqueue_common_field_assets', [ $this, 'get_user_registration_assets' ] );
		add_action( 'srfm_form_css_variables', [ $this, 'render_user_registration_css_variables' ] );
		add_filter( 'srfm_css_vars_sizes', [ $this, 'user_registration_spacing_variables' ] );
		add_filter( 'srfm_rest_api_endpoints', [ $this, 'add_login_endpoint' ] );
		add_filter( 'srfm_block_preview_images', [ $this, 'add_login_block_preview' ] );
		add_filter( 'srfm_smart_tag_list', [ $this, 'add_user_registration_smart_tags' ] );
		add_filter( 'srfm_parse_smart_tags', [ $this, 'parse_user_registration_smart_tags' ], 10, 2 );
		add_filter( 'srfm_save_user_registration_settings', [ $this, 'save_user_registration_settings' ], 10, 2 );
		add_filter( 'srfm_pro_global_settings', [ $this, 'get_user_registration_settings' ], 10, 2 );
		add_action( 'srfm_register_additional_post_meta', [ $this, 'add_user_registration_meta' ] );

		// Add redirect hooks for custom login and registration pages.
		add_action( 'login_init', [ $this, 'redirect_to_custom_pages' ] );

		// Filter login URL to use custom login page if configured.
		add_filter( 'login_url', [ $this, 'filter_login_url' ], 10, 3 );
		add_filter( 'register_url', [ $this, 'filter_register_url' ], 10, 1 );

		// Load user registration processor.
		Processor::get_instance();
	}

	/**
	 * Add pro block's default error message values.
	 *
	 * @param array<mixed> $default_values the default values.
	 * @param array<mixed> $common_err_msg the common error message.
	 * @since 1.8.0
	 * @return array<mixed>
	 */
	public static function add_password_default_dynamic_pro_block_values( $default_values, $common_err_msg ) {

		$default_pro_values = [
			'srfm_password_block_required_text'  => $common_err_msg['required'],
			'srfm_password_strength_weak'        => __( 'Your password strength is weak.', 'sureforms-pro' ),
			'srfm_password_strength_medium'      => __( 'Your password strength is moderate.', 'sureforms-pro' ),
			'srfm_password_strength_strong'      => __( 'Your password strength is strong.', 'sureforms-pro' ),
			'srfm_password_strength_very_strong' => __( 'Your password strength is very strong.', 'sureforms-pro' ),
			'srfm_password_mismatch'             => __( 'Confirmation password does not match.', 'sureforms-pro' ),
		];

		return array_merge( $default_values, $default_pro_values );
	}

	/**
	 * Allow blocks related to User Registration and Login Feature in SureForms.
	 * 1. Password Block
	 *
	 * @param array<string> $blocks Array of blocks.
	 *
	 * @since 1.8.0
	 * @return array<string>
	 */
	public function allow_user_registration_blocks_in_sureforms( $blocks ) {
		return array_merge(
			$blocks,
			[
				'srfm/password',
				'srfm/login',
				'srfm/link',
				'srfm/register',
			]
		);
	}

	/**
	 * Add password field messages.
	 *
	 * @param array<string> $default_options Default options.
	 * @param array<string> $setting_options Setting options.
	 *
	 * @since 1.8.0
	 * @return array<string>
	 */
	public function add_password_validation_messages_to_save( $default_options, $setting_options ) {
		$pro_options_keys    = [
			'srfm_password_block_required_text',
			'srfm_password_strength_weak',
			'srfm_password_strength_medium',
			'srfm_password_strength_strong',
			'srfm_password_strength_very_strong',
			'srfm_password_mismatch',
		];
		$pro_options_to_save = [];
		foreach ( $pro_options_keys as $key ) {
			if ( isset( $setting_options[ $key ] ) ) {
				$pro_options_to_save[ $key ] = $setting_options[ $key ];
			}
		}
		return array_merge( $default_options, $pro_options_to_save );
	}

	/**
	 * Retrieve default dynamic validation messages.
	 *
	 * @param array<string, string> $default_value Associative array of translated validation messages for frontend use.
	 * @since 1.8.0
	 * @return array Associative array of translated validation messages for frontend use.
	 */
	public static function add_password_messages( $default_value = [] ) {
		$translatable_array = [
			'srfm_password_block_required_text'  => __( 'This field is required.', 'sureforms-pro' ),
			'srfm_password_strength_weak'        => __( 'Your password strength is weak.', 'sureforms-pro' ),
			'srfm_password_strength_medium'      => __( 'Your password strength is moderate.', 'sureforms-pro' ),
			'srfm_password_strength_strong'      => __( 'Your password strength is strong.', 'sureforms-pro' ),
			'srfm_password_strength_very_strong' => __( 'Your password strength is very strong.', 'sureforms-pro' ),
			'srfm_password_mismatch'             => __( 'Confirmation password does not match.', 'sureforms-pro' ),
		];

		return ! empty( $default_value ) && is_array( $default_value ) ? array_merge( $default_value, $translatable_array ) : $translatable_array;
	}

	/**
	 * Render function.
	 *
	 * @param string $block_content Entire Block Content.
	 * @param array  $block Block Properties As An Array.
	 * @since 1.8.0
	 * @return string
	 */
	public function generate_render_script( $block_content, $block ) {
		// Check if the block is one of the user registration blocks and enqueue the required assets.
		$blocks = [ 'srfm/password', 'srfm/link' ];
		if ( isset( $block['blockName'] ) && in_array( $block['blockName'], $blocks, true ) ) {
			self::enqueue_frontend_assets( $block['blockName'] );
		}
		return $block_content;
	}

	/**
	 * Enqueue required styles for the user registration in the editor and frontend.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function enqueue_styles() {
		$file_prefix = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? '' : '.min';
		$dir_name    = defined( 'SRFM_DEBUG' ) && SRFM_DEBUG ? 'unminified' : 'minified';
		$css_uri     = SRFM_PRO_URL . 'assets/css/' . $dir_name . '/package/business/';
		// This checks the text direction of the site, if it is RTL then it will load the RTL version of the CSS file.
		$rtl = is_rtl() ? '-rtl' : '';

		// Load the frontend custom app styles.
		wp_enqueue_style( SRFM_PRO_SLUG . '-password', $css_uri . 'password' . $file_prefix . $rtl . '.css', [], SRFM_PRO_VER );
		wp_enqueue_style( SRFM_PRO_SLUG . '-user-registration', $css_uri . 'user-registration' . $file_prefix . $rtl . '.css', [], SRFM_PRO_VER );
	}

	/**
	 * Register user registration blocks.
	 *
	 * @param array<mixed> $blocks Array of blocks.
	 *
	 * @since 1.8.0
	 * @return array<mixed>
	 */
	public function register_field( $blocks ) {
		$block_files = [ 'password', 'login', 'link', 'register' ];
		foreach ( $block_files as $file ) {
			$blocks[] = [
				'dir'       => SRFM_PRO_DIR . "inc/business/user-registration/{$file}/block.php",
				'namespace' => 'SRFM_PRO\\Inc\\Blocks',
			];
		}

		return $blocks;
	}

	/**
	 * Enqueue user registration form admin scripts.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function enqueue_block_editor_assets() {
		$this->enqueue_styles();
		$scripts = [
			[
				'unique_file'        => 'srfmUserRegistrationAdmin',
				'unique_handle'      => 'user-registration-admin',
				'extra_dependencies' => [ 'srfm-blocks', SRFM_PRO_SLUG . '-block-editor' ],
			],
		];
		$this->enqueue_scripts( $scripts );
	}

	/**
	 * Enqueue user registration admin scripts.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function enqueue_admin_script() {
		$scripts = [
			[
				'unique_file'        => 'srfmUserRegistration',
				'unique_handle'      => 'user-registration',
				'extra_dependencies' => [],
			],
		];
		$this->enqueue_scripts( $scripts );
	}

	/**
	 * Enqueue user registration frontend scripts.
	 *
	 * @param string $block_type Block type.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function enqueue_frontend_assets( $block_type = '' ) {
		$block_name = str_replace( 'srfm/', '', $block_type );

		// Check if block is in the array and check if block is already enqueued.
		if (
			in_array( $block_name, array_keys( self::$script_dep_blocks ), true ) &&
			0 === self::$script_dep_blocks[ $block_name ]
		) {
			self::$script_dep_blocks[ $block_name ] += 1;
			$this->get_user_registration_assets();
		}
	}

	/**
	 * Get user registration assets.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function get_user_registration_assets() {
		$this->enqueue_styles();
		wp_enqueue_script( 'srfm-pro-zxcvbn', SRFM_PRO_URL . 'assets/js/minified/deps/zxcvbn.min.js', [], SRFM_PRO_VER, true );
		$scripts = [
			[
				'unique_file'         => 'srfmUserRegistrationFrontend',
				'unique_handle'       => 'user-registration-frontend',
				'extra_dependencies'  => [ 'srfm-pro-zxcvbn' ],
				'maybe_localize_data' => true,
			],
		];
		$this->enqueue_scripts( $scripts );
	}

	/**
	 * Render user registration CSS variables.
	 *
	 * @param array<string,string> $params array of values sent by action 'srfm_form_css_variables'.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function render_user_registration_css_variables( $params ) {
		// The unset is used to handle php insights error for unused parameter.
		unset( $params );
		$css_variables = [
			'--srfm-password-strength-weak'                => '#DC2626',
			'--srfm-password-strength-weak-border-glow'    => 'hsl( from var(--srfm-password-strength-weak) h s l / 0.15 )',
			'--srfm-password-strength-weak-border-color'   => 'hsl( from var(--srfm-password-strength-weak) h s l / 0.65 )',
			'--srfm-password-strength-medium'              => '#D97706',
			'--srfm-password-strength-medium-border-glow'  => 'hsl( from var(--srfm-password-strength-medium) h s l / 0.15 )',
			'--srfm-password-strength-medium-border-color' => 'hsl( from var(--srfm-password-strength-medium) h s l / 0.65 )',
			'--srfm-password-strength-strong'              => '#16A34A',
			'--srfm-password-strength-strong-border-glow'  => 'hsl( from var(--srfm-password-strength-strong) h s l / 0.15 )',
			'--srfm-password-strength-strong-border-color' => 'hsl( from var(--srfm-password-strength-strong) h s l / 0.65 )',
		];

		// Escape and echo the CSS variables.
		Pro_Helper::add_css_variables( $css_variables );
	}

	/**
	 * Add the spacing variables for the login block used in user registration.
	 * This is used in the frontend.
	 *
	 * @param array<string|mixed> $sizes array of css variables coming from 'srfm_css_vars_sizes' filter.
	 * @since 1.8.0
	 * @return array<string|mixed>
	 */
	public function user_registration_spacing_variables( $sizes ) {
		return array_replace_recursive(
			$sizes,
			[
				'small'  => [
					'--srfm-login-row-gap' => '12px',
				],
				'medium' => [
					'--srfm-login-row-gap' => '14px',
				],
				'large'  => [
					'--srfm-login-row-gap' => '16px',
				],
			]
		);
	}

	/**
	 * Add password field dynamic option callback.
	 *
	 * @param array<string> $callbacks Array of callbacks.
	 *
	 * @since 1.8.0
	 * @return array<mixed>
	 */
	public function add_password_dynamic_option_callback( $callbacks ) {
		$callbacks['1.8.0'] = [ 'SRFM_Pro\Inc\Business\User_Registration\Init::add_password_default_dynamic_options' ];
		return $callbacks;
	}

	/**
	 * Add password field dynamic option callback.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public static function add_password_default_dynamic_options() {
		$previous_options = get_option( 'srfm_default_dynamic_block_option' );

		if ( ! empty( $previous_options ) && is_array( $previous_options ) ) {
			$required_message = Helper::get_common_err_msg();
			$current_options  = self::add_password_messages();
			$current_options  = self::add_password_default_dynamic_pro_block_values( $current_options, $required_message );

			// Iterate $current_options and update the options.
			foreach ( $current_options as $key => $value ) {
				if ( ! isset( $previous_options[ $key ] ) ) {
					$previous_options[ $key ] = $value;
				}
			}
			update_option( 'srfm_default_dynamic_block_option', $previous_options );
		}
	}

	/**
	 * Add login endpoint.
	 *
	 * @param array<string, mixed> $endpoints Array of endpoints.
	 * @since 1.8.0
	 * @return array<string, mixed>
	 */
	public function add_login_endpoint( $endpoints ) {
		$endpoints['login'] = [
			'methods'             => 'POST',
			'callback'            => [ $this, 'srfm_handle_login' ],
			'permission_callback' => '__return_true',
		];

		// Add the endpoint for fetching the user roles.
		$endpoints['user-roles']       = [
			'methods'             => 'GET',
			'callback'            => [ $this, 'get_user_roles' ],
			'permission_callback' => static function () {
				return Helper::current_user_can(); // Only allow admins to access this endpoint.
			},
		];
		$endpoints['custom-meta-keys'] = [
			'methods'             => 'GET',
			'callback'            => [ $this, 'get_custom_meta_keys' ],
			'permission_callback' => static function () {
				return Helper::current_user_can(); // Only allow admins to access this endpoint.
			},
		];

		return $endpoints;
	}

	/**
	 * Handle login request.
	 *
	 * @param \WP_REST_Request $request The request object.
	 * @since 1.8.0
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function srfm_handle_login( $request ) {
		$nonce = Helper::get_string_value( $request->get_header( 'X-WP-Nonce' ) );
		if ( ! wp_verify_nonce( sanitize_text_field( $nonce ), 'wp_rest' ) ) {
			return new \WP_Error( 'invalid_nonce', __( 'Nonce Verification Failed', 'sureforms-pro' ), [ 'status' => 403 ] );
		}

		// If the user is logged in then return an error.
		if ( is_user_logged_in() ) {
			return new \WP_REST_Response(
				[
					'success' => false,
					'code'    => 'already_logged_in',
					'message' => __( 'You are already logged in.', 'sureforms-pro' ),
				],
				200
			);
		}

		$creds = [
			'user_login'    => Helper::get_string_value( $request->get_param( 'username' ) ),
			'user_password' => Helper::get_string_value( $request->get_param( 'password' ) ),
			'remember'      => $request->get_param( 'remember' ) === 'true',
		];

		$user = wp_signon( $creds, false );

		if ( is_wp_error( $user ) ) {
			return new \WP_REST_Response(
				[
					'success' => false,
					'code'    => 'login_failed',
					'message' => $user->get_error_message(),
				],
				401
			);
		}

		// Set the current user and authentication cookie so the login session is established.
		wp_set_current_user( $user->ID );
		$secure = is_ssl();
		wp_set_auth_cookie(
			$user->ID,
			$creds['remember'],
			$secure
		);

		return new \WP_REST_Response(
			[
				'success' => true,
				'code'    => 'login_success',
				'message' => __( 'Login successful.', 'sureforms-pro' ),
				'user'    => [
					'id'    => $user->ID,
					'name'  => $user->display_name,
					'email' => $user->user_email,
				],
			],
			200
		);
	}

	/**
	 * Add login block preview.
	 *
	 * @param array<string,string> $preview_images Array of preview images.
	 * @since 1.8.0
	 * @return array<string,string>
	 */
	public function add_login_block_preview( $preview_images ) {
		return array_merge(
			$preview_images,
			[
				'login_preview'    => SRFM_PRO_URL . 'inc/business/user-registration/login/login-preview.svg',
				'password_preview' => SRFM_PRO_URL . 'inc/business/user-registration/password/password-preview.svg',
				'register_preview' => SRFM_PRO_URL . 'inc/business/user-registration/register/register-preview.svg',
			]
		);
	}

	/**
	 * Add the smart tags for user registration.
	 *
	 * @param array<string,string> $smart_tags Array of smart tags.
	 * @since 1.8.0
	 * @return array<string,string>
	 */
	public function add_user_registration_smart_tags( $smart_tags ) {
		return array_merge(
			$smart_tags,
			[
				'{login_url}'    => __( 'Login URL', 'sureforms-pro' ),
				'{register_url}' => __( 'Registration URL', 'sureforms-pro' ),
				'{forgot_url}'   => __( 'Forgot Password URL', 'sureforms-pro' ),
				'{logout_url}'   => __( 'Logout URL', 'sureforms-pro' ),
			]
		);
	}

	/**
	 * Parse the above custom smart tags for user registration.
	 *
	 * @param string               $parsed The parsed smart tag.
	 * @param array<string,string> $context The context of the smart tag.
	 * @since 1.8.0
	 * @return mixed
	 */
	public function parse_user_registration_smart_tags( $parsed, $context ) {
		unset( $parsed );
		$tag      = $context['tag'] ?? '';
		$settings = get_option( 'srfm_pro_user_registration_settings', [] );

		// Ensure $settings is an array.
		if ( ! is_array( $settings ) ) {
			$settings = [];
		}

		$custom_registration_page = ! empty( $settings['srfm_enable_custom_registration_page'] ) ? Helper::get_string_value( $settings['srfm_custom_registration_page'] ?? '' ) : '';
		$custom_login_page        = ! empty( $settings['srfm_enable_custom_login_page'] ) ? Helper::get_string_value( $settings['srfm_custom_login_page'] ?? '' ) : '';

		switch ( $tag ) {
			case '{login_url}':
				return ! empty( $custom_login_page ) ? $custom_login_page : wp_login_url();
			case '{register_url}':
				return ! empty( $custom_registration_page ) ? $custom_registration_page : wp_registration_url();
			case '{forgot_url}':
				return wp_lostpassword_url();
			case '{logout_url}':
				return wp_logout_url( Helper::get_string_value( get_permalink() ) ); // Redirect to current page after logging out.
			default:
				return null; // Let the main callback handle it if tags are not matched.
		}
	}

	/**
	 * Save User Registration Settings
	 *
	 * @param bool         $result Result of the save operation.
	 * @param array<mixed> $settings Setting options.
	 * @since 1.8.0
	 *
	 * @return bool
	 */
	public static function save_user_registration_settings( $result, $settings ) {
		// Unset the result variable to prevent phpinsights error for unused variable.
		unset( $result );
		$enabled_registration_page = isset( $settings['srfm_enable_custom_registration_page'] ) ?
			(bool) $settings['srfm_enable_custom_registration_page'] : false;
		$registration_page         = isset( $settings['srfm_custom_registration_page'] ) ?
			Helper::get_string_value( $settings['srfm_custom_registration_page'] ) : '';
		$enabled_login_page        = isset( $settings['srfm_enable_custom_login_page'] ) ?
			(bool) $settings['srfm_enable_custom_login_page'] : false;
		$login_page                = isset( $settings['srfm_custom_login_page'] ) ?
			Helper::get_string_value( $settings['srfm_custom_login_page'] ) : '';

		return update_option(
			'srfm_pro_user_registration_settings',
			[
				'srfm_enable_custom_registration_page' => $enabled_registration_page,
				'srfm_enable_custom_login_page'        => $enabled_login_page,
				'srfm_custom_registration_page'        => $registration_page,
				'srfm_custom_login_page'               => $login_page,
			]
		);
	}

	/**
	 * Get User Registration Settings
	 *
	 * @param array<mixed>  $global_settings Global settings array.
	 * @param array<string> $options_to_get Options to get.
	 * @since 1.8.0
	 *
	 * @return array<mixed> $global_settings Global settings array with user registration settings added.
	 */
	public function get_user_registration_settings( $global_settings, $options_to_get ) {
		if ( in_array( 'srfm_pro_user_registration_settings', $options_to_get, true ) && empty( $global_settings['srfm_pro_user_registration_settings'] ) ) {
			$global_settings['srfm_pro_user_registration_settings'] = [
				'srfm_enable_custom_registration_page' => false,
				'srfm_custom_registration_page'        => '',
				'srfm_enable_custom_login_page'        => false,
				'srfm_custom_login_page'               => '',
			];
		}

		return $global_settings;
	}

	/**
	 * Register additional post meta for user registration.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function add_user_registration_meta() {
		register_post_meta(
			'sureforms_form',
			'_srfm_user_registration_settings',
			[
				'type'              => 'string',
				'single'            => true,
				'show_in_rest'      => true,
				'auth_callback'     => static function() {
					return Helper::current_user_can();
				},
				'sanitize_callback' => [ $this, 'sanitize_user_registration_settings' ],
			]
		);
	}

	/**
	 * Sanitize user registration settings.
	 *
	 * Sanitizes each field in the user registration settings:
	 * - name (string): Sanitized with sanitize_text_field()
	 * - status (boolean): Cast to boolean with (bool)
	 * - registrationType (string): Sanitized with sanitize_text_field()
	 * - username (string): Sanitized with sanitize_text_field()
	 * - firstname (string): Sanitized with sanitize_text_field()
	 * - lastname (string): Sanitized with sanitize_text_field()
	 * - email (string): Sanitized with sanitize_text_field()
	 * - password (string): Sanitized with sanitize_text_field()
	 * - userRole (string): Sanitized with sanitize_text_field()
	 * - sendUserEmail (boolean): Cast to boolean with (bool)
	 * - emailNotificationType (string): Sanitized with sanitize_text_field()
	 * - customUserMeta (array): Each key and value sanitized with sanitize_text_field()
	 * - autoLogin (boolean): Cast to boolean with (bool)
	 *
	 * @param string $meta_value The meta value to sanitize.
	 * @since 1.8.0
	 * @return string Sanitized JSON string.
	 */
	public function sanitize_user_registration_settings( $meta_value ) {
		if ( empty( $meta_value ) ) {
			return '';
		}

		$decoded_value = json_decode( $meta_value, true );

		if ( ! is_array( $decoded_value ) ) {
			return '';
		}

		$sanitized_data = [];

		foreach ( $decoded_value as $registration ) {
			if ( ! is_array( $registration ) ) {
				continue;
			}

			$sanitized_item = [
				'name'                  => isset( $registration['name'] ) ? sanitize_text_field( $registration['name'] ) : '',
				'status'                => isset( $registration['status'] ) ? (bool) $registration['status'] : false,
				'registrationType'      => isset( $registration['registrationType'] ) ? sanitize_text_field( $registration['registrationType'] ) : 'create',
				'username'              => isset( $registration['username'] ) ? sanitize_text_field( $registration['username'] ) : '',
				'firstname'             => isset( $registration['firstname'] ) ? sanitize_text_field( $registration['firstname'] ) : '',
				'lastname'              => isset( $registration['lastname'] ) ? sanitize_text_field( $registration['lastname'] ) : '',
				'email'                 => isset( $registration['email'] ) ? sanitize_text_field( $registration['email'] ) : '',
				'password'              => isset( $registration['password'] ) ? sanitize_text_field( $registration['password'] ) : '',
				'userRole'              => isset( $registration['userRole'] ) ? sanitize_text_field( $registration['userRole'] ) : 'subscriber',
				'sendUserEmail'         => isset( $registration['sendUserEmail'] ) ? (bool) $registration['sendUserEmail'] : false,
				'emailNotificationType' => isset( $registration['emailNotificationType'] ) ? sanitize_text_field( $registration['emailNotificationType'] ) : 'email',
				'autoLogin'             => isset( $registration['autoLogin'] ) ? (bool) $registration['autoLogin'] : false,
			];

			// Handle custom user meta separately.
			if ( isset( $registration['customUserMeta'] ) && is_array( $registration['customUserMeta'] ) ) {
				$sanitized_item['customUserMeta'] = [];

				foreach ( $registration['customUserMeta'] as $meta ) {
					if ( is_array( $meta ) ) {
						$sanitized_item['customUserMeta'][] = [
							'key'   => isset( $meta['key'] ) ? sanitize_text_field( $meta['key'] ) : '',
							'value' => isset( $meta['value'] ) ? sanitize_text_field( $meta['value'] ) : '',
						];
					}
				}
			} else {
				$sanitized_item['customUserMeta'] = [
					[
						'key'   => '',
						'value' => '',
					],
				];
			}

			$sanitized_data[] = $sanitized_item;
		}

		$return = wp_json_encode( $sanitized_data );
		return $return ? $return : '';
	}

	/**
	 * Fetch roles from the site, and the roles that are allowed to register.
	 *
	 * @since 1.8.0
	 * @return \WP_REST_Response
	 */
	public function get_user_roles() {
		$user_roles      = wp_roles()->get_names();
		$formatted_roles = [];
		foreach ( $user_roles as $key => $label ) {
			// Skip the administrator role for the inital release.
			if ( 'administrator' === $key ) {
				continue;
			}

			$formatted_roles[] = [
				'label' => $label,
				'value' => $key,
			];
		}

		return new \WP_REST_Response(
			[
				'success' => true,
				'roles'   => $formatted_roles,
			],
			200
		);
	}

	/**
	 * API Endpoint callback to get all custom meta keys.
	 *
	 * @since 1.8.0
	 * @return \WP_REST_Response
	 */
	public static function get_custom_meta_keys() {
		// Get all custom meta keys.
		$meta_keys = self::get_all_custom_meta_keys();

		$formatted_keys = [];

		// Add existing meta keys.
		if ( is_array( $meta_keys ) ) {
			foreach ( $meta_keys as $key ) {
				$formatted_keys[] = [
					'label' => $key,
					'value' => $key,
				];
			}
		}

		// Add the "Add Custom Meta" option in the end.
		$formatted_keys[] = [
			'label' => __( 'Add Custom Meta', 'sureforms-pro' ),
			'value' => '__add_custom_meta__',
		];

		return new \WP_REST_Response(
			[
				'success' => true,
				'keys'    => $formatted_keys,
			],
			200
		);
	}

	/**
	 * Redirect users to custom login/registration pages if configured.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	public function redirect_to_custom_pages() {
		// Don't redirect if we're in admin area.
		if ( is_admin() ) {
			return;
		}

		// Don't redirect AJAX requests.
		if ( wp_doing_ajax() ) {
			return;
		}

		// Don't redirect REST API requests.
		if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) {
			return;
		}

		// Get user registration settings.
		$settings = get_option( 'srfm_pro_user_registration_settings', [] );

		// Ensure $settings is an array.
		if ( ! is_array( $settings ) ) {
			$settings = [];
		}

		// Get current action.
		$current_action = isset( $_REQUEST['action'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ) : 'login'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification not needed for this case.

		// Handle login page redirect.
		if ( 'login' === $current_action ) {
			$this->maybe_redirect_login_page( $settings );
		}

		// Handle registration page redirect.
		if ( 'register' === $current_action ) {
			$this->maybe_redirect_registration_page( $settings );
		}
	}

	/**
	 * Filter login URL to use custom login page if configured
	 *
	 * @param string $login_url The login URL.
	 * @param string $redirect The redirect URL.
	 * @param bool   $force_reauth Whether to force reauth.
	 * @since 1.8.0
	 * @return string
	 */
	public function filter_login_url( $login_url, $redirect = '', $force_reauth = false ) {
		// Get user registration settings.
		$settings = get_option( 'srfm_pro_user_registration_settings', [] );

		// Ensure $settings is an array.
		if ( ! is_array( $settings ) ) {
			return $login_url;
		}

		$custom_login_enabled = ! empty( $settings['srfm_enable_custom_login_page'] );
		$custom_login_page    = ! empty( $settings['srfm_custom_login_page'] ) ?
			Helper::get_string_value( $settings['srfm_custom_login_page'] ) : '';

		// Return original URL if custom login is not configured.
		if ( ! $custom_login_enabled || empty( $custom_login_page ) ) {
			return $login_url;
		}

		// Don't override if force reauth is required (admin login scenarios).
		if ( $force_reauth ) {
			return $login_url;
		}

		// Build custom login URL with redirect parameter if provided.
		$custom_url = $custom_login_page;
		if ( ! empty( $redirect ) ) {
			$custom_url = add_query_arg( 'redirect_to', rawurlencode( $redirect ), $custom_url );
		}

		return $custom_url;
	}

	/**
	 * Filter register URL to use custom registration page if configured
	 *
	 * @param string $register_url The registration URL.
	 * @since 1.8.0
	 * @return string
	 */
	public function filter_register_url( $register_url ) {
		// Get user registration settings.
		$settings = get_option( 'srfm_pro_user_registration_settings', [] );

		// Ensure $settings is an array.
		if ( ! is_array( $settings ) ) {
			return $register_url;
		}

		$custom_registration_enabled = ! empty( $settings['srfm_enable_custom_registration_page'] );
		$custom_registration_page    = ! empty( $settings['srfm_custom_registration_page'] ) ?
			Helper::get_string_value( $settings['srfm_custom_registration_page'] ) : '';

		// Return original URL if custom registration is not configured.
		if ( ! $custom_registration_enabled || empty( $custom_registration_page ) ) {
			return $register_url;
		}

		return $custom_registration_page;
	}

	/**
	 * Enqueue scripts.
	 *
	 * @param array<array<string, mixed>> $scripts Array of scripts to enqueue.
	 *
	 * @since 1.8.0
	 * @return void
	 */
	private function enqueue_scripts( $scripts ) {
		foreach ( $scripts as $script ) {
			$script_dep_path = SRFM_PRO_DIR . 'dist/package/business/' . $script['unique_file'] . '.asset.php';
			$script_dep_data = file_exists( $script_dep_path )
				? include $script_dep_path
				: [
					'dependencies' => [],
					'version'      => SRFM_PRO_VER,
				];

			// Ensure dependencies are arrays.
			$script_dep = array_merge(
				is_array( $script_dep_data['dependencies'] ) ? $script_dep_data['dependencies'] : [],
				is_array( $script['extra_dependencies'] ) ? $script['extra_dependencies'] : []
			);

			// Enqueue script.
			wp_enqueue_script(
				SRFM_PRO_SLUG . '-' . $script['unique_handle'], // Handle.
				SRFM_PRO_URL . 'dist/package/business/' . $script['unique_file'] . '.js',
				$script_dep, // Dependencies.
				$script_dep_data['version'], // Version.
				true // Enqueue the script in the footer.
			);

			if ( isset( $script['maybe_localize_data'] ) ) {
				wp_localize_script(
					SRFM_PRO_SLUG . '-' . $script['unique_handle'],
					'srfm_pro_user_registration',
					[
						'loginURL'    => wp_login_url(),
						'forgotURL'   => wp_lostpassword_url(),
						'registerURL' => wp_registration_url(),
						'ajaxURL'     => admin_url( 'admin-ajax.php' ),
						'nonce'       => wp_create_nonce( 'wp_rest' ),
					]
				);
			}

			// Register script translations.
			Pro_Helper::register_script_translations( SRFM_PRO_SLUG . '-' . $script['unique_handle'] );
		}
	}

	/**
	 * Get all the custom meta keys from the postmeta table.
	 *
	 * @since 1.8.0
	 * @return array<string>|mixed An array of custom meta keys.
	 */
	private static function get_all_custom_meta_keys() {
		global $wpdb;

		// Attempt to get from cache first.
		$cache_key     = 'srfm_custom_meta_keys';
		$cached_result = wp_cache_get( $cache_key, 'sureforms-pro' );

		if ( false !== $cached_result ) {
			return $cached_result;
		}

		// Query to get distinct meta keys that are not private/internal (those usually start with "_").
		// PHPCS: Ignore direct database query warning, as there is no built-in alternative.
    	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
		$meta_keys = $wpdb->get_col(
			$wpdb->prepare(
				"
			SELECT DISTINCT meta_key
			FROM {$wpdb->usermeta}
			WHERE meta_key NOT LIKE %s
			ORDER BY meta_key
			",
				$wpdb->esc_like( '_' ) . '%'
			)
		);

		// Store the result in cache for 1 hour.
		wp_cache_set( $cache_key, $meta_keys, 'sureforms-pro', HOUR_IN_SECONDS );

		// Return the array of keys.
		return $meta_keys;
	}

	/**
	 * Maybe redirect to custom login page
	 *
	 * @param array<string,mixed> $settings User registration settings.
	 * @since 1.8.0
	 * @return void
	 */
	private function maybe_redirect_login_page( $settings ) {
		$custom_login_enabled = ! empty( $settings['srfm_enable_custom_login_page'] );
		$custom_login_page    = ! empty( $settings['srfm_custom_login_page'] ) ?
			Helper::get_string_value( $settings['srfm_custom_login_page'] ) : '';

		// Only redirect if custom login is enabled and URL is set.
		if ( ! $custom_login_enabled || empty( $custom_login_page ) ) {
			return;
		}

		// Don't redirect for specific login actions that need the default page.
		$restricted_actions = [ 'logout', 'lostpassword', 'resetpass', 'rp', 'postpass' ];
		$current_action     = isset( $_REQUEST['action'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['action'] ) ) : 'login'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification not needed for this case.

		if ( in_array( $current_action, $restricted_actions, true ) ) {
			return;
		}

		// Preserve redirect_to parameter if present.
		$redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification not needed for this case.

		// Build custom login URL.
		$custom_url = $custom_login_page;
		if ( ! empty( $redirect_to ) ) {
			$custom_url = add_query_arg( 'redirect_to', $redirect_to, $custom_url );
		}

		// Perform the redirect.
		wp_safe_redirect( $custom_url );
		exit;
	}

	/**
	 * Maybe redirect to custom registration page
	 *
	 * @param array<string,mixed> $settings User registration settings.
	 * @since 1.8.0
	 * @return void
	 */
	private function maybe_redirect_registration_page( $settings ) {
		$custom_registration_enabled = ! empty( $settings['srfm_enable_custom_registration_page'] );
		$custom_registration_page    = ! empty( $settings['srfm_custom_registration_page'] ) ?
			Helper::get_string_value( $settings['srfm_custom_registration_page'] ) : '';

		// Only redirect if custom registration is enabled and URL is set.
		if ( ! $custom_registration_enabled || empty( $custom_registration_page ) ) {
			return;
		}

		// Perform the redirect.
		wp_safe_redirect( $custom_registration_page );
		exit;
	}
}
