<?php
/**
 * Google reCaptcha validation
 */

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

if ( ! class_exists( 'OPL_Google_ReCaptcha_Validation' ) ) {

	class OPL_Google_ReCaptcha_Validation {

		/**
		 * The single instance of OPL_Google_ReCaptcha_Validation
		 *
		 * @var     object
		 * @access  private
		 */
		private static $_instance = null;

		/**
		 * Instance.
		 *
		 * @static
		 * @return OPL_Google_ReCaptcha_Validation - instance.
		 */
		public static function instance() {
			if ( is_null( self::$_instance ) ) {
				self::$_instance = new self();
			}
			return self::$_instance;
		}

		/**
		 * Constructor function.
		 *
		 * @access  public
		 * @return  void
		 */
		public function __construct() {

			$recaptcha_validation_default_login = get_theme_mod( 'opl_popup_recaptcha_enable_on_default_login', false );
			$recaptcha_validation_other_login   = get_theme_mod( 'opl_popup_recaptcha_enable_on_other_login_forms', false );
			$recaptcha_validation_comment_forms = get_theme_mod( 'opl_recpatcha_verification_comment_forms', false );

			if ( true === $recaptcha_validation_default_login || true === $recaptcha_validation_other_login ) {
				add_action( 'wp_enqueue_scripts', array( $this, 'opl_recaptcha_script' ), 999 );
				add_filter( 'authenticate', array( $this, 'opl_validate_recaptcha_login_forms' ), 21, 3 );
				add_action( 'lostpassword_post', array( $this, 'opl_validate_recaptcha_to_lost_password_form' ), 10, 2 );
				add_action( 'validate_password_reset', array( $this, 'opl_validate_recaptcha_to_lost_password_form'), 10, 2 );
			}

			if ( true === $recaptcha_validation_default_login ) {
				add_action( 'login_form', array( $this, 'opl_recaptcha_field' ) );
				add_action( 'login_enqueue_scripts', array( $this, 'opl_recaptcha_script' ), 999 );
				add_action( 'register_form', array( $this, 'opl_recaptcha_field' ) );
				add_filter( 'registration_errors', array( $this, 'opl_validate_recaptcha_registration_forms' ), 10, 3 );
				add_action( 'lostpassword_form', array( $this, 'opl_recaptcha_field' ) );
				add_action( 'resetpass_form', array( $this, 'opl_recaptcha_field') );

				if ( is_multisite() ) {
					add_action( 'signup_extra_fields', array( $this, 'opl_recaptcha_field' ) );
					add_filter( 'wpmu_validate_user_signup', array( $this, 'opl_wpmu_validate_signup' ) );
					add_filter( 'wpmu_validate_blog_signup', array( $this, 'opl_wpmu_validate_signup' ) );
				}
			}

			if ( true === $recaptcha_validation_comment_forms ) {
				add_action( 'comment_form_after_fields', array( $this, 'opl_recaptcha_field' ) );
                add_filter( 'preprocess_comment', array(  $this, 'opl_validate_recaptcha_comment_form'), 10, 1 );
			}

			if ( true === $recaptcha_validation_other_login ) {

				if ( class_exists( 'WooCommerce' ) ) {
					add_action( 'woocommerce_login_form', array( $this, 'opl_recaptcha_field' ) );
					add_action( 'woocommerce_register_form', array(  $this, 'opl_recaptcha_field') );
					add_action( 'woocommerce_lostpassword_form', array( $this, 'opl_recaptcha_field' ) );
					add_action( 'woocommerce_resetpassword_form', array( $this, 'opl_recaptcha_field') );
					add_filter( 'woocommerce_process_registration_errors', array( $this, 'opl_validate_recaptcha_registration_forms' ), 10, 3 );
					add_action( 'woocommerce_review_order_before_submit', array( $this, 'opl_recaptcha_field' ) );
					add_action( 'woocommerce_checkout_process', array( $this, 'opl_validate_recaptcha_woo_checkout_process' ) );
				}

				if ( class_exists( 'Easy_Digital_Downloads' ) ) {
					add_action( 'edd_login_fields_after', array( $this, 'opl_recaptcha_field' ) );
					add_filter( 'edd_register_form_fields_before_submit', array( $this, 'opl_recaptcha_field') );
					add_action( 'edd_process_register_form', array( $this, 'opl_validate_recaptcha_edd_register_form') );
				}

				if ( class_exists( 'BuddyPress' ) ) {
					add_action( 'bp_after_signup_profile_fields', array( $this, 'opl_recaptcha_field' ) );
                	add_action( 'bp_signup_validate', array(  $this, 'opl_validate_recaptcha_bp_signup_form') );
				}

				do_action('opl_popup_recaptcha_login_forms');
			}
		}


		public function opl_recaptcha_script() {

			if ( ! OPL_Form_Validation::should_render_validation('google_recaptcha') ) {
				return;
			}

			$recaptcha_version = get_option( 'owp_recaptcha_version' );
			$site_key          = ( 'v3' == $recaptcha_version ) ? get_option( 'owp_recaptcha3_site_key' ) : get_option( 'owp_recaptcha_site_key' );
			$secret_key        = ( 'v3' == $recaptcha_version ) ? get_option( 'owp_recaptcha3_secret_key' ) : get_option( 'owp_recaptcha_secret_key' );
			if ( ! empty( $site_key ) || ! empty( $secret_key ) ) {
				if ( 'v3' == $recaptcha_version ) {
					$recaptcha_script_args = array(
						'hl'     => str_replace( '_', '-', get_locale() ),
						'onload' => 'opl_google_recaptcha',
						'render' => $site_key,
					);
					$popup_script_localize = true;
					wp_enqueue_script( 'opl-recaptcha', add_query_arg( $recaptcha_script_args, 'https://www.google.com/recaptcha/api.js' ) );

					$inline_script = "
						function opl_google_recaptcha() {
							grecaptcha.execute('" . esc_html( $site_key ) . "', {action: 'submit'}).then(function(token) {
								var captchas = document.querySelectorAll('.opl-recaptcha-response');
								captchas.forEach(function(captcha) {
									captcha.value = token;
								});
							});
						}
					";
					wp_add_inline_script( 'opl-recaptcha', $inline_script );
				} else {
					wp_enqueue_script(
						'recaptcha',
						add_query_arg(
							array(
								'hl' => str_replace( '_', '-', get_locale() ),
							),
							'https://www.google.com/recaptcha/api.js'
						)
					);

					$style = '.login .g-recaptcha {
						transform: scale(0.9);
						display: flex;
						justify-content: center;
						margin-bottom: 10px;
					}';
					wp_add_inline_style( 'login', $style );
				}
			}
		}

		public function opl_validate_recaptcha_login_forms($user, $username, $password) {

			if ( ! OPL_Form_Validation::should_render_validation('google_recaptcha') ) {
				return $user;
			}

			if (! OPL_Form_Validation::is_targeted_login_request()) {
				return $user;
			}

			if (defined('WP_CLI') && WP_CLI || empty($username)) {
				return $user;
			}

			if (defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST) {
				return $user;
			}

			if (defined( 'REST_REQUEST' ) && REST_REQUEST) {
				return $user;
			}

			if(is_wp_error($user) && isset($user->errors['empty_username']) && isset($user->errors['empty_password']) ) {
				return $user;
			}

			do_action('opl_before_login_google_recpatcha_validation', $user);

			$captcha_code = isset($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : null;

			$recaptcha_valid = $this->opl_validate_recaptcha($captcha_code, 'wp_error');

			if ($recaptcha_valid !== true) {
				return $recaptcha_valid;
			}

			do_action('opl_after_login_google_recpatcha_validation', $user);

			return $user;
		}

		public function opl_validate_recaptcha_to_lost_password_form( $errors, $user ) {

			if (empty($_POST['pass1']) || empty($_POST['pass2'])) {
				return;
			}

			if ( ! OPL_Form_Validation::should_render_validation('google_recaptcha') ) {
				return $errors;
			}

			if ( ! OPL_Form_Validation::is_targeted_login_request() ) {
				return $errors;
			}

			if( stripos($_SERVER["REQUEST_URI"], strrchr(wp_login_url(), '/')) !== false
				|| isset( $_POST['woocommerce-lost-password-nonce'] ) ) {

				$captcha_code = isset($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : null;

				$recaptcha_valid = $this->opl_validate_recaptcha($captcha_code, 'add_error', $errors);

				if ($recaptcha_valid !== true) {
					return $recaptcha_valid;
				}
			}
		}

		public function opl_recaptcha_field() {

			if ( ! OPL_Form_Validation::should_render_validation('google_recaptcha') ) {
				return;
			}

			$recaptcha_version = get_option( 'owp_recaptcha_version' );
			$site_key          = ( 'v3' == $recaptcha_version ) ? get_option( 'owp_recaptcha3_site_key' ) : get_option( 'owp_recaptcha_site_key' );

			$output = '';
			if ( 'v3' == $recaptcha_version ) {
				$output .= '<input type="hidden" name="g-recaptcha-response" class="opl-recaptcha-response" value=""/>';
			} else {
				$output .= '<div class="g-recaptcha" data-sitekey="' . esc_html($site_key )  . '"></div>';
			}

			$html = apply_filters( 'opl_recaptcha_field', $output, $recaptcha_version, $site_key );

			echo $html;
		}

		public function opl_validate_recaptcha_registration_forms( $errors, $user_login, $user_email ) {

			if ( ! OPL_Form_Validation::should_render_validation('google_recaptcha') ) {
				return $errors;
			}

			if (defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST) {
				return $errors;
			}

			if (defined( 'REST_REQUEST' ) && REST_REQUEST) {
				return $errors;
			}

			do_action('opl_before_registration_google_recpatcha_validation', $errors);

			$captcha_code = isset($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : null;

			$recaptcha_valid = $this->opl_validate_recaptcha($captcha_code, 'add_error');

			if ($recaptcha_valid !== true) {
				return $recaptcha_valid;
			}

			do_action('opl_after_registration_google_recpatcha_validation', $errors);

			return $errors;
		}

		public function opl_wpmu_validate_signup( $result ) {

			if ( ! OPL_Form_Validation::should_render_validation('google_recaptcha') ) {
				return $result;
			}

			$captcha_code = isset($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : null;

			if ( empty( $captcha_code ) ) {
				$captcha_validate_result = OPL_Form_Validation::recaptcha_validate($captcha_code);

				if (is_wp_error($captcha_validate_result)) {
					$error_message = wp_strip_all_tags( $captcha_validate_result->get_error_message() );
					$result['errors']->add( 'recaptcha-failed', $error_message );
					return $result;
				}
			} else {
				$result['errors']->add( 'missing-input-response', __( 'reCAPTCHA verification failed. Please try again.', 'ocean-popup-login' ) );
				return $result;
			}

			return $result;

		}

		public function opl_validate_recaptcha_comment_form($commentdata) {

			// if user logged in, return.
			if ( absint($commentdata['user_ID']) > 0 ) {
				return $commentdata;
			}

			$captcha_code = isset($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : null;

			$recaptcha_valid = $this->opl_validate_recaptcha($captcha_code, 'wp_die');

			if ($recaptcha_valid !== true) {
				return $recaptcha_valid;
			}

			return $commentdata;
		}

		public function opl_validate_recaptcha_woo_checkout_process() {

			$only_guest = get_theme_mod( 'opl_recpatcha_verification_woo_guest_user', true );

			if ( ! $only_guest || ( $only_guest && !is_user_logged_in() ) ) {

				$captcha_code = isset($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : null;

				$recaptcha_valid = $this->opl_validate_recaptcha($captcha_code, 'wc_add_notice');

				if ($recaptcha_valid !== true) {
					return $recaptcha_valid;
				}
			}
		}

		public function opl_validate_recaptcha_edd_register_form( $errors ) {

			if ( ! OPL_Form_Validation::should_render_validation('google_recaptcha') ) {
				return $errors;
			}

			$captcha_code = isset($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : null;

			$recaptcha_valid = $this->opl_validate_recaptcha($captcha_code, 'edd_set_error');

			if ($recaptcha_valid !== true) {
				return $recaptcha_valid;
			}
		}

		public function opl_validate_recaptcha_bp_signup_form() {

			$captcha_code = isset($_POST['g-recaptcha-response']) ? sanitize_text_field($_POST['g-recaptcha-response']) : null;

			if (!empty($captcha_code)) {
				$this->opl_validate_recaptcha($captcha_code, 'wp_die');
			}
		}

		public function opl_validate_recaptcha($captcha_code, $error_method = 'wp_error', $errors = null) {

			if (!empty($captcha_code)) {
				$captcha_validate_result = OPL_Form_Validation::recaptcha_validate($captcha_code);

				if (is_wp_error($captcha_validate_result)) {
					$error_message = wp_strip_all_tags($captcha_validate_result->get_error_message());
					return $this->handle_recaptcha_error($error_message, $error_method, 'recaptcha-failed', $errors);
				}
			} else {
				$error_message = __('reCAPTCHA verification failed. Please try again.', 'ocean-popup-login');
				return $this->handle_recaptcha_error($error_message, $error_method, 'missing-input-response', $errors);
			}

			return true;
		}

		public function handle_recaptcha_error($error_message, $error_method, $error_code = null, $errors = null) {

			if ($error_method === 'wp_error') {
				return new WP_Error($error_code, $error_message);
			}

			switch ($error_method) {
				case 'wp_die':
					wp_die(
						'<p><strong>' . esc_html__('ERROR:', 'ocean-popup-login') . '</strong> ' . esc_html($error_message) . '</p>',
						esc_html__('reCAPTCHA', 'ocean-popup-login'),
						array(
							'response'  => 403,
							'back_link' => true,
						)
					);
					break;

				case 'edd_set_error':
					edd_set_error($error_code, $error_message);
					break;

				case 'wc_add_notice':
					wc_add_notice($error_message, $error_code);
					break;

				case 'add_error':
					if ($errors) {
						$errors->add($error_code, $error_message);
					}
					break;

				default:
					return new WP_Error($error_code, $error_message);
			}
		}
	}

	OPL_Google_ReCaptcha_Validation::instance();
}
