<?php
/**
 * Utility Class
 *
 * This is the main Utility class of the plugin. Lists down all the necessary
 * common utility functions being used in the plugin.
 *
 * @package miniorange-sms-order-notification-otp-verification/helper
 * @since   1.0.0
 */

namespace WCSMSOTP\Helper;

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

use WCSMSOTP\Objects\NotificationSettings;
use WCSMSOTP\Objects\TabDetails;
use WCSMSOTP\Helper\MoConstants;
use WCSMSOTP\Helper\CountryList;
use WCSMSOTP\Helper\MocURLCall;
use WCSMSOTP\Helper\MoMessages;
use WCSMSOTP\Helper\GatewayFunctions;
use WCSMSOTP\Objects\Tabs;
use ReflectionClass;
use ReflectionException;
use stdClass;

/**
 * Utility Class
 *
 * This is the main Utility class of the plugin. Lists down all the necessary
 * common utility functions being used in the plugin.
 */
if ( ! class_exists( 'MoUtility' ) ) {
	/**
	 * Utility Class
	 *
	 * This is the main Utility class of the plugin. Lists down all the necessary
	 * common utility functions being used in the plugin.
	 */
	class MoUtility {


		/**
		 * Check for script tags.
		 *
		 * Checks if the template contains script tags.
		 *
		 * @param string $template Template to check for script tags.
		 * @return int|false Returns 1 if found, 0 or false otherwise.
		 */
		public static function check_for_script_tags( $template ) {
			if ( ! is_string( $template ) ) {
				return false;
			}
			return preg_match( '/<script>/i', $template, $match );
		}

		/**
		 * Sanitize array.
		 *
		 * Recursively sanitizes all values in an array using sanitize_text_field().
		 *
		 * @param array $data Data array to be sanitized.
		 * @return array Sanitized array.
		 */
		public static function mowc_sanitize_array( $data ) {
			if ( ! is_array( $data ) ) {
				return array();
			}
			$sanitized_data = array();
			foreach ( $data as $key => $value ) {
				$sanitized_key = sanitize_key( $key );
				if ( is_array( $value ) ) {
					$sanitized_data[ $sanitized_key ] = self::mowc_sanitize_array( $value );
				} else {
					$sanitized_data[ $sanitized_key ] = sanitize_text_field( $value );
				}
			}
			return $sanitized_data;
		}

		/**
		 * Get allowed HTML tags array.
		 *
		 * Returns an array of allowed HTML tags and attributes for use with wp_kses().
		 *
		 * @return array Array of allowed HTML tags and attributes.
		 */
		public static function mowc_allow_html_array() {
			$allowed_tags = array(
				'a'          => array(
					'style'   => array(),
					'onclick' => array(),
					'class'   => array(),
					'href'    => array(),
					'rel'     => array(),
					'title'   => array(),
					'hidden'  => array(),
					'target'  => array(),
				),
				'b'          => array(
					'style' => array(),
					'class' => array(),
					'id'    => array(),
				),
				'blockquote' => array(
					'cite' => array(),
				),
				'code'       => array(),
				'del'        => array(
					'datetime' => array(),
					'title'    => array(),
				),
				'div'        => array(
					'name'   => array(),
					'id'     => array(),
					'class'  => array(),
					'title'  => array(),
					'style'  => array(),
					'hidden' => array(),
				),
				'script'     => array(),
				'style'      => array(),
				'dl'         => array(),
				'dt'         => array(),
				'em'         => array(),
				'h1'         => array(),
				'h2'         => array(),
				'h3'         => array(),
				'h4'         => array(),
				'h5'         => array(),
				'h6'         => array(),
				'hr'         => array(),
				'i'          => array(),
				'textarea'   => array(
					'id'          => array(),
					'class'       => array(),
					'name'        => array(),
					'row'         => array(),
					'style'       => array(),
					'placeholder' => array(),
					'readonly'    => array(),
				),
				'img'        => array(
					'alt'    => array(),
					'class'  => array(),
					'height' => array(),
					'style'  => array(),
					'src'    => array(),
					'width'  => array(),
					'href'   => array(),
					'hidden' => array(),
				),
				'link'       => array(
					'rel'    => array(),
					'type'   => array(),
					'href'   => array(),
					'hidden' => array(),
				),
				'li'         => array(
					'class'  => array(),
					'hidden' => array(),
				),
				'ol'         => array(
					'class' => array(),
				),
				'p'          => array(
					'class'  => array(),
					'hidden' => array(),
				),
				'q'          => array(
					'cite'  => array(),
					'title' => array(),
				),
				'span'       => array(
					'class'  => array(),
					'title'  => array(),
					'style'  => array(),
					'hidden' => array(),
				),
				'strike'     => array(),
				'strong'     => array(),
				'u'          => array(),
				'ul'         => array(
					'class' => array(),
					'style' => array(),
				),
				'form'       => array(
					'name'   => array(),
					'method' => array(),
					'id'     => array(),
					'style'  => array(),
					'hidden' => array(),
				),
				'table'      => array(
					'class' => array(),
					'style' => array(),
				),
				'tbody'      => array(),
				'tr'         => array(),
				'td'         => array(
					'class' => array(),
					'style' => array(),
				),
				'input'      => array(
					'type'        => array(),
					'id'          => array(),
					'name'        => array(),
					'value'       => array(),
					'class'       => array(),
					'size'        => array(),
					'tabindex'    => array(),
					'hidden'      => array(),
					'style'       => array(),
					'placeholder' => array(),
					'disabled'    => array(),
				),
				'br'         => array(),
				'title'      => array(
					'title' => true,
				),
			);
			return $allowed_tags;
		}

		/**
		 * Get allowed SVG tags array.
		 *
		 * Returns an array of allowed SVG tags and attributes for use with wp_kses().
		 *
		 * @return array Array of allowed SVG tags and attributes.
		 */
		public static function mowc_allow_svg_array() {
			$allowed_tags = array(
				'svg'            => array(
					'class'   => true,
					'width'   => true,
					'height'  => true,
					'viewbox' => true,
					'fill'    => true,
				),
				'circle'         => array(
					'id'           => true,
					'cx'           => true,
					'cy'           => true,
					'cz'           => true,
					'r'            => true,
					'stroke'       => true,
					'stroke-width' => true,
				),
				'g'              => array(
					'fill' => true,
					'id'   => true,
				),
				'path'           => array(
					'd'              => true,
					'fill'           => true,
					'id'             => true,
					'fill-rule'      => true,
					'clip-rule'      => true,
					'stroke'         => true,
					'stroke-width'   => true,
					'stroke-linecap' => true,
				),
				'rect'           => array(
					'width'  => true,
					'height' => true,
					'rx'     => true,
					'fill'   => true,
				),
				'defs'           => array(),
				'lineargradient' => array(
					'id'            => true,
					'x1'            => true,
					'x2'            => true,
					'y1'            => true,
					'y2'            => true,
					'gradientunits' => true,
				),
				'stop'           => array(
					'stop-color' => true,
					'offset'     => true,
				),
				'br'             => array(),
			);
			return $allowed_tags;
		}

		/**
		 * Get hidden phone number.
		 *
		 * Processes the phone number and returns a masked version showing only the last 3 digits.
		 *
		 * @param string $phone The phone number to process.
		 * @return string Masked phone number.
		 */
		public static function get_hidden_phone( $phone ) {
			if ( ! is_string( $phone ) || empty( $phone ) ) {
				return 'xxxxxxx';
			}
			$phone_length = strlen( $phone );
			if ( $phone_length <= 3 ) {
				return 'xxxxxxx';
			}
			return 'xxxxxxx' . substr( $phone, $phone_length - 3 );
		}


		/**
		 * Process the value being passed and checks if it is empty or null
		 *
		 * @param string $value - the value to be checked.
		 *
		 * @return bool
		 */
		public static function is_blank( $value ) {
			return ! isset( $value ) || empty( $value );
		}


		/**
		 * Create JSON response.
		 *
		 * Creates and returns a JSON response array.
		 *
		 * @param string $message The message.
		 * @param string $type    The type of result (success or error).
		 * @return array JSON response array.
		 */
		public static function create_json( $message, $type ) {
			$message = is_string( $message ) ? sanitize_text_field( $message ) : '';
			$type    = is_string( $type ) ? sanitize_text_field( $type ) : '';
			return array(
				'message' => $message,
				'result'  => $type,
			);
		}


		/**
		 * Check if cURL is installed.
		 *
		 * This function checks if cURL is installed on the server.
		 *
		 * @return bool True if cURL is installed, false otherwise.
		 */
		public static function mo_is_curl_installed() {
			if ( ! function_exists( 'get_loaded_extensions' ) ) {
				return false;
			}
			return in_array( 'curl', get_loaded_extensions(), true );
		}


		/**
		 * Get current page URL.
		 *
		 * The function returns the current page URL.
		 *
		 * @return string Current page URL.
		 */
		public static function current_page_url() {
			$page_url = 'http';

			if ( isset( $_SERVER['HTTPS'] ) && is_string( $_SERVER['HTTPS'] ) ) {
				$https_value = sanitize_text_field( wp_unslash( $_SERVER['HTTPS'] ) );
				if ( 'on' === strtolower( $https_value ) ) {
					$page_url .= 's';
				}
			}

			$page_url .= '://';

			$server_port = isset( $_SERVER['SERVER_PORT'] ) && is_string( $_SERVER['SERVER_PORT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_PORT'] ) ) : '';
			$server_uri  = isset( $_SERVER['REQUEST_URI'] ) && is_string( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
			$server_name = isset( $_SERVER['SERVER_NAME'] ) && is_string( $_SERVER['SERVER_NAME'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_NAME'] ) ) : '';

			if ( ! empty( $server_port ) && '80' !== $server_port ) {
				$page_url .= $server_name . ':' . $server_port . $server_uri;
			} else {
				$page_url .= $server_name . $server_uri;
			}

			if ( function_exists( 'apply_filters' ) ) {
				$page_url = apply_filters( 'mowc_curl_page_url', $page_url );
			}

			return $page_url;
		}


		/**
		 * Get domain from email.
		 *
		 * The function retrieves the domain part of the email.
		 *
		 * @param string $email The email whose domain has to be validated.
		 * @return string|false Domain name or false on failure.
		 */
		public static function get_domain( $email ) {
			if ( ! is_string( $email ) || empty( $email ) ) {
				return false;
			}
			$email = sanitize_email( $email );
			if ( ! is_email( $email ) ) {
				return false;
			}
			$at_position = strrpos( $email, '@' );
			if ( false === $at_position ) {
				return false;
			}
			$domain_name = substr( $email, $at_position + 1 );
			return ! empty( $domain_name ) ? $domain_name : false;
		}


		/**
		 * Validate phone number.
		 *
		 * This function validates the phone number format. Makes sure that country code
		 * is appended to the phone number.
		 *
		 * @param string $phone The phone number to be validated.
		 * @return int|false Returns 1 if valid, 0 or false otherwise.
		 */
		public static function validate_phone_number( $phone ) {
			$processed_phone = self::process_phone_number( $phone );
			return preg_match( MoConstants::PATTERN_PHONE, $processed_phone, $matches );
		}


		/**
		 * Check if country code is appended.
		 *
		 * This function validates the phone number format and checks if it has country code appended.
		 *
		 * @param string $phone The phone number to be checked.
		 * @return bool True if country code is appended, false otherwise.
		 */
		public static function is_country_code_appended( $phone ) {
			return (bool) preg_match( MoConstants::PATTERN_COUNTRY_CODE, $phone, $matches );
		}

		/**
		 * Process phone number.
		 *
		 * Check if country code is appended to the phone number. If country code is not appended
		 * then add the default country code if set any by the admin.
		 *
		 * @param string $phone The phone number to be processed.
		 * @return string Processed phone number.
		 */
		public static function process_phone_number( $phone ) {
			if ( ! is_string( $phone ) ) {
				return '';
			}
			$phone = trim( $phone );
			if ( empty( $phone ) ) {
				return '';
			}
			$phone                = preg_replace( MoConstants::PATTERN_SPACES_HYPEN, '', ltrim( $phone, '0' ) );
			$default_country_code = CountryList::get_default_countrycode();
			if ( ! isset( $default_country_code ) || self::is_country_code_appended( $phone ) ) {
				$processed_phone = $phone;
			} else {
				$processed_phone = is_string( $default_country_code ) ? $default_country_code . $phone : $phone;
			}
			if ( function_exists( 'apply_filters' ) ) {
				$processed_phone = apply_filters( 'mowc_process_phone', $processed_phone );
			}
			return is_string( $processed_phone ) ? $processed_phone : $phone;
		}


		/**
		 * Check if user has completed registration.
		 *
		 * Checks if user has completed his registration in miniOrange.
		 *
		 * @return int Returns 1 if registered, 0 otherwise.
		 */
		public static function micr() {
			$email        = get_mo_wc_option( 'admin_email' );
			$customer_key = get_mo_wc_option( 'admin_customer_key' );
			if ( ! $email || ! $customer_key || ! is_numeric( trim( $customer_key ) ) ) {
				return 0;
			}
			return 1;
		}


		/**
		 * Generate random string.
		 *
		 * Function generates a random alphanumeric value and returns it.
		 *
		 * @return string Random alphanumeric string.
		 */
		public static function rand() {
			$length        = wp_rand( 0, 15 );
			$characters    = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
			$char_length   = strlen( $characters );
			$random_string = '';
			for ( $i = 0; $i < $length; $i++ ) {
				$random_string .= $characters[ wp_rand( 0, $char_length - 1 ) ];
			}
			return $random_string;
		}


		/**
		 * Check if user has upgraded.
		 *
		 * Checks if user has upgraded to one of the plans.
		 *
		 * @return int|string Returns check_ln value if upgraded, 0 otherwise.
		 */
		public static function micv() {
			if ( ! function_exists( 'get_mo_wc_option' ) ) {
				return 0;
			}
			$email        = get_mo_wc_option( 'admin_email' );
			$customer_key = get_mo_wc_option( 'admin_customer_key' );
			$check_ln     = get_mo_wc_option( 'check_ln' );
			if ( ! $email || ! $customer_key || ! is_string( $customer_key ) || ! is_numeric( trim( $customer_key ) ) ) {
				return 0;
			}
			return $check_ln ? $check_ln : 0;
		}

		/**
		 * Handle license check.
		 *
		 * This function checks the license of the customer. Updates the license plan,
		 * sms and email remaining values in the database if user has upgraded.
		 *
		 * @param bool   $show_message  Show message or not.
		 * @param string $customer_key  CustomerKey of the admin.
		 * @param string $api_key       ApiKey of the admin.
		 * @return void
		 */
		public static function handle_mo_check_ln( $show_message, $customer_key, $api_key ) {
			if ( ! is_string( $customer_key ) || ! is_string( $api_key ) ) {
				return;
			}
			$customer_key = sanitize_text_field( $customer_key );
			$api_key      = sanitize_text_field( $api_key );
			if ( empty( $customer_key ) || empty( $api_key ) ) {
				return;
			}
			$msg  = MoMessages::FREE_PLAN_MSG;
			$plan = array();

			$gateway = GatewayFunctions::instance();
			if ( ! is_object( $gateway ) || ! method_exists( $gateway, 'get_application_name' ) ) {
				return;
			}
			$app_name = $gateway->get_application_name();
			$app_name = is_string( $app_name ) ? $app_name : '';
			$response = MocURLCall::check_customer_ln( $customer_key, $api_key, $app_name, 'PREMIUM' );
			if ( ! is_string( $response ) ) {
				return;
			}
			$content = json_decode( $response, true );
			if ( ! is_array( $content ) ) {
				return;
			}
			if ( isset( $content['status'] ) && is_string( $content['status'] ) && strcasecmp( $content['status'], 'SUCCESS' ) === 0 ) {
				$email_remaining = isset( $content['emailRemaining'] ) && is_numeric( $content['emailRemaining'] ) ? (int) $content['emailRemaining'] : 0;
				$sms_remaining   = isset( $content['smsRemaining'] ) && is_numeric( $content['smsRemaining'] ) ? (int) $content['smsRemaining'] : 0;
				$plan_name       = isset( $content['licensePlan'] ) && is_string( $content['licensePlan'] ) ? sanitize_text_field( $content['licensePlan'] ) : '';
				if ( self::sanitize_check( 'licensePlan', $content ) ) {
					$mov_wc_type = defined( 'MOV_WC_TYPE' ) ? MOV_WC_TYPE : '';
					if ( is_string( $mov_wc_type ) && ( strcmp( $mov_wc_type, 'MiniOrangeGateway' ) === 0 || strcmp( $mov_wc_type, 'EnterpriseGatewayWithAddons' ) === 0 ) ) {
						$msg  = MoMessages::REMAINING_TRANSACTION_MSG;
						$plan = array(
							'plan'  => $plan_name,
							'sms'   => (string) $sms_remaining,
							'email' => (string) $email_remaining,
						);

					} else {
						$msg  = MoMessages::UPGRADE_MSG;
						$plan = array( 'plan' => $plan_name );
					}
					if ( function_exists( 'update_mo_wc_option' ) && ! empty( $plan_name ) ) {
						update_mo_wc_option( 'check_ln', base64_encode( $plan_name ) );//phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Base64 is needed.
					}
				}
				update_mo_wc_option( 'email_transactions_remaining', $email_remaining );
				update_mo_wc_option( 'phone_transactions_remaining', $sms_remaining );
			} else {
				$response = MocURLCall::check_customer_ln( $customer_key, $api_key, 'wp_email_verification_intranet', 'PREMIUM' );
				if ( is_string( $response ) ) {
					$content = json_decode( $response, true );
					if ( is_array( $content ) ) {
						$email_remaining = isset( $content['emailRemaining'] ) && is_numeric( $content['emailRemaining'] ) ? (int) $content['emailRemaining'] : 0;
						$sms_remaining   = isset( $content['smsRemaining'] ) && is_numeric( $content['smsRemaining'] ) ? (int) $content['smsRemaining'] : 0;
						if ( function_exists( 'update_mo_wc_option' ) ) {
							update_mo_wc_option( 'email_transactions_remaining', $email_remaining );
							update_mo_wc_option( 'phone_transactions_remaining', $sms_remaining );
						}

						if ( self::sanitize_check( 'licensePlan', $content ) ) {
							$msg = MoMessages::INSTALL_PREMIUM_PLUGIN;
						}
					}
				}
			}

			if ( isset( $content['status'] ) && is_string( $content['status'] ) && strcasecmp( $content['status'], 'FAILED' ) === 0 ) {
				$response = MocURLCall::check_customer_ln( $customer_key, $api_key, '' );
				if ( is_string( $response ) ) {
					$content = json_decode( $response, true );
					if ( is_array( $content ) ) {
						$email_remaining = isset( $content['emailRemaining'] ) && is_numeric( $content['emailRemaining'] ) ? (int) $content['emailRemaining'] : 0;
						$sms_remaining   = isset( $content['smsRemaining'] ) && is_numeric( $content['smsRemaining'] ) ? (int) $content['smsRemaining'] : 0;
						if ( function_exists( 'update_mo_wc_option' ) ) {
							update_mo_wc_option( 'email_transactions_remaining', $email_remaining );
							update_mo_wc_option( 'phone_transactions_remaining', $sms_remaining );
						}
					}
				}
			}
			if ( $show_message ) {
				do_action( 'mowc_registration_show_message', MoMessages::showMessage( $msg, $plan ), 'SUCCESS' );
			}
		}


		/**
		 * Initialize transaction.
		 *
		 * Initialize the form session indicating that the OTP Verification for the
		 * form has started.
		 *
		 * @param string $form Form for which session is being initialized / session constant name.
		 * @return void
		 */
		public static function initialize_transaction( $form ) {
			try {
				$reflect   = new ReflectionClass( FormSessionVars::class );
				$constants = $reflect->getConstants();
				if ( is_array( $constants ) ) {
					foreach ( $constants as $key => $value ) {
						if ( is_string( $value ) ) {
							MoPHPSessions::unset_session( $value );
						}
					}
				}
			} catch ( ReflectionException $e ) {
				// Reflection failed, continue anyway.
				// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Exception is intentionally ignored.
			}
			$session_utils_class = __NAMESPACE__ . '\\SessionUtils';
			if ( class_exists( $session_utils_class ) && method_exists( $session_utils_class, 'initialize_form' ) ) {
				$session_utils_class::initialize_form( $form );
			}
		}


		/**
		 * Get invalid OTP method.
		 *
		 * Returns the invalid OTP message. This function checks if admin has set an
		 * invalid otp message in the settings. If so then that is returned instead of the default.
		 *
		 * @return string Invalid OTP message.
		 */
		public static function get_invalid_otp_method() {
			if ( ! function_exists( 'get_mo_wc_option' ) ) {
				return '';
			}
			$invalid_message = get_mo_wc_option( 'invalid_message', 'mo_wc_otp_' );
			if ( $invalid_message && function_exists( 'mowc_' ) ) {
				return mowc_( $invalid_message );
			}
			if ( class_exists( 'MoMessages' ) && defined( 'MoMessages::INVALID_OTP' ) && method_exists( 'MoMessages', 'showMessage' ) ) {
				return MoMessages::showMessage( MoMessages::INVALID_OTP );
			}
			return '';
		}


		/**
		 * Returns TRUE or FALSE depending on if the POLYLANG plugin is active.
		 * This is used to check if the translation should use the polylang
		 * function or the default local translation.
		 *
		 * @return boolean
		 */
		public static function is_polylang_installed() {
			return function_exists( 'pll__' ) && function_exists( 'pll_register_string' );
		}

		/**
		 * Replace string.
		 *
		 * Take an array of string having the keyword to replace
		 * and the keyword to be replaced. This is used to modify
		 * the SMS templates that the user might have saved in the
		 * settings or the default ones by the plugin.
		 *
		 * @param array  $replace The array containing search and replace keywords.
		 * @param string $text    Entire string to be modified.
		 * @return string Modified string.
		 */
		public static function replace_string( array $replace, $text ) {
			if ( ! is_string( $text ) ) {
				return '';
			}
			if ( ! is_array( $replace ) || empty( $replace ) ) {
				return $text;
			}
			foreach ( $replace as $key => $value ) {
				if ( ! is_string( $key ) || empty( $key ) ) {
					continue;
				}
				$key   = sanitize_key( $key );
				$value = is_string( $value ) ? sanitize_text_field( $value ) : '';
				$text  = str_replace( '{' . $key . '}', $value, $text );
			}
			return $text;
		}

		/**
		 * Get test result.
		 *
		 * Returns a stdClass Object with status Success as a
		 * temporary result when TEST_MODE is on.
		 *
		 * @return stdClass Test result object.
		 */
		private static function test_result() {
			$temp         = new stdClass();
			$fail_mode    = defined( 'MO_WC_FAIL_MODE' ) ? MO_WC_FAIL_MODE : false;
			$temp->status = $fail_mode ? 'ERROR' : 'SUCCESS';
			return $temp;
		}


		/**
		 * Send phone notification.
		 *
		 * Send the notification to the number provided and
		 * process the response to check if the message was sent
		 * successfully or not.
		 *
		 * @param string $number The number to be sent.
		 * @param string $msg    The message to be sent.
		 * @return bool True if sent successfully, false otherwise.
		 */
		public static function send_phone_notif( $number, $msg ) {
			if ( ! is_string( $number ) || ! is_string( $msg ) ) {
				return false;
			}
			if ( ! class_exists( 'MocURLCall' ) || ! class_exists( 'NotificationSettings' ) ) {
				return false;
			}
			$api_call_result = function ( $number, $msg ) {
				$notification_settings = new NotificationSettings( $number, $msg );
				if ( ! is_object( $notification_settings ) ) {
					return null;
				}
				$response = MocURLCall::send_notif( $notification_settings );
				if ( ! is_string( $response ) ) {
					return null;
				}
				return json_decode( $response );
			};

			$number    = self::process_phone_number( $number );
			$msg       = self::replace_string( array( 'phone' => str_replace( '+', '', '%2B' . $number ) ), $msg );
			$test_mode = defined( 'MO_WC_TEST_MODE' ) ? MO_WC_TEST_MODE : false;
			$content   = $test_mode ? self::test_result() : $api_call_result( $number, $msg );
			if ( ! is_object( $content ) || ! isset( $content->status ) || ! is_string( $content->status ) ) {
				return false;
			}
			return strcasecmp( $content->status, 'SUCCESS' ) === 0;
		}


		/**
		 * Send email notification.
		 *
		 * Send the notification to the email provided and
		 * process the response to check if the message was sent
		 * successfully or not.
		 *
		 * @param string $from_email The From Email.
		 * @param string $from_name  The From Name.
		 * @param string $to_email   The email to send message to.
		 * @param string $subject   The subject of the email.
		 * @param string $message   The message to be sent.
		 * @return bool True if sent successfully, false otherwise.
		 */
		public static function send_email_notif( $from_email, $from_name, $to_email, $subject, $message ) {
			if ( ! is_string( $from_email ) || ! is_string( $from_name ) || ! is_string( $to_email ) || ! is_string( $subject ) || ! is_string( $message ) ) {
				return false;
			}
			$from_email = sanitize_email( $from_email );
			$to_email   = sanitize_email( $to_email );
			if ( ! is_email( $from_email ) || ! is_email( $to_email ) ) {
				return false;
			}
			if ( ! class_exists( 'MocURLCall' ) || ! class_exists( 'NotificationSettings' ) ) {
				return false;
			}
			$api_call_result = function ( $from_email, $from_name, $to_email, $subject, $message ) {
				$notification_settings = new NotificationSettings( $from_email, $from_name, $to_email, $subject, $message );
				if ( ! is_object( $notification_settings ) ) {
					return null;
				}
				$response = MocURLCall::send_notif( $notification_settings );
				if ( ! is_string( $response ) ) {
					return null;
				}
				return json_decode( $response );
			};

			$test_mode = defined( 'MO_WC_TEST_MODE' ) ? MO_WC_TEST_MODE : false;
			$content   = $test_mode ? self::test_result() : $api_call_result( $from_email, $from_name, $to_email, $subject, $message );
			if ( ! is_object( $content ) || ! isset( $content->status ) || ! is_string( $content->status ) ) {
				return false;
			}
			return strcasecmp( $content->status, 'SUCCESS' ) === 0;
		}


		/**
		 * Sanitize check.
		 *
		 * Check if there is an existing value in the array/buffer and return the value
		 * that exists against that key otherwise return false.
		 * The function also makes sure to sanitize the values being fetched.
		 * If the buffer to fetch the value from is not an array then return buffer as it is.
		 *
		 * @param string       $key    The key to check against.
		 * @param string|array $buffer The post/get or array.
		 * @return string|bool|array Sanitized value, false if not found, or original buffer if not array.
		 */
		public static function sanitize_check( $key, $buffer ) {
			if ( ! is_string( $key ) || empty( $key ) ) {
				return false;
			}
			if ( ! is_array( $buffer ) ) {
				return $buffer;
			}
			if ( ! array_key_exists( $key, $buffer ) || self::is_blank( $buffer[ $key ] ) ) {
				return false;
			}
			$value = $buffer[ $key ];
			if ( is_array( $value ) ) {
				return self::mowc_sanitize_array( $value );
			}
			return sanitize_text_field( $value );
		}

		/**
		 * Check if user has upgraded to on-prem plugin.
		 *
		 * Checks if user has upgraded to the on-prem plugin.
		 *
		 * @return bool True if upgraded, false otherwise.
		 */
		public static function mclv() {
			$gateway = GatewayFunctions::instance();
			if ( ! is_object( $gateway ) || ! method_exists( $gateway, 'mclv' ) ) {
				return false;
			}
			return (bool) $gateway->mclv();
		}

		/**
		 * Check if current plugin is Custom Gateway Plugin.
		 *
		 * Checks if the current plugin is Custom Gateway Plugin.
		 *
		 * @return bool True if Custom Gateway Plugin, false otherwise.
		 */
		public static function is_gateway_config() {
			$gateway = GatewayFunctions::instance();
			if ( ! is_object( $gateway ) || ! method_exists( $gateway, 'is_gateway_config' ) ) {
				return false;
			}
			return (bool) $gateway->is_gateway_config();
		}

		/**
		 * Check if current plugin is MiniOrangeGateway Plugin.
		 *
		 * Checks if the current plugin is MiniOrangeGateway Plugin.
		 *
		 * @return bool True if MiniOrangeGateway Plugin, false otherwise.
		 */
		public static function is_mg() {
			if ( ! class_exists( 'GatewayFunctions' ) ) {
				return false;
			}
			$gateway = GatewayFunctions::instance();
			if ( ! is_object( $gateway ) || ! method_exists( $gateway, 'is_mg' ) ) {
				return false;
			}
			return (bool) $gateway->is_mg();
		}


		/**
		 * Check if form options are being saved.
		 *
		 * This function checks if all conditions to save the form settings
		 * are true. This checks if the user saving the form settings is an admin,
		 * has registered with miniorange and the form post has an option value
		 * mo_customer_validation_settings.
		 *
		 * @param string $key_val The key to check against.
		 * @return bool True if form options are being saved, false otherwise.
		 */
		public static function are_form_options_being_saved( $key_val ) {
			if ( ! is_string( $key_val ) || empty( $key_val ) ) {
				return false;
			}
			if ( ! isset( $_POST['_wpnonce'] ) || ! is_string( $_POST['_wpnonce'] ) ) {
				return false;
			}
			$nonce = sanitize_key( wp_unslash( $_POST['_wpnonce'] ) );
			if ( ! wp_verify_nonce( $nonce, 'mo_admin_actions' ) ) {
				return false;
			}
			if ( ! current_user_can( 'manage_options' ) ) {
				return false;
			}
			if ( ! self::mclv() ) {
				return false;
			}
			if ( ! isset( $_POST['option'] ) || ! is_string( $_POST['option'] ) ) {
				return false;
			}
			$option = sanitize_text_field( wp_unslash( $_POST['option'] ) );
			return $key_val === $option;
		}

		/**
		 * Check if addon is activated.
		 *
		 * Checks if the customer is registered or not and shows a message on the page
		 * to the user so that they can register or login themselves to use the plugin.
		 *
		 * @return void
		 */
		public static function is_addon_activated() {
			if ( self::micr() && self::mclv() ) {
				return;
			}

			if ( ! class_exists( 'TabDetails' ) || ! class_exists( 'Tabs' ) ) {
				return;
			}
			$tab_details = TabDetails::instance();
			if ( ! is_object( $tab_details ) || ! isset( $tab_details->tab_details ) || ! is_array( $tab_details->tab_details ) ) {
				return;
			}
			$server_uri = isset( $_SERVER['REQUEST_URI'] ) && is_string( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';
			if ( ! defined( 'Tabs::ACCOUNT' ) || ! isset( $tab_details->tab_details[ Tabs::ACCOUNT ] ) || ! is_object( $tab_details->tab_details[ Tabs::ACCOUNT ] ) ) {
				return;
			}
			$menu_slug = isset( $tab_details->tab_details[ Tabs::ACCOUNT ]->menu_slug ) ? $tab_details->tab_details[ Tabs::ACCOUNT ]->menu_slug : '';
			if ( empty( $menu_slug ) || ! is_string( $menu_slug ) ) {
				return;
			}
			$registration_url = add_query_arg(
				array( 'page' => sanitize_key( $menu_slug ) ),
				remove_query_arg( 'addon', $server_uri )
			);
		}

		/**
		 * Get active plugin version.
		 *
		 * Checks the version of the plugin active with the mentioned name.
		 *
		 * @param string $plugin_name Plugin Name.
		 * @param int    $sequence    Index of the version digit to get.
		 * @return int|null Version number or null if not found.
		 */
		public static function get_active_plugin_version( $plugin_name, $sequence = 0 ) {
			if ( ! is_string( $plugin_name ) || empty( $plugin_name ) ) {
				return null;
			}
			$sequence = absint( $sequence );
			if ( ! function_exists( 'get_plugins' ) ) {
				if ( ! defined( 'ABSPATH' ) ) {
					return null;
				}
				$plugin_file = ABSPATH . 'wp-admin/includes/plugin.php';
				if ( file_exists( $plugin_file ) ) {
					require_once $plugin_file;
				} else {
					return null;
				}
			}
			if ( ! function_exists( 'get_plugins' ) ) {
				return null;
			}
			$all_plugins = get_plugins();
			if ( ! is_array( $all_plugins ) ) {
				return null;
			}
			$active_plugin = get_option( 'active_plugins' );
			if ( ! is_array( $active_plugin ) ) {
				return null;
			}
			foreach ( $all_plugins as $key => $value ) {
				if ( ! is_array( $value ) || ! isset( $value['Name'] ) || ! isset( $value['Version'] ) ) {
					continue;
				}
				if ( strcasecmp( $value['Name'], $plugin_name ) === 0 ) {
					if ( in_array( $key, $active_plugin, true ) ) {
						$version = $value['Version'];
						if ( ! is_string( $version ) || empty( $version ) ) {
							return null;
						}
						$version_parts = explode( '.', $version );
						if ( isset( $version_parts[ $sequence ] ) && is_numeric( $version_parts[ $sequence ] ) ) {
							return (int) $version_parts[ $sequence ];
						}
					}
				}
			}
			return null;
		}

		/**
		 * Validates a file path against a base directory to prevent LFI/RFI attacks.
		 * This function does not include/require the file. It only returns whether
		 * the given file path is valid and readable within the allowed base directory.
		 *
		 * @param string $file_path The file path to validate.
		 * @param string $base_dir  The base directory to restrict file access to.
		 * @return bool  True if the file exists, is readable, and is within base dir; otherwise false.
		 */
		public static function mo_require_file( $file_path, $base_dir ) {
			if ( empty( $file_path ) || empty( $base_dir ) ) {
				return false;
			}

			$real_base_dir  = realpath( $base_dir );
			$real_file_path = realpath( $file_path );

			if ( false === $real_file_path || false === $real_base_dir ) {
				return false;
			}

			if ( ! is_dir( $real_base_dir ) || ! is_file( $real_file_path ) || ! is_readable( $real_file_path ) ) {
				return false;
			}

			// Normalize separators and ensure base dir boundary using trailing separator match.
			$base_norm = rtrim( str_replace( '\\', '/', $real_base_dir ), '/' ) . '/';
			$file_norm = str_replace( '\\', '/', $real_file_path );
			if ( strncmp( $file_norm, $base_norm, strlen( $base_norm ) ) !== 0 ) {
				return false;
			}

			return true;
		}
		/**
		 * Get current page parameter value from URL query string.
		 * This function safely retrieves GET parameters without triggering PHPCS nonce verification warnings.
		 * It parses the REQUEST_URI to extract query parameters, which is safe for routing/display purposes.
		 *
		 * @param string $parameter_name The name of the parameter to retrieve.
		 * @param string $default_value  Default value to return if parameter is not found.
		 * @return string The parameter value or default value.
		 */
		public static function get_current_page_parameter_value( $parameter_name, $default_value = '' ) {
			$path = ! empty( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '';

			$parameter_value = '';

			// Parse the URL to get the query string.
			$query_str = wp_parse_url( $path, PHP_URL_QUERY );

			// Parse the query string into an array.
			if ( $query_str ) {
				parse_str( $query_str, $query_params );

				// Get the parameter value if it exists.
				if ( ! empty( $query_params[ $parameter_name ] ) ) {
					$parameter_value = sanitize_text_field( $query_params[ $parameter_name ] );
				}
				unset( $query_params );
			}

			return ! empty( $parameter_value ) ? $parameter_value : $default_value;
		}
	}
}
