<?php
/**
 * Cloudflare Turnstile validation
 */

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

if ( ! class_exists( 'OPL_Cloudflare_Turnstile_Validation' ) ) {

	class OPL_Cloudflare_Turnstile_Validation {

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

		/**
		 * Instance.
		 *
		 * @static
		 * @return OPL_Cloudflare_Turnstile_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() {

			$validation_popup_login   = get_theme_mod( 'opl_popup_turnstile_enable_on_popup_login', false );
			$validation_default_login = get_theme_mod( 'opl_popup_turnstile_enable_on_default_login', false );
			$validation_other_login   = get_theme_mod( 'opl_popup_turnstile_enable_on_other_login_forms', false );
			$validation_comment_forms = get_theme_mod( 'opl_turnstile_verification_comment_forms', false );

			if ( true === $validation_popup_login) {
				add_action( 'opl_after_login_form_field_integration', array( $this, 'opl_validation_response_key_field' ) );
				add_action( 'opl_after_registration_form_field_integration', array( $this, 'opl_validation_response_key_field' ) );
			}

			if ( true === $validation_default_login || true === $validation_other_login ) {
				add_action( 'wp_enqueue_scripts', array( $this, 'opl_validation_script' ), 999 );
				add_filter( 'script_loader_tag', array( $this, 'add_async_defer_to_turnstile' ), 10, 2);
				add_filter( 'authenticate', array( $this, 'opl_validate_login_forms' ), 21, 3 );
				add_action( 'lostpassword_post', array( $this, 'opl_validate_to_lost_password_form' ), 10, 2 );
				add_action( 'validate_password_reset', array( $this, 'opl_validate_to_lost_password_form'), 10, 2 );
			}

			if ( true === $validation_default_login ) {
				add_action( 'login_form', array( $this, 'opl_validation_response_key_field' ) );
				add_action( 'login_enqueue_scripts', array( $this, 'opl_validation_script' ), 999 );
				add_action( 'register_form', array( $this, 'opl_validation_response_key_field' ) );
				add_filter( 'registration_errors', array( $this, 'opl_validate_registration_forms' ), 10, 3 );
				add_action( 'lostpassword_form', array( $this, 'opl_validation_response_key_field' ) );
				add_action( 'resetpass_form', array( $this, 'opl_validation_response_key_field') );

				if ( is_multisite() ) {
					add_action( 'signup_extra_fields', array( $this, 'opl_validation_response_key_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 === $validation_comment_forms ) {
				add_action( 'comment_form_after_fields', array( $this, 'opl_validation_response_key_field' ) );
                add_filter( 'preprocess_comment', array(  $this, 'opl_validate_comment_form'), 10, 1 );
			}

			if ( true === $validation_other_login ) {

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

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

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

				do_action('opl_popup_turnstile_login_forms');
			}
		}

		public function opl_validation_script() {

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

			$site_key   = get_option( 'owp_turnstile_site_key' );
			$secret_key = get_option( 'owp_turnstile_secret_key' );

			$render_method = get_option( 'owp_turnstile_render_method' );
			$render_method = $render_method ? $render_method : 'implicit';
			$theme_style   = get_option( 'owp_turnstile_theme' );
			$theme_style   = $theme_style ? $theme_style : 'auto';

			$locale = str_replace( '_', '-', get_locale() );
			$locale = strtolower($locale);

			if ( ! empty( $site_key ) || ! empty( $secret_key ) ) {
				if ( 'implicit' === $render_method ) {
					wp_enqueue_script(
						'cf-turnstile',
						'https://challenges.cloudflare.com/turnstile/v0/api.js',
						array(),
						null,
						true
					);
				} elseif ('explicit' === $render_method) {
					$script_args = array(
						'onload' => 'opl_turnstile_cb',
						'render' => 'explicit',
					);
					wp_enqueue_script(
						'owp-cf-turnstile',
						add_query_arg( $script_args, 'https://challenges.cloudflare.com/turnstile/v0/api.js' ),
						array(),
						null,
						true
					);

					$inline_script = "function opl_turnstile_cb() {
							var containers = document.querySelectorAll('#owp-turnstile-container');
							if (containers.length) {
								containers.forEach(function(container) {
									turnstile.render(container, {
										sitekey: '" . $site_key . "',
										theme: '" . $theme_style . "',
										language: '" . $locale . "'
									});
								});
							}
						}
					";
					wp_add_inline_script( 'owp-cf-turnstile', $inline_script );
				}

				$style = '.login .cf-turnstile, .login #owp-turnstile-container {
					transform: scale(0.9);
					display: flex;
					justify-content: center;
					margin-bottom: 10px;
				}';
				wp_add_inline_style( 'login', $style );
			}

			wp_localize_script(
				'opl-js-script',
				'oplLoginTurnstile',
				array(
					'key' => $site_key,
					'theme' => $theme_style,
					'language' => $locale,
				)
			);
		}

		public function add_async_defer_to_turnstile($tag, $handle) {
			if ('opl-turnstile' === $handle) {
				return str_replace(' src', ' async src', $tag);
			}
			return $tag;
		}

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

			if ( ! OPL_Form_Validation::should_render_validation('cloudflare_turnstile') ) {
				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_turnstile_validation', $user);

			$turnstile_token = isset($_POST['cf-turnstile-response']) ? sanitize_text_field($_POST['cf-turnstile-response']) : null;

			$turnstile_valid = $this->opl_validate_turnstile($turnstile_token, 'wp_error');

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

			do_action('opl_after_login_turnstile_validation', $user);

			return $user;
		}

		public function opl_validate_to_lost_password_form( $errors, $user ) {

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

			if ( ! OPL_Form_Validation::should_render_validation('cloudflare_turnstile') ) {
				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'] ) ) {

				$turnstile_token = isset($_POST['cf-turnstile-response']) ? sanitize_text_field($_POST['cf-turnstile-response']) : null;

				$turnstile_valid = $this->opl_validate_turnstile($turnstile_token, 'add_error', $errors);

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

		public function opl_validation_response_key_field() {

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

			$site_key   = get_option( 'owp_turnstile_site_key' );

			$render_method = get_option( 'owp_turnstile_render_method' );
			$render_method = $render_method ? $render_method : 'implicit';
			$theme_style   = get_option( 'owp_turnstile_theme' );
			$theme_style   = $theme_style ? $theme_style : 'auto';

			$locale = str_replace( '_', '-', get_locale() );
			$locale = strtolower($locale);

			$output = '';
			if ( 'implicit' == $render_method ) {
				$output .= '<div class="cf-turnstile" data-sitekey="' . $site_key . '" data-language="' . $locale . '" data-theme="' . $theme_style . '"></div>';
			} elseif ( 'explicit' == $render_method ) {
				$output .= '<div id="owp-turnstile-container"></div>';
			}

			$html = apply_filters( 'opl_validation_response_key_field', $output, $render_method, $site_key, $theme_style );

			echo $html;
		}

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

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

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

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

			do_action('opl_before_registration_cloudflare_turnstile_validation', $errors);

			$turnstile_token = isset($_POST['cf-turnstile-response']) ? sanitize_text_field($_POST['cf-turnstile-response']) : null;

			$turnstile_valid = $this->opl_validate_turnstile($turnstile_token, 'add_error');

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

			do_action('opl_after_registration_cloudflare_turnstile_validation', $errors);

			return $errors;
		}

		public function opl_wpmu_validate_signup( $result ) {

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

			$turnstile_token = isset($_POST['cf-turnstile-response']) ? sanitize_text_field($_POST['cf-turnstile-response']) : null;

			if ( empty( $turnstile_token ) ) {
				$turnstile_valid_result = OPL_Form_Validation::turnstile_validate($turnstile_token);

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

			return $result;

		}

		public function opl_validate_comment_form($commentdata) {

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

			$turnstile_token = isset($_POST['cf-turnstile-response']) ? sanitize_text_field($_POST['cf-turnstile-response']) : null;

			$turnstile_valid = $this->opl_validate_turnstile($turnstile_token, 'wp_die');

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

			return $commentdata;
		}

		public function opl_validate_woo_checkout_process() {

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

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

				$turnstile_token = isset($_POST['cf-turnstile-response']) ? sanitize_text_field($_POST['cf-turnstile-response']) : null;

				$turnstile_valid = $this->opl_validate_turnstile($turnstile_token, 'wc_add_notice');

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

		public function opl_validate_edd_register_form( $errors ) {

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

			$turnstile_token = isset($_POST['cf-turnstile-response']) ? sanitize_text_field($_POST['cf-turnstile-response']) : null;

			$turnstile_valid = $this->opl_validate_turnstile($turnstile_token, 'edd_set_error');

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

		public function opl_validate_bp_signup_form() {

			$turnstile_token = isset($_POST['cf-turnstile-response']) ? sanitize_text_field($_POST['cf-turnstile-response']) : null;

			if (!empty($turnstile_token)) {
				$this->opl_validate_turnstile($turnstile_token, 'wp_die');
			}
		}

		public function opl_validate_turnstile($turnstile_token, $error_method = 'wp_error', $errors = null) {

			if (!empty($turnstile_token)) {
				$turnstile_validate_result = OPL_Form_Validation::turnstile_validate($turnstile_token);

				if (is_wp_error($turnstile_validate_result)) {
					$error_message = wp_strip_all_tags($turnstile_validate_result->get_error_message());
					return $this->handle_validation_error($error_message, $error_method, 'turnstile-failed', $errors);
				}
			} else {
				$error_message = __('Turnstile verification failed. Please try again.', 'ocean-popup-login');
				return $this->handle_validation_error($error_message, $error_method, 'invalid-input-response', $errors);
			}

			return true;
		}

		public function handle_validation_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__('Turnstile', '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_Cloudflare_Turnstile_Validation::instance();
}
