<?php
/**
 * Service class for handling OTP (One Time Password) lifecycle.
 * Centralizes sending, verifying, and managing OTPs across the plugin.
 *
 * @package automation-web-platform
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}


/**
 * Class WAWP_OTP_Service
 */
class WAWP_OTP_Service {

	use WAWP_Security_Trait;
	use WAWP_Phone_Helper_Trait;
	use WAWP_Logging_Trait;

	/**
	 * Instance.
	 *
	 * @var WAWP_OTP_Service|null
	 */
	private static $instance = null;

	/**
	 * Constructor.
	 */
	private function __construct() {
		add_action( 'init', array( $this, 'handle_magic_link_request' ) );
	}

	/**
	 * Get instance.
	 *
	 * @return WAWP_OTP_Service
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Send an Authentication Challenge (OTP or Interactive List).
	 *
	 * @param string $recipient Phone number or Email.
	 * @param array  $args      Arguments (method, context, settings, user_id, extra_data).
	 * @return array [success, message, auth_id, transient_key]
	 */
	public function send_challenge( $recipient, $args ) {
		$method     = $args['method'] ?? 'whatsapp';
		$context    = $args['context'] ?? 'General';
		$settings   = $args['settings'] ?? array();
		$user_id    = $args['user_id'] ?? null;
		$extra_data = $args['extra_data'] ?? array();

		if ( WAWP()->block_manager && WAWP()->block_manager->is_recipient_blocked( $recipient ) ) {
			// Log in Activity Hub.
			if ( class_exists( 'WAWP_Unified_Log' ) ) {
				$details = array(
					'phone'        => is_email( $recipient ) ? '' : $recipient,
					'email'        => is_email( $recipient ) ? $recipient : '',
					'message'      => 'Verification Challenge Blocked',
					'message_type' => 'Blocked Challenge',
					'response'     => array(
						'status' => 'failed',
						'error'  => 'Recipient is blocked/blacklisted.',
					),
					'context'      => $context,
				);
				WAWP()->unified_log->add_activity_log(
					array(
						'user_id'    => $user_id,
						'phone'      => $details['phone'],
						'email'      => $details['email'],
						'event_type' => 'message',
						'gateway'    => $method,
						'log_status' => 'failed',
						'message'    => 'Blocked Challenge: Recipient is blacklisted.',
						'details'    => $details,
					)
				);
			}
			return array(
				'success' => false,
				'message' => esc_html__( 'Recipient is blocked/blacklisted.', 'automation-web-platform' ),
			);
		}

		$transient_key = 'wawp_otp_' . md5( $recipient . $context . uniqid() );

		$response = array(
			'success'       => false,
			'message'       => '',
			'auth_id'       => '',
			'transient_key' => $transient_key,
			'method'        => $method,
		);

		switch ( $method ) {
			case 'whatsapp':
				$wa_instance = $this->get_active_whatsapp_instance( $settings );
				$wa_auth     = WAWP_WhatsApp_Auth_Service::send_auth( $recipient, $settings, $wa_instance, $context, $user_id );
				if ( $wa_auth['success'] ) {
					$response['success']            = true;
					$response['message']            = $wa_auth['message'];
					$response['auth_id']            = $wa_auth['wawp_auth_id'];
					$response['whatsapp_auth_type'] = $wa_auth['whatsapp_auth_type'] ?? 'otp';
					$response['instance_id']        = $wa_instance ? $wa_instance->instance_id : '';
				} else {
					$response['message'] = $wa_auth['message'];
					$this->log_action(
						'failed',
						"WhatsApp auth failed for {$recipient}: {$wa_auth['message']}",
						array(
							'gateway' => 'whatsapp',
							'context' => $context,
						),
						'whatsapp',
						$user_id
					);
				}
				break;

			case 'email':
				$sent = $this->send_email_challenge( $recipient, $settings, $context, $transient_key, $user_id, $extra_data );
				if ( $sent ) {
					$response['success'] = true;
					$response['message'] = esc_html__( 'Verification link sent to your email.', 'automation-web-platform' );
				} else {
					$response['message'] = esc_html__( 'Failed to send email. Please check SMTP settings.', 'automation-web-platform' );
					$this->log_action(
						'failed',
						"Email verification link failed for {$recipient}",
						array(
							'gateway' => 'email',
							'context' => $context,
						),
						'email',
						$user_id
					);
				}
				break;

			case 'firebase':
				$response['success'] = true;
				$response['message'] = esc_html__( 'Please complete the SMS verification.', 'automation-web-platform' );
				$this->log_action(
					'info',
					"Firebase SMS challenge initiated for {$recipient}",
					array(
						'gateway' => 'firebase',
						'context' => $context,
					),
					'firebase',
					$user_id
				);
				break;
		}

		if ( $response['success'] ) {
			$session_data = array_merge(
				$extra_data,
				array(
					'otp'          => '',
					'recipient'    => $recipient,
					'method'       => $method,
					'auth_id'      => $response['auth_id'],
					'wawp_auth_id' => $response['auth_id'], // Compatibility.
					'instance_id'  => $response['instance_id'] ?? '',
					'context'      => $context,
					'user_id'      => $user_id,
					'created_at'   => time(),
				)
			);
			set_transient( $transient_key, $session_data, 15 * MINUTE_IN_SECONDS );

			if ( 'whatsapp' !== $method ) {
				$this->log_action(
					'success',
					ucfirst( $method ) . " verification link sent to {$recipient}",
					array(
						'gateway' => $method,
						'context' => $context,
					),
					$method,
					$user_id
				);
			}
		}

		return $response;
	}

	/**
	 * Verify Token with Firebase Identity Toolkit.
	 *
	 * @param string $id_token ID Token.
	 * @return array
	 */
	public function verify_firebase_token( $id_token ) {
		if ( empty( $id_token ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'ID Token is required.', 'automation-web-platform' ),
			);
		}

		if ( ! class_exists( 'WAWP_Firebase_Sender' ) && file_exists( WAWP_PLUGIN_DIR . 'includes/senders/class-wawp-firebase-sender.php' ) ) {
			include_once WAWP_PLUGIN_DIR . 'includes/senders/class-wawp-firebase-sender.php';
		}

		if ( ! class_exists( 'WAWP_Firebase_Sender' ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'Firebase Sender features require Wawp Pro.', 'automation-web-platform' ),
			);
		}

		if ( ! WAWP_Connector()->is_firebase_active() ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'Firebase API key is missing or not configured.', 'automation-web-platform' ),
			);
		}

		$config  = WAWP_Firebase_Sender::get_active_account();
		$api_key = $config['apiKey'] ?? $config['api_key'] ?? '';
		$url     = "https://identitytoolkit.googleapis.com/v1/accounts:lookup?key={$api_key}";

		$response = wp_remote_post(
			$url,
			array(
				'body'    => wp_json_encode( array( 'idToken' => $id_token ) ),
				'headers' => array( 'Content-Type' => 'application/json' ),
				'timeout' => 20,
			)
		);

		if ( is_wp_error( $response ) ) {
			$this->log_action( 'failed', 'Firebase API error: ' . $response->get_error_message(), array( 'gateway' => 'firebase' ), 'Firebase Token Verification' );

			return array(
				'success' => false,
				'message' => esc_html__( 'Failed to verify token with Firebase: ', 'automation-web-platform' ) . $response->get_error_message(),
			);
		}

		$code = wp_remote_retrieve_response_code( $response );
		$body = wp_remote_retrieve_body( $response );

		if ( 200 !== $code ) {
			$this->log_action( 'failed', 'Firebase verification failed (Code ' . $code . ')', array( 'gateway' => 'firebase' ), 'Firebase Token Verification' );

			return array(
				'success' => false,
				'message' => esc_html__( 'Failed to verify token with Firebase.', 'automation-web-platform' ),
			);
		}

		$res_body = json_decode( $body, true );
		if ( empty( $res_body['users'][0]['phoneNumber'] ) ) {
			$this->log_action( 'failed', 'Invalid or expired Firebase token.', array( 'gateway' => 'firebase' ), 'Firebase Token Verification' );

			return array(
				'success' => false,
				'message' => esc_html__( 'Invalid or expired token.', 'automation-web-platform' ),
			);
		}

		$phone = self::get_digits_only( $res_body['users'][0]['phoneNumber'] );

		return array(
			'success' => true,
			'phone'   => $phone,
		);
	}

	/**
	 * Verify an Authentication Challenge.
	 *
	 * @param string $entered_code  Entered code.
	 * @param string $transient_key Transient key.
	 * @param array  $settings      Settings.
	 * @return array
	 */
	public function verify_challenge( $entered_code, $transient_key, $settings = array() ) {
		$stored_data = get_transient( $transient_key );
		if ( ! $stored_data ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'OTP expired or not found. Please try again.', 'automation-web-platform' ),
			);
		}

		$attempts_key = $transient_key . '_wawp_attempts';
		$attempts     = (int) get_transient( $attempts_key );
		if ( $attempts >= 5 ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'Too many failed attempts. Please request a new code.', 'automation-web-platform' ),
			);
		}

		$success = false;
		$message = '';
		$auth_id = $stored_data['auth_id'] ?? '';
		$method  = $stored_data['method'] ?? 'whatsapp';

		if ( ! empty( $auth_id ) && 'whatsapp' === $method ) {
			$used_instance_id = $stored_data['instance_id'] ?? '';
			$wa_instance      = null;
			if ( ! empty( $used_instance_id ) ) {
				$wa_instance = WAWP()->instance_manager->get_instance_by_instance_id( $used_instance_id );
				if ( ! $wa_instance ) {
					$wa_instance = WAWP()->instance_manager->get_instance_by_id( (int) $used_instance_id );
				}
			}
			if ( ! $wa_instance ) {
				$wa_instance = $this->get_active_whatsapp_instance( $settings );
			}

			if ( $wa_instance ) {
				$res = WAWP_Senders::verify_otp( $wa_instance->instance_id, $wa_instance->access_token, $auth_id, $stored_data['recipient'], $entered_code );
				if ( 'success' === $res['status'] ) {
					$success = true;
				} else {
					$message = $res['message'] ?? esc_html__( 'Incorrect OTP.', 'automation-web-platform' );
				}
			} else {
				$message = esc_html__( 'WhatsApp instance not available.', 'automation-web-platform' );
			}
		} elseif ( (string) $entered_code === (string) $stored_data['otp'] ) {
			// Local verification (Email or manual OTP).
			$success = true;
		} else {
			// Local verification (Email or manual OTP).
			$message = esc_html__( 'Incorrect OTP.', 'automation-web-platform' );
		}

		if ( $success ) {
			delete_transient( $transient_key );
			delete_transient( $attempts_key );
			$this->log_action(
				'success',
				"OTP verified for {$stored_data['recipient']}",
				array(
					'method'  => $method,
					'context' => $stored_data['context'],
				),
				'otp'
			);

			return array(
				'success' => true,
				'message' => esc_html__( 'Verified successfully.', 'automation-web-platform' ),
				'data'    => $stored_data,
			);
		} else {
			set_transient( $attempts_key, $attempts + 1, 15 * MINUTE_IN_SECONDS );
			$this->log_action(
				'failed',
				"OTP verification failed for {$stored_data['recipient']}",
				array(
					'method'  => $method,
					'context' => $stored_data['context'],
					'reason'  => $message,
				),
				'otp'
			);

			return array(
				'success' => false,
				'message' => $message,
			);
		}
	}

	/**
	 * Resend OTP.
	 *
	 * @param array  $params   Parameters.
	 * @param string $context  Context.
	 * @param array  $settings Settings.
	 * @return array|WP_Error
	 */
	public function resend_otp( $params, $context = 'General', $settings = array() ) {
		$transient_key = sanitize_text_field( $params['otp_transient'] ?? '' );
		if ( empty( $transient_key ) ) {
			return new WP_Error( 'missing_session', esc_html__( 'Missing session key.', 'automation-web-platform' ) );
		}

		$stored_data = get_transient( $transient_key );
		if ( ! $stored_data ) {
			return new WP_Error( 'expired_session', esc_html__( 'OTP session has expired. Please try again.', 'automation-web-platform' ) );
		}

		$otp_method = sanitize_text_field( $params['force_otp_method'] ?? $stored_data['method'] ?? $settings['otp_method'] ?? 'whatsapp' );
		$recipient  = ( 'email' === $otp_method ? $stored_data['recipient'] : ( $stored_data['phone'] ?? $stored_data['recipient'] ) );

		$challenge = $this->send_challenge(
			$recipient,
			array(
				'method'     => $otp_method,
				'context'    => $context,
				'settings'   => $settings,
				'extra_data' => $stored_data,
				'user_id'    => $stored_data['user_id'] ?? null,
			)
		);

		if ( ! $challenge['success'] ) {
			return new WP_Error( 'challenge_failed', $challenge['message'] );
		}

		delete_transient( $transient_key );

		return array(
			'success'            => true,
			'message'            => esc_html__( 'OTP resent successfully!', 'automation-web-platform' ),
			'otp_transient'      => $challenge['transient_key'],
			'phone'              => $stored_data['phone'] ?? $stored_data['recipient'],
			'email'              => $stored_data['email'] ?? $stored_data['recipient'],
			'otp_method'         => $otp_method,
			'whatsapp_auth_type' => $challenge['whatsapp_auth_type'] ?? 'otp',
		);
	}

	/**
	 * Handle AJAX resend.
	 *
	 * @param array $args Arguments.
	 */
	public function handle_ajax_resend( $args ) {
		$nonce_field  = $args['nonce_field'] ?? 'security';
		$nonce_action = $args['nonce_action'] ?? 'otp-ajax-nonce';
		$this->check_frontend_nonce( $nonce_action, $nonce_field );

		$params = filter_input_array( INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS );
		$res    = $this->resend_otp( $params ? $params : array(), $args['context'] ?? 'General', $args['settings'] ?? array() );
		if ( is_wp_error( $res ) ) {
			$this->send_error( $res->get_error_message() );
		}
		$this->send_success( '', $res );
	}

	/**
	 * Verify OTP.
	 *
	 * @param array $params   Parameters.
	 * @param array $settings Settings.
	 * @return array|WP_Error
	 */
	public function verify_otp( $params, $settings = array() ) {
		$entered_otp = sanitize_text_field( $params['otp'] ?? '' );
		$auth_id     = sanitize_text_field( $params['auth_id'] ?? '' );
		$phone       = sanitize_text_field( $params['phone'] ?? $params['phone_number'] ?? '' );
		$method      = sanitize_text_field( $params['method'] ?? 'whatsapp' );

		if ( empty( $entered_otp ) ) {
			return new WP_Error( 'missing_data', esc_html__( 'OTP is required.', 'automation-web-platform' ) );
		}

		// For WhatsApp/Wawp OTP - verify directly via API (no transient needed).
		if ( 'whatsapp' === $method && ! empty( $auth_id ) && ! empty( $phone ) ) {
			$wa_instance = $this->get_active_whatsapp_instance( $settings );
			if ( ! $wa_instance ) {
				return new WP_Error( 'verification_failed', esc_html__( 'WhatsApp instance not available.', 'automation-web-platform' ) );
			}

			$res = WAWP_Senders::verify_otp( $wa_instance->instance_id, $wa_instance->access_token, $auth_id, $phone, $entered_otp );
			if ( 'success' === $res['status'] ) {
				return array(
					'success' => true,
					'message' => esc_html__( 'Verified successfully.', 'automation-web-platform' ),
					'data'    => array(
						'method'    => 'whatsapp',
						'auth_id'   => $auth_id,
						'recipient' => $phone,
					),
				);
			} else {
				return new WP_Error( 'verification_failed', $res['message'] ?? esc_html__( 'Incorrect OTP.', 'automation-web-platform' ) );
			}
		}

		// For email OTP - use transient-based verification.
		$transient_key = sanitize_text_field( $params['otp_transient'] ?? '' );
		if ( empty( $transient_key ) ) {
			return new WP_Error( 'missing_data', esc_html__( 'OTP and Session Key are required.', 'automation-web-platform' ) );
		}

		$verification = $this->verify_challenge( $entered_otp, $transient_key, $settings );
		if ( ! $verification['success'] ) {
			return new WP_Error( 'verification_failed', $verification['message'] );
		}

		return $verification;
	}

	/**
	 * Verify OTP return.
	 *
	 * @param array    $params   Parameters.
	 * @param array    $settings Settings.
	 * @param callable $callback Callback.
	 * @return mixed
	 */
	public function verify_otp_return( $params, $settings = array(), $callback = null ) {
		if ( is_callable( $settings ) && null === $callback ) {
			$callback = $settings;
			$settings = array();
		}
		$res = $this->verify_otp( $params, $settings );
		if ( is_wp_error( $res ) ) {
			return $res;
		}

		return call_user_func( $callback, $res['data'], $params['otp_transient'] ?? '' );
	}

	/**
	 * Handle AJAX verify.
	 *
	 * @param array    $args     Arguments.
	 * @param callable $callback Callback.
	 * @return mixed
	 */
	public function handle_ajax_verify( $args, $callback ) {
		$nonce_field  = $args['nonce_field'] ?? 'security';
		$nonce_action = $args['nonce_action'] ?? 'otp-ajax-nonce';
		$this->check_frontend_nonce( $nonce_action, $nonce_field );

		$params = filter_input_array( INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS );
		$res    = $this->verify_otp( $params ? $params : array(), $args['settings'] ?? array() );
		if ( is_wp_error( $res ) ) {
			$this->send_error( $res->get_error_message() );
		}

		$otp_transient = filter_input( INPUT_POST, 'otp_transient', FILTER_SANITIZE_SPECIAL_CHARS );
		return call_user_func( $callback, $res['data'], $otp_transient ? $otp_transient : '' );
	}

	/**
	 * Handle signup submit return.
	 *
	 * @param array    $params   Parameters.
	 * @param callable $callback Callback.
	 * @return mixed
	 */
	public function handle_signup_submit_return( $params, $callback ) {
		// Normalize incoming parameters.
		if ( isset( $params['wawp_phone'] ) ) {
			$params['phone'] = $params['wawp_phone'];
		}
		if ( isset( $params['wawp_email'] ) ) {
			$params['email'] = $params['wawp_email'];
		}
		if ( isset( $params['wawp_first_name'] ) ) {
			$params['first_name'] = $params['wawp_first_name'];
		}
		if ( isset( $params['wawp_last_name'] ) ) {
			$params['last_name'] = $params['wawp_last_name'];
		}
		if ( isset( $params['wawp_password'] ) ) {
			$params['password'] = $params['wawp_password'];
		}
		if ( isset( $params['wawp_custom_fields'] ) ) {
			$params['custom_fields_data'] = $params['wawp_custom_fields'];
		}
		if ( empty( $params['username'] ) ) {
			if ( ! empty( $params['email'] ) ) {
				$params['username'] = $params['email'];
			} elseif ( ! empty( $params['phone'] ) ) {
				$params['username'] = $params['phone'];
			}
		}

		$otp_code       = sanitize_text_field( $params['otp'] ?? '' );
		$firebase_token = sanitize_text_field( $params['idToken'] ?? '' );
		$transient_key  = sanitize_text_field( $params['otp_transient'] ?? '' );

		if ( ! empty( $otp_code ) || ! empty( $firebase_token ) ) {
			if ( ! empty( $firebase_token ) ) {
				$verification = $this->verify_firebase( $params );
			} else {
				$verification = $this->verify_otp( $params );
			}

			if ( is_wp_error( $verification ) ) {
				return array(
					'success' => false,
					'message' => $verification->get_error_message(),
				);
			}

			return call_user_func( $callback, $verification['data'], $transient_key );
		}

		$phone = self::get_digits_only( $params['phone'] ?? '' );
		$email = sanitize_email( $params['email'] ?? '' );

		if ( empty( $phone ) && empty( $email ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'Phone or Email is required.', 'automation-web-platform' ),
			);
		}

		// Check if OTP is disabled.
		$enable_otp = true;
		if ( isset( $params['settings']['wawp_signup_enable_otp'] ) ) {
			$enable_otp = ! empty( $params['settings']['wawp_signup_enable_otp'] );
		} else {
			$enable_otp = (int) get_option( 'wawp_signup_enable_otp', 1 ) === 1;
		}

		if ( ! $enable_otp ) {
			$register_res = call_user_func( $callback, $params, '' );
			if ( ! empty( $register_res['success'] ) ) {
				$redirect_url = $params['settings']['wawp_signup_redirect_url'] ?? '';
				if ( empty( $redirect_url ) ) {
					$redirect_url = home_url();
				}
				return array(
					'success' => true,
					'message' => esc_html__( 'Registration completed successfully.', 'automation-web-platform' ),
					'data'    => array(
						'redirect_url' => $redirect_url,
					),
				);
			}
			return $register_res;
		}

		$otp_method = sanitize_text_field( $params['otp_method'] ?? $params['settings']['wawp_signup_otp_method'] ?? 'whatsapp' );
		if ( 'wa' === $otp_method ) {
			$otp_method = 'whatsapp';
		}
		$recipient = ( 'email' === $otp_method ) ? $email : $phone;

		if ( empty( $recipient ) ) {
			return array(
				'success' => false,
				/* translators: %s: OTP method name (e.g. WhatsApp, Email) */
				'message' => sprintf( esc_html__( '%s is required for this method.', 'automation-web-platform' ), ucfirst( $otp_method ) ),
			);
		}

		$challenge = $this->send_challenge(
			$recipient,
			array(
				'method'     => $otp_method,
				'context'    => 'Signup',
				'settings'   => $params['settings'] ?? array(),
				'extra_data' => $params,
			)
		);

		if ( ! $challenge['success'] ) {
			return array(
				'success' => false,
				'message' => $challenge['message'],
			);
		}

		return array(
			'success' => true,
			'message' => $challenge['message'],
			'data'    => array(
				'step'               => 'otp_verify',
				'otp_transient'      => $challenge['transient_key'],
				'whatsapp_auth_type' => $challenge['whatsapp_auth_type'] ?? 'otp',
				'method'             => $otp_method,
				'is_magic_link'      => ( 'email' === $otp_method ) ? true : false,
				'email'              => $email,
				'phone'              => $phone,
			),
		);
	}

	/**
	 * Verify Firebase.
	 *
	 * @param array $params Parameters.
	 * @return array|WP_Error
	 */
	public function verify_firebase( $params ) {
		$id_token = sanitize_text_field( $params['idToken'] ?? '' );
		if ( empty( $id_token ) ) {
			return new WP_Error( 'missing_token', esc_html__( 'ID Token is required.', 'automation-web-platform' ) );
		}

		$verification = $this->verify_firebase_token( $id_token );
		if ( ! $verification['success'] ) {
			return new WP_Error( 'verification_failed', $verification['message'] );
		}

		$transient_key = sanitize_text_field( $params['otp_transient'] ?? '' );
		$stored_data   = $transient_key ? get_transient( $transient_key ) : array();

		return array(
			'success'       => true,
			'data'          => array_merge(
				$stored_data ? $stored_data : array(),
				array(
					'phone'     => $verification['phone'],
					'recipient' => $verification['phone'],
					'method'    => 'firebase',
				)
			),
			'transient_key' => $transient_key,
		);
	}

	/**
	 * Handle AJAX verify Firebase.
	 *
	 * @param array    $args     Arguments.
	 * @param callable $callback Callback.
	 * @return mixed
	 */
	public function handle_ajax_verify_firebase( $args, $callback ) {
		$nonce_field  = $args['nonce_field'] ?? 'security';
		$nonce_action = $args['nonce_action'] ?? 'otp-ajax-nonce';
		$this->check_frontend_nonce( $nonce_action, $nonce_field );

		$params = filter_input_array( INPUT_POST, FILTER_SANITIZE_SPECIAL_CHARS );
		$res    = $this->verify_firebase( $params ? $params : array() );
		if ( is_wp_error( $res ) ) {
			$this->send_error( $res->get_error_message() );
		}

		return call_user_func( $callback, $res['data'], $res['transient_key'] );
	}

	/**
	 * Verify Firebase return.
	 *
	 * @param array    $params   Parameters.
	 * @param callable $callback Callback.
	 * @return mixed
	 */
	public function verify_firebase_return( $params, $callback ) {
		$res = $this->verify_firebase( $params );
		if ( is_wp_error( $res ) ) {
			return $res;
		}

		return call_user_func( $callback, $res['data'], $res['transient_key'] );
	}

	/**
	 * Check challenge status (Polling).
	 *
	 * @param string $transient_key Transient key.
	 * @param array  $settings      Settings.
	 * @return array
	 */
	public function check_challenge_status( $transient_key, $settings = array() ) {
		$stored_data = get_transient( $transient_key );
		if ( ! $stored_data ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'Session expired or invalid.', 'automation-web-platform' ),
			);
		}

		$status_val = $stored_data['status'] ?? '';
		if ( 'verified' === $status_val || 'consumed' === $status_val ) {
			// Keep the transient briefly (5s) so concurrent poll requests
			// from the same client still receive a success response.
			if ( 'verified' === $status_val && is_array( $stored_data ) ) {
				$stored_data['status'] = 'consumed';
				set_transient( $transient_key, $stored_data, 5 );
			} else {
				delete_transient( $transient_key );
			}

			return array(
				'success'  => true,
				'verified' => true,
				'data'     => $stored_data,
			);
		}

		if ( ! empty( $stored_data['wawp_auth_id'] ) ) {
			$used_instance_id = $stored_data['instance_id'] ?? '';
			$instance         = null;
			if ( ! empty( $used_instance_id ) ) {
				$instance = WAWP()->instance_manager->get_instance_by_instance_id( $used_instance_id );
				if ( ! $instance ) {
					$instance = WAWP()->instance_manager->get_instance_by_id( (int) $used_instance_id );
				}
			}
			if ( ! $instance ) {
				$instance = WAWP()->instance_manager->get_first_online_instance();
				if ( ! empty( $settings['selected_instance'] ) ) {
					$instance = WAWP()->instance_manager->get_instance_by_id( $settings['selected_instance'] );
				}
			}

			if ( ! $instance ) {
				return array(
					'success' => false,
					'message' => esc_html__( 'No active WhatsApp instance found.', 'automation-web-platform' ),
				);
			}

			$res    = WAWP_Senders::check_auth_status( $instance->instance_id, $instance->access_token, $stored_data['wawp_auth_id'] );
			$status = $res['full_response']['status'] ?? ( ( 'error' === $res['status'] ) ? 'error' : 'pending' );

			if ( 'success' === $res['status'] && ! empty( $res['full_response']['verified'] ) ) {
				delete_transient( $transient_key );
				$this->log_action(
					'success',
					'WhatsApp auth verified for ' . ( $stored_data['recipient'] ?? 'unknown' ),
					array(
						'context'  => $stored_data['context'] ?? 'OTP',
						'response' => $res,
					),
					'whatsapp'
				);

				return array(
					'success'  => true,
					'verified' => true,
					'data'     => $stored_data,
				);
			}

			if ( in_array( $status, array( 'rejected', 'expired', 'error' ), true ) ) {
				$this->log_action(
					'failed',
					"WhatsApp auth {$status} for " . ( $stored_data['recipient'] ?? 'unknown' ),
					array(
						'context'  => $stored_data['context'] ?? 'OTP',
						'status'   => $status,
						'response' => $res,
					),
					'whatsapp'
				);
			}

			return array(
				'success'  => true,
				'verified' => false,
				'status'   => $status,
			);
		}

		return array(
			'success'  => true,
			'verified' => false,
			'status'   => 'pending',
		);
	}

	/**
	 * Check challenge status return.
	 *
	 * @param array    $params   Parameters.
	 * @param array    $settings Settings.
	 * @param callable $callback Callback.
	 * @return mixed
	 */
	public function check_challenge_status_return( $params, $settings, $callback ) {
		$transient_key = sanitize_text_field( $params['otp_transient'] ?? '' );
		if ( empty( $transient_key ) ) {
			return new WP_Error( 'missing_session', esc_html__( 'Missing session key.', 'automation-web-platform' ) );
		}

		$status = $this->check_challenge_status( $transient_key, $settings );
		if ( ! $status['success'] ) {
			return new WP_Error( 'status_failed', $status['message'] );
		}

		if ( $status['verified'] ) {
			return call_user_func( $callback, $status['data'], $transient_key );
		}

		return array(
			'verified' => false,
			'status'   => $status['status'] ?? 'pending',
		);
	}

	/**
	 * Backward compatibility helper for check status return.
	 *
	 * @param array    $params   Parameters.
	 * @param callable $callback Callback.
	 * @return mixed
	 */
	public function check_status_return( $params, $callback ) {
		$settings = $params['settings'] ?? array();
		return $this->check_challenge_status_return( $params, $settings, $callback );
	}

	/**
	 * Backward compatibility helper for resend return.
	 *
	 * @param array $params Parameters.
	 * @return mixed
	 */
	public function resend_return( $params ) {
		$context  = $params['context'] ?? 'General';
		$settings = $params['settings'] ?? array();
		return $this->resend_otp( $params, $context, $settings );
	}

	/**
	 * AJAX check auth status.
	 *
	 * @param array    $args     Arguments.
	 * @param callable $callback Callback.
	 * @return mixed
	 */
	public function ajax_check_auth_status( $args, $callback ) {
		$nonce_field  = $args['nonce_field'] ?? 'security';
		$nonce_action = $args['nonce_action'] ?? 'otp-ajax-nonce';
		$this->check_frontend_nonce( $nonce_action, $nonce_field );

		$transient_key = filter_input( INPUT_POST, 'otp_transient', FILTER_SANITIZE_SPECIAL_CHARS );
		if ( ! $transient_key ) {
			$transient_key = $args['transient_key'] ?? '';
		}
		if ( empty( $transient_key ) ) {
			$this->send_error( esc_html__( 'Missing session key.', 'automation-web-platform' ) );
		}

		$status = $this->check_challenge_status( $transient_key, $args['settings'] ?? array() );
		if ( ! $status['success'] ) {
			$this->send_error( $status['message'] );
		}

		if ( $status['verified'] ) {
			return call_user_func( $callback, $status['data'], $transient_key );
		}

		$this->send_success(
			'',
			array(
				'verified' => false,
				'status'   => $status['status'] ?? 'pending',
			)
		);
	}

	/**
	 * Handle magic link request.
	 */
	public function handle_magic_link_request() {
		$query_string = isset( $_SERVER['QUERY_STRING'] ) ? sanitize_text_field( wp_unslash( $_SERVER['QUERY_STRING'] ) ) : '';
		wp_parse_str( $query_string, $query_args );

		$token_input  = $query_args['wawp_magic_token'] ?? '';
		$token        = $token_input ? sanitize_text_field( $token_input ) : '';
		$action_input = $query_args['wawp_action'] ?? '';
		$action_type  = $action_input ? sanitize_text_field( $action_input ) : 'login';

		if ( ! $token ) {
			return;
		}

		$transient_key = $token;
		if ( 0 !== strpos( $token, 'wawp_otp_' ) ) {
			if ( 'login' === $action_type ) {
				$transient_key = 'wawp_magic_token_' . $token;
			} elseif ( 'signup' === $action_type ) {
				$transient_key = 'wawp_signup_otp_' . $token;
			} elseif ( 'checkout' === $action_type ) {
				$transient_key = 'wawp_checkout_auth_' . $token;
			}
		}

		$data = get_transient( $transient_key );
		if ( ! $data ) {
			wp_die( esc_html__( 'Invalid or expired verification link.', 'automation-web-platform' ) );
		}

		if ( is_array( $data ) ) {
			$data['status'] = 'verified';
			set_transient( $transient_key, $data, 15 * MINUTE_IN_SECONDS );
		} else {
			set_transient(
				$transient_key . '_status',
				array(
					'status'  => 'verified',
					'user_id' => $data,
				),
				5 * MINUTE_IN_SECONDS
			);
		}

		if ( 'login' !== $action_type ) {
			$title = esc_html__( 'Verified Successfully!', 'automation-web-platform' );
			$desc  = esc_html__( 'Verification complete. You can now close this tab and return to your original screen.', 'automation-web-platform' );
			wp_die( '<h2>✅ ' . esc_html( $title ) . '</h2><p> ' . esc_html( $desc ) . '</p>', esc_html( $title ), array( 'response' => 200 ) );
		}
	}

	/**
	 * Send email challenge.
	 *
	 * @param string   $email         Email.
	 * @param array    $settings      Settings.
	 * @param string   $context       Context.
	 * @param string   $transient_key Transient key.
	 * @param int|null $user_id       User ID.
	 * @param array    $extra_data    Extra data.
	 * @return bool
	 */
	private function send_email_challenge( $email, $settings, $context, $transient_key = '', $user_id = null, $extra_data = array() ) {
		/* translators: %s: Site Name */
		$subject          = $settings['otp_subject_email'] ?? sprintf( esc_html__( 'Verification Link from %s', 'automation-web-platform' ), get_bloginfo( 'name' ) );
		$message          = $settings['otp_message_email'] ?? esc_html__( 'Please click the link below to verify your identity:', 'automation-web-platform' );
		$is_html_template = false;

		$template_id = ! empty( $settings['otp_email_template_id'] ) ? intval( $settings['otp_email_template_id'] ) : 0;
		if ( $template_id > 0 && class_exists( 'WAWP_Email_Templates' ) ) {
			$template_response = WAWP_Email_Templates::get_instance()->handle_get_template( $template_id );
			if ( ! empty( $template_response['success'] ) && ! empty( $template_response['data']->content_html ) ) {
				$message          = $template_response['data']->content_html;
				$is_html_template = true;
			}
		}

		// Clean up default template if it has OTP placeholder.
		if ( ! $is_html_template && ( strpos( $message, 'Your OTP code is: {{otp}}' ) !== false || strpos( $message, '{{otp}}' ) !== false ) ) {
			$message = esc_html__( 'Please click the link below to verify your identity:', 'automation-web-platform' );
		}

		if ( ! empty( $transient_key ) ) {
			$magic_link = network_site_url( '/?wawp_magic_token=' . rawurlencode( $transient_key ) . '&wawp_action=' . rawurlencode( strtolower( $context ) ) );
			if ( strpos( $message, '{{magic_link}}' ) !== false ) {
				$message = str_replace( '{{magic_link}}', $magic_link, $message );
			} elseif ( strpos( $message, '{{wawp_login_link}}' ) !== false ) {
				$message = str_replace( '{{wawp_login_link}}', $magic_link, $message );
			} elseif ( $is_html_template ) {
				// Fallback for template if they forgot the tag.
				$message = str_replace( '</body>', '<p><a href="' . esc_url( $magic_link ) . '">' . esc_html__( 'Click here to verify automatically', 'automation-web-platform' ) . '</a></p></body>', $message );
			} else {
				$message .= '<p><a href="' . esc_url( $magic_link ) . '">' . esc_html__( 'Click here to verify automatically', 'automation-web-platform' ) . '</a></p>';
			}
		}

		if ( $is_html_template && class_exists( 'WAWP_Email_Inliner' ) ) {
			$message = WAWP_Email_Inliner::inline( $message );
		}

		$replacements = array(
			'site_name' => get_bloginfo( 'name' ),
			'shop_name' => get_bloginfo( 'name' ),
			'sitename'  => get_bloginfo( 'name' ),
			'site_link' => site_url(),
			'siteurl'   => home_url(),
		);

		if ( 'Signup' === $context && ! empty( $extra_data ) ) {
			$first_name = $extra_data['first_name'] ?? '';
			$last_name  = $extra_data['last_name'] ?? '';
			$full_name  = trim( $first_name . ' ' . $last_name );
			if ( empty( $full_name ) ) {
				$full_name = $extra_data['username'] ?? $extra_data['email'] ?? '';
			}
			$recipient_email = $extra_data['email'] ?? '';
			$recipient_phone = $extra_data['phone'] ?? '';

			$replacements['user_name']             = $full_name;
			$replacements['user_first_last_name']  = $full_name;
			$replacements['wp-first-name']         = $first_name ? $first_name : 'N/A';
			$replacements['wp-last-name']          = $last_name ? $last_name : 'N/A';
			$replacements['wp-username']           = $extra_data['username'] ?? $full_name;
			$replacements['wp-email']              = $recipient_email ? $recipient_email : 'N/A';
			$replacements['wc_billing_first_name'] = $first_name ? $first_name : 'N/A';
			$replacements['wc_billing_last_name']  = $last_name ? $last_name : 'N/A';
			$replacements['wc_billing_phone']      = $recipient_phone ? $recipient_phone : 'N/A';
			$replacements['first_name']            = $first_name ? $first_name : 'N/A';
			$replacements['last_name']             = $last_name ? $last_name : 'N/A';
			$replacements['phone']                 = $recipient_phone ? $recipient_phone : 'N/A';
			$replacements['email']                 = $recipient_email ? $recipient_email : 'N/A';
		}

		$email_sender = WAWP()->senders->email_sender ?? null;
		if ( $email_sender ) {
			remove_action( 'wp_mail_succeeded', array( $email_sender, 'log_wp_mail_success' ) );
			remove_action( 'wp_mail_failed', array( $email_sender, 'log_wp_mail_failure' ) );
		}
		if ( class_exists( 'WAWP_Email_Sender' ) ) {
			$inst = WAWP_Email_Sender::get_instance();
			remove_action( 'wp_mail_succeeded', array( $inst, 'log_wp_mail_success' ) );
			remove_action( 'wp_mail_failed', array( $inst, 'log_wp_mail_failure' ) );
		}

		$sent = wawp_messenger()->send(
			$email,
			array(
				'channel'          => 'email',
				'subject'          => $subject,
				'message'          => $message,
				'context'          => $context,
				'send_timing'      => 'instant',
				'is_html_template' => $is_html_template,
				'user_id'          => $user_id,
				'replacements'     => $replacements,
				'skip_log'         => true,
			)
		);

		if ( $email_sender ) {
			add_action( 'wp_mail_succeeded', array( $email_sender, 'log_wp_mail_success' ) );
			add_action( 'wp_mail_failed', array( $email_sender, 'log_wp_mail_failure' ) );
		}

		return $sent;
	}

	/**
	 * Get active WhatsApp instance.
	 *
	 * @param array $settings Settings.
	 * @return object|null
	 */
	public function get_active_whatsapp_instance( $settings ) {
		$raw_ids = $settings['selected_instance'] ?? ( $settings['instance'] ?? '' );
		if ( empty( $raw_ids ) ) {
			$online = WAWP()->instance_manager->get_online_instances_cached();
			return ! empty( $online ) ? $online[0] : null;
		}

		$ids = is_array( $raw_ids ) ? $raw_ids : explode( ',', (string) $raw_ids );
		$ids = array_filter( array_map( 'trim', $ids ) );

		if ( empty( $ids ) ) {
			$online = WAWP()->instance_manager->get_online_instances_cached();
			return ! empty( $online ) ? $online[0] : null;
		}

		$online_instances = array();
		foreach ( $ids as $id ) {
			// Try internal ID first.
			$inst = WAWP()->instance_manager->get_instance_by_id( (int) $id );

			// If not found, try external instance_id.
			if ( ! $inst ) {
				$inst = WAWP()->instance_manager->get_instance_by_instance_id( (string) $id );
			}

			if ( $inst && strtolower( (string) $inst->status ) === 'online' ) {
				$online_instances[] = $inst;
			}
		}

		if ( ! empty( $online_instances ) ) {
			// Randomly pick one for load balancing.
			return $online_instances[ array_rand( $online_instances ) ];
		}

		// Fallback to any online instance if specified ones are offline.
		$online = WAWP()->instance_manager->get_online_instances_cached();

		return ! empty( $online ) ? $online[0] : null;
	}

	/**
	 * Render dynamic styles.
	 *
	 * @param array $settings Settings.
	 */
	public function render_dynamic_styles( $settings ) {
		$btn_bg      = $settings['button_background_color'] ?? $settings['button_bg_color'] ?? $settings['wawp_checkout_otp_button_color'] ?? '#0073aa';
		$btn_text    = $settings['button_text_color'] ?? $settings['wawp_checkout_otp_button_text_color'] ?? '#ffffff';
		$wa_bg       = $settings['whatsapp_button_color'] ?? '#22c55e';
		$wa_text     = $settings['whatsapp_button_text_color'] ?? '#ffffff';
		$fb_bg       = $settings['firebase_button_color'] ?? '#3b82f6';
		$fb_text     = $settings['firebase_button_text_color'] ?? '#ffffff';
		$email_bg    = $settings['email_otp_button_color'] ?? '#22c55e';
		$email_text  = $settings['email_otp_button_text_color'] ?? '#ffffff';
		$verify_bg   = $settings['verify_button_color'] ?? $settings['wawp_checkout_otp_button_color'] ?? '#3b82f6';
		$verify_text = $settings['verify_button_text_color'] ?? $settings['wawp_checkout_otp_button_text_color'] ?? '#ffffff';
		$resend_bg   = $settings['resend_button_color'] ?? $settings['wawp_checkout_otp_resend_color'] ?? '#64748b';
		$resend_text = $settings['resend_button_text_color'] ?? $settings['wawp_checkout_otp_resend_text_color'] ?? '#ffffff';
		$pass_bg     = $settings['password_login_button_color'] ?? '#ef4444';
		$pass_text   = $settings['password_login_button_text_color'] ?? '#ffffff';
		$show_bg     = $settings['show_password_button_color'] ?? '#ef4444';
		$show_text   = $settings['show_password_button_text_color'] ?? '#ffffff';
		$back_color  = $settings['back_button_color'] ?? '#64748b';
		?>
		<style id="wawp-dynamic-buttons-css">
			.wawp-submit-button,
			.wawp-primary-btn {
				background-color:
					<?php echo esc_attr( (string) $btn_bg ); ?>
					!important;
				color:
					<?php echo esc_attr( (string) $btn_text ); ?>
					!important;
			}

			.wawp-custom-shortcode-item button,
			.wawp-custom-shortcode-item .button,
			.wawp-custom-shortcode-item .wawp-btn {
				background-color:
					<?php echo esc_attr( (string) $btn_bg ); ?>
					!important;
				color:
					<?php echo esc_attr( (string) $btn_text ); ?>
					!important;
			}

			.wawp-verify-btn,
			#wawp_verify_otp_phone,
			#wawp_verify_otp_email_merged,
			#wawp_verify_otp_btn {
				background-color:
					<?php echo esc_attr( (string) $verify_bg ); ?>
					!important;
				color:
					<?php echo esc_attr( (string) $verify_text ); ?>
					!important;
			}

			.wawp-whatsapp-btn {
				background-color:
					<?php echo esc_attr( (string) $wa_bg ); ?>
					!important;
				color:
					<?php echo esc_attr( (string) $wa_text ); ?>
					!important;
			}

			.wawp-firebase-btn {
				background-color:
					<?php echo esc_attr( (string) $fb_bg ); ?>
					!important;
				color:
					<?php echo esc_attr( (string) $fb_text ); ?>
					!important;
			}

			.wawp-email-otp-btn {
				background-color:
					<?php echo esc_attr( (string) $email_bg ); ?>
					!important;
				color:
					<?php echo esc_attr( (string) $email_text ); ?>
					!important;
			}

			.wawp-resend-btn {
				background-color:
					<?php echo esc_attr( (string) $resend_bg ); ?>
					!important;
				color:
					<?php echo esc_attr( (string) $resend_text ); ?>
					!important;
			}

			.wawp-password-login-btn {
				background-color:
					<?php echo esc_attr( (string) $pass_bg ); ?>
					!important;
				color:
					<?php echo esc_attr( (string) $pass_text ); ?>
					!important;
			}

			.wawp-show-password-btn {
				background-color:
					<?php echo esc_attr( (string) $show_bg ); ?>
					!important;
				color:
					<?php echo esc_attr( (string) $show_text ); ?>
					!important;
			}

			.wawp-back-btn {
				color:
					<?php echo esc_attr( (string) $back_color ); ?>
					!important;
			}

			.wawp-otp-inputs input:focus,
			.wawp-otp-inputs input.filled {
				border-color:
					<?php echo esc_attr( (string) $verify_bg ); ?>
					!important;
			}

			.wawp-otp-inputs input:focus {
				box-shadow: 0 0 0 4px
					<?php echo esc_attr( (string) $verify_bg ); ?>
					26 !important;
			}

			input:checked+.wawp-slider {
				background-color:
					<?php echo esc_attr( (string) $verify_bg ); ?>
					!important;
			}

			.wawp-otp-login-form .wawp-edit-button,
			.wawp-otp-resend a.wawp-edit-button {
				color:
					<?php echo esc_attr( (string) $verify_bg ); ?>
					!important;
			}
		</style>
		<?php
	}
}

