<?php
/**
 * Extra form/membership integrations (Gravity, Jetpack, bbPress, etc.).
 *
 * @package SimpleCaptcha
 */

namespace SimpleCaptcha;

final class ExtendedIntegrations {

	private static bool $booted = false;

	public static function boot( Module $m ): void {
		if ( self::$booted ) {
			return;
		}
		self::$booted = true;

		$s = $m->getCaptchaSettings();

		if ( ! empty( $s['integration_gravity'] ) && class_exists( 'GFForms' ) ) {
			add_filter(
				'gform_submit_button',
				static function ( $button, $form ) use ( $m ) {
					if ( ! is_array( $form ) || empty( $form['id'] ) ) {
						return $button;
					}
					ob_start();
					$m->renderWidget( 'gravity_' . absint( $form['id'] ) );
					return (string) ob_get_clean() . $button;
				},
				10,
				2
			);
			add_filter(
				'gform_validation',
				static function ( $result ) use ( $m ) {
					if ( empty( $result['form']['id'] ) || ! is_array( $result ) ) {
						return $result;
					}
					$form_id = absint( $result['form']['id'] );
					// Multi-page: only enforce on the final submit (Gravity Forms' own page resolution).
					if ( ! self::gravityIsFinalSubmit( $result['form'], $form_id ) ) {
						return $result;
					}
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						$result['is_valid'] = false;
						if ( empty( $result['form']['validation_message'] ) ) {
							$result['form']['validation_message'] = $m->getSimpleCaptchaRequestErrorMessage();
						}
					}
					return $result;
				},
				10
			);
		}

		if ( ! empty( $s['integration_jetpack'] ) ) {
			add_filter(
				'jetpack_contact_form_html',
				static function ( $html ) use ( $m ) {
					if ( ! is_string( $html ) || $html === '' ) {
						return $html;
					}
					ob_start();
					$m->renderWidget( 'jetpack_contact' );
					$inject = (string) ob_get_clean();

					return self::injectBeforeSubmit( $html, $inject );
				},
				10
			);
			add_filter(
				'jetpack_contact_form_is_spam',
				static function ( $spam ) use ( $m ) {
					if ( $spam ) {
						return $spam;
					}
					return ! $m->validateSimpleCaptchaRequest();
				},
				10
			);
		}

		if ( ! empty( $s['integration_bbpress'] ) ) {
			add_action(
				'bbp_theme_before_topic_form_submit_wrapper',
				static function () use ( $m ) {
					$m->renderWidget( 'bbpress_topic' );
				}
			);
			add_action(
				'bbp_theme_before_reply_form_submit_wrapper',
				static function () use ( $m ) {
					$m->renderWidget( 'bbpress_reply' );
				}
			);
			add_action(
				'bbp_new_topic_pre_extras',
				static function () use ( $m ) {
					if ( 'POST' !== strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ?? '' ) ) ) ) {
						return;
					}
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						bbp_add_error( 'bbp_simple_captcha', $m->getSimpleCaptchaRequestErrorMessage() );
					}
				}
			);
			add_action(
				'bbp_new_reply_pre_extras',
				static function () use ( $m ) {
					if ( 'POST' !== strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ?? '' ) ) ) ) {
						return;
					}
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						bbp_add_error( 'bbp_simple_captcha', $m->getSimpleCaptchaRequestErrorMessage() );
					}
				}
			);
		}

		if ( ! empty( $s['integration_buddypress'] ) && class_exists( 'BuddyPress' ) ) {
			add_action(
				'bp_before_registration_submit_buttons',
				static function () use ( $m ) {
					$m->renderWidget( 'buddypress_register' );
				}
			);
			add_action(
				'bp_signup_validate',
				static function () use ( $m ) {
					if ( 'POST' !== strtoupper( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_METHOD'] ?? '' ) ) ) ) {
						return;
					}
					if ( $m->validateSimpleCaptchaRequest() ) {
						return;
					}
					$message = $m->getSimpleCaptchaRequestErrorMessage();
					if ( function_exists( 'buddypress' ) ) {
						$bp = buddypress();
						if ( isset( $bp->signup ) && is_object( $bp->signup ) ) {
							if ( ! isset( $bp->signup->errors ) || ! is_array( $bp->signup->errors ) ) {
								$bp->signup->errors = [];
							}
							$bp->signup->errors['wpps_simple_captcha'] = $message;
							return;
						}
					}
					if ( function_exists( 'bp_core_add_message' ) ) {
						bp_core_add_message( $message, 'error' );
						return;
					}
					wp_die( esc_html( $message ), '', [ 'response' => 403, 'back_link' => true ] );
				}
			);
		}

		if ( ! empty( $s['integration_ultimate_member'] ) && class_exists( 'UM' ) ) {
			add_action( 'um_after_login_fields', static fn () => $m->renderWidget( 'ultimate_member_login' ) );
			add_action( 'um_after_register_fields', static fn () => $m->renderWidget( 'ultimate_member_register' ) );
			add_action( 'um_after_password_reset_fields', static fn () => $m->renderWidget( 'ultimate_member_reset' ) );
			add_action(
				'um_submit_form_errors_hook_login',
				static function () use ( $m ) {
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						UM()->form()->add_error( 'simple_captcha', $m->getSimpleCaptchaRequestErrorMessage() );
					}
				},
				20
			);
			add_action(
				'um_submit_form_errors_hook__registration',
				static function () use ( $m ) {
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						UM()->form()->add_error( 'simple_captcha', $m->getSimpleCaptchaRequestErrorMessage() );
					}
				},
				20
			);
			add_action(
				'um_reset_password_errors_hook',
				static function () use ( $m ) {
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						UM()->form()->add_error( 'simple_captcha', $m->getSimpleCaptchaRequestErrorMessage() );
					}
				},
				20
			);
		}

		if ( ! empty( $s['integration_memberpress'] ) && class_exists( 'MeprAppCtrl' ) ) {
			add_action( 'mepr-login-form-before-submit', static fn () => $m->renderWidget( 'memberpress_login' ) );
			add_action( 'mepr-checkout-before-submit', static fn () => $m->renderWidget( 'memberpress_checkout' ) );
			add_filter(
				'mepr-validate-login',
				static function ( $errors ) use ( $m ) {
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						$errors[] = $m->getSimpleCaptchaRequestErrorMessage();
					}
					return $errors;
				}
			);
			add_filter(
				'mepr-validate-signup',
				static function ( $errors ) use ( $m ) {
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						$errors[] = $m->getSimpleCaptchaRequestErrorMessage();
					}
					return $errors;
				},
				20
			);
		}

		if ( ! empty( $s['integration_edd'] ) && function_exists( 'EDD' ) ) {
			add_action( 'edd_login_fields_after', static fn () => $m->renderWidget( 'edd_login' ) );
			add_action( 'edd_register_form_fields_before_submit', static fn () => $m->renderWidget( 'edd_register' ) );
			add_action( 'edd_purchase_form_before_submit', static fn () => $m->renderWidget( 'edd_checkout' ) );
			add_filter(
				'authenticate',
				static function ( $user ) use ( $m ) {
					if ( isset( $_POST['edd_login_nonce'] ) && function_exists( 'edd_is_checkout' ) && ! edd_is_checkout() ) {
						if ( ! $m->validateSimpleCaptchaRequest() ) {
							return new \WP_Error( 'simple_captcha_failed', $m->getSimpleCaptchaRequestErrorMessage() );
						}
					}
					return $user;
				},
				21,
				1
			);
			add_action(
				'edd_process_register_form',
				static function () use ( $m ) {
					if ( function_exists( 'edd_is_checkout' ) && edd_is_checkout() ) {
						return;
					}
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						edd_set_error( 'simple_captcha', $m->getSimpleCaptchaRequestErrorMessage() );
					}
				}
			);
			add_action(
				'edd_pre_process_purchase',
				static function () use ( $m ) {
					if ( ! isset( $_POST['edd-process-checkout-nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['edd-process-checkout-nonce'] ) ), 'edd-process-checkout' ) ) {
						return;
					}
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						edd_set_error( 'simple_captcha', $m->getSimpleCaptchaRequestErrorMessage() );
					}
				}
			);
		}

		if ( ! empty( $s['integration_pmp'] ) && defined( 'PMPRO_VERSION' ) ) {
			add_action( 'pmpro_checkout_before_submit_button', static fn () => $m->renderWidget( 'pmp' ) );
			// ONLY runs on submit — pmpro_checkout_after_parameters_set fires on page load.
			add_filter(
				'pmpro_registration_checks',
				static function ( $ok ) use ( $m ) {
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						pmpro_setMessage( $m->getSimpleCaptchaRequestErrorMessage(), 'pmpro_error' );
						return false;
					}
					return $ok;
				}
			);

			// Frontend login ([pmpro_login]) uses wp_login_form → login_form_middle.
			add_filter(
				'login_form_middle',
				static function ( $content, $args = [] ) use ( $m ) {
					unset( $args );
					if ( ! self::isPmproLoginPage() ) {
						return $content;
					}
					if ( str_contains( (string) $content, 'wpps-simple-captcha-wrap' ) || str_contains( (string) $content, 'wpps_simple_captcha' ) ) {
						return $content;
					}
					ob_start();
					$m->renderWidget( 'pmp_login' );
					return $content . (string) ob_get_clean();
				},
				10,
				2
			);
			add_filter(
				'authenticate',
				static function ( $user ) use ( $m ) {
					if ( empty( $_POST['pmpro_login_form_used'] ) ) {
						return $user;
					}
					if ( $user instanceof \WP_Error ) {
						return $user;
					}
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						return new \WP_Error( 'simple_captcha_failed', $m->getSimpleCaptchaRequestErrorMessage() );
					}
					return $user;
				},
				21,
				1
			);

			add_action(
				'pmpro_lost_password_before_submit_button',
				static function () use ( $m ) {
					$m->renderWidget( 'pmp_lostpassword' );
				}
			);
			add_action(
				'lostpassword_post',
				static function ( $errors ) use ( $m ) {
					if ( empty( $_POST['pmpro_login_form_used'] ) ) {
						return;
					}
					if ( ! ( $errors instanceof \WP_Error ) ) {
						return;
					}
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						$errors->add( 'simple_captcha_failed', $m->getSimpleCaptchaRequestErrorMessage() );
					}
				},
				10,
				1
			);

			add_filter(
				'the_content',
				static function ( $content ) use ( $m, $s ) {
					if ( ! is_string( $content ) || ! self::isPmproLoginPage() ) {
						return $content;
					}
					if ( ! str_contains( $content, 'id="resetpassform"' ) ) {
						return $content;
					}
					if ( str_contains( $content, 'wpps-simple-captcha-wrap' ) || str_contains( $content, 'wpps_simple_captcha' ) ) {
						return $content;
					}
					$action = ! empty( $s['protect_password_reset'] ) ? 'resetpass' : 'pmp_resetpass';
					ob_start();
					$m->renderWidget( $action );
					$inject = (string) ob_get_clean();
					if ( $inject === '' ) {
						return $content;
					}
					$replaced = preg_replace(
						'/(<input\b[^>]*\bid\s*=\s*["\']resetpass-button["\'][^>]*>)/i',
						$inject . '$1',
						$content,
						1
					);
					return is_string( $replaced ) ? $replaced : $content;
				},
				20
			);
			if ( empty( $s['protect_password_reset'] ) ) {
				add_action(
					'validate_password_reset',
					static function ( $errors, $user ) use ( $m ) {
						unset( $user );
						if ( ! isset( $_POST['pass1'] ) || ! ( $errors instanceof \WP_Error ) ) {
							return;
						}
						if ( ! $m->validateSimpleCaptchaRequest() ) {
							$errors->add( 'simple_captcha_failed', $m->getSimpleCaptchaRequestErrorMessage() );
						}
					},
					10,
					2
				);
			}
		}

		// MC4WP: shortcode-only (same approach as Turnstile / reCAPTCHA).
		// Do not gate on MC4WP_VERSION — PowerSuite may boot before that constant exists.
		if ( ! empty( $s['integration_mc4wp'] ) ) {
			$shortcode    = 'wpps-mc4wp-simple-captcha';
			$render_mc4wp = static function () use ( $m ) {
				ob_start();
				$m->renderWidget( 'mc4wp' );
				return (string) ob_get_clean();
			};

			add_shortcode(
				$shortcode,
				static function () use ( $render_mc4wp ) {
					return $render_mc4wp();
				}
			);

			add_filter( 'mc4wp_form_content', 'do_shortcode', 11 );

			add_filter(
				'mc4wp_form_errors',
				static function ( $errors, $form ) use ( $m, $shortcode ) {
					$post = ( is_object( $form ) && ! empty( $form->ID ) ) ? get_post( (int) $form->ID ) : null;
					if ( ! $post instanceof \WP_Post || ! has_shortcode( (string) $post->post_content, $shortcode ) ) {
						return $errors;
					}
					if ( ! $m->validateSimpleCaptchaRequest() ) {
						$errors[] = 'wpps_simple_captcha';
					}
					return $errors;
				},
				10,
				2
			);
			add_filter(
				'mc4wp_form_messages',
				static function ( $messages ) {
					$default  = __( 'Please complete the verification.', 'wp-powersuite' );
					$filtered = apply_filters( 'wpps_captcha_error_message', $default, 'mc4wp', 'simple-captcha' );
					$messages['wpps_simple_captcha'] = is_string( $filtered ) && $filtered !== ''
						? $filtered
						: $default;
					return $messages;
				}
			);
		}
	}

	/**
	 * Whether the current front-end request is the PMPro login page.
	 */
	private static function isPmproLoginPage(): bool {
		if ( function_exists( 'pmpro_is_login_page' ) && pmpro_is_login_page() ) {
			return true;
		}
		if ( ! function_exists( 'pmpro_getOption' ) ) {
			return false;
		}
		$page_id = (int) pmpro_getOption( 'login_page_id' );
		if ( $page_id <= 0 ) {
			return false;
		}
		$current = (int) get_queried_object_id();
		if ( $current <= 0 && function_exists( 'get_the_ID' ) ) {
			$current = (int) get_the_ID();
		}
		return $current > 0 && $current === $page_id;
	}

	/**
	 * Whether this Gravity Forms request is the submit that saves the entry.
	 *
	 * Must mirror Gravity Forms' own resolution rather than reading
	 * `gform_target_page_number_{id}` directly.
	 *
	 * @param array<string, mixed> $form    Form object from the validation result.
	 * @param int                  $form_id Form ID.
	 */
	private static function gravityIsFinalSubmit( array $form, int $form_id ): bool {
		if (
			$form_id === 0
			|| ! class_exists( '\GFFormDisplay' )
			|| ! method_exists( '\GFFormDisplay', 'get_target_page' )
			|| ! method_exists( '\GFFormDisplay', 'get_source_page' )
		) {
			return true;
		}

		$source       = (int) \GFFormDisplay::get_source_page( $form_id );
		$field_values = isset( $_POST['gform_field_values'] ) ? wp_unslash( $_POST['gform_field_values'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Gravity Forms owns nonce handling for its own submission.

		return (int) \GFFormDisplay::get_target_page( $form, $source, $field_values ) === 0;
	}

	/**
	 * Insert CAPTCHA markup immediately before the first submit control.
	 */
	private static function injectBeforeSubmit( string $html, string $inject ): string {
		if ( $inject === '' ) {
			return $html;
		}
		if ( preg_match( '/<input\b[^>]*\btype\s*=\s*["\']?submit["\']?[^>]*\/?>/i', $html ) ) {
			return (string) preg_replace(
				'/(<input\b[^>]*\btype\s*=\s*["\']?submit["\']?[^>]*\/?>)/i',
				$inject . '$1',
				$html,
				1
			);
		}
		if ( preg_match( '/<button\b[^>]*\btype\s*=\s*["\']?submit["\']?[^>]*>/i', $html ) ) {
			return (string) preg_replace(
				'/(<button\b[^>]*\btype\s*=\s*["\']?submit["\']?[^>]*>)/i',
				$inject . '$1',
				$html,
				1
			);
		}
		if ( preg_match( '/<button\b(?![^>]*\btype\s*=)[^>]*>/i', $html ) ) {
			return (string) preg_replace(
				'/(<button\b(?![^>]*\btype\s*=)[^>]*>)/i',
				$inject . '$1',
				$html,
				1
			);
		}
		if ( stripos( $html, '</form>' ) !== false ) {
			return (string) preg_replace( '/(<\/form>)/i', $inject . '$1', $html, 1 );
		}

		return $html . $inject;
	}
}
