<?php

namespace WPProofShield\Integration;

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


use WPProofShield\Security\RequestVerifier;
use WPProofShield\Security\StatsStore;
use WPProofShield\Support\FeatureAccess\FeatureAccess;
use WPProofShield\Support\RequestInput;
use WPProofShield\Support\Settings;
use WPProofShield\Support\UiLanguage;
final class PostedFormGuard {

	public function __construct( private Settings $settings, private RequestVerifier $verifier, private FeatureAccess $feature_access, private StatsStore $stats ) {
	}
	public function register(): void {

		add_action( 'init', array( $this, 'verifyPostedFormContext' ), 1 );
		add_action( 'wp_ajax_wpsh_log_nojs_view', array( $this, 'handleNoJsViewPing' ) );
		add_action( 'wp_ajax_nopriv_wpsh_log_nojs_view', array( $this, 'handleNoJsViewPing' ) );
	}
	public function verifyPostedFormContext(): void {

		if ( ! isset( $_SERVER['REQUEST_METHOD'] ) || strtoupper( sanitize_text_field( wp_unslash( (string) $_SERVER['REQUEST_METHOD'] ) ) ) !== 'POST' ) {
			return;
		}

		if ( $this->shouldSkipDedicatedAjaxValidation() ) {
			return;
		}
		$contact_forms_enabled = false && $this->settings->isEnabled( 'enabled_contact_forms' );
		$generic_forms_enabled = $this->license_gate->canUse( 'generic_forms' ) && $this->settings->isEnabled( 'enabled_generic_forms' );
		if ( ! $contact_forms_enabled && ! $generic_forms_enabled ) {
			return;
		}

		$required = $this->postText( 'wpsh_required' );
		$ctx      = $this->postText( 'wpsh_ctx' );
		if ( $required === '1' && $ctx === '' ) {
			$this->reject( $ctx );
		}

		if ( $ctx === '' || ! str_starts_with( $ctx, 'form:' ) ) {
			if ( $contact_forms_enabled && $this->isLikelyNoJsProtectedFormSubmit() ) {
				$this->recordNoJsBlock();
				$this->rejectNoJs();
			}
			return;
		}
		$is_free_contact_context = in_array(
			$ctx,
			array(
				'form:elementor',
				'form:cf7',
				'form:wpforms',
				'form:enfold',
				'form:gravityforms',
				'form:ninjaforms',
				'form:formidable',
				'form:fluentforms',
				'form:jetformbuilder',
				'form:everestforms',
				'form:bricks',
				'form:divi',
			),
			true
		);
		if ( $is_free_contact_context && ! $contact_forms_enabled ) {
			return;
		}

		$is_generic_context = ! $is_free_contact_context;
		if ( $is_generic_context ) {
			if ( ! $generic_forms_enabled ) {
				return;
			}

			if ( ! $this->isAllowedGenericContext( $ctx ) ) {
				$this->reject( $ctx );
			}
		}

		$result = $this->verifier->verify( $ctx, true );
		if ( is_wp_error( $result ) ) {
			$this->reject( $ctx, $result->get_error_code() );
		}

		$GLOBALS['wpsh_form_ctx_verified'] = true;
	}

	private function shouldSkipDedicatedAjaxValidation(): bool {

		if ( ! wp_doing_ajax() ) {
			return false;
		}

		$action = sanitize_key( $this->requestText( 'action' ) );
		if ( $action === '' ) {
			return false;
		}

		$actions = array();

		return in_array( $action, $actions, true );
	}
	private function isAllowedGenericContext( string $ctx ): bool {
		$selectors_raw = trim( (string) $this->settings->get( 'generic_form_selectors', '' ) );
		if ( $selectors_raw === '' ) {
			return false;
		}

		$selectors = preg_split( '/\R+/', $selectors_raw ) ?: array();
		foreach ( $selectors as $selector ) {
			$selector = trim( (string) $selector );
			if ( $selector === '' ) {
				continue;
			}

			$allowed_ctx = 'form:' . substr( md5( $selector ), 0, 12 );
			if ( hash_equals( $allowed_ctx, $ctx ) ) {
				return true;
			}
		}

		return false;
	}

	private function reject( string $ctx = '', string $reason = 'wpsh_verification_failed' ): void {

		$this->logSecurityError(
			'form:verify_failed',
			array(
				'reason' => sanitize_key( $reason ),
				'ctx'    => sanitize_text_field( $ctx ),
			)
		);
		if ( wp_doing_ajax() ) {
			wp_send_json_error(
				array(
					'message' => __( 'ProofShield verification failed.', 'proofshield' ),
					'reason'  => sanitize_key( $reason ),
					'errors'  => array(),
					'data'    => array(),
				)
			);
		}

		wp_die( esc_html__( 'ProofShield verification failed.', 'proofshield' ), esc_html__( 'Blocked', 'proofshield' ), array( 'response' => 403 ) );
	}

	private function rejectNoJs(): void {

		$message = __( 'ProofShield requires JavaScript for this protected form. Please enable JavaScript and try again.', 'proofshield' );
		$this->logSecurityError( 'form:no_js_blocked', array( 'reason' => 'wpsh_js_required' ) );
		if ( wp_doing_ajax() ) {
			wp_send_json_error(
				array(
					'message' => $message,
					'reason'  => 'wpsh_js_required',
				),
				403
			);
		}

		wp_die( esc_html( $message ), esc_html__( 'Blocked', 'proofshield' ), array( 'response' => 403 ) );
	}

	public function renderNoJsFallbackUi(): void {

		if ( is_admin() || wp_doing_ajax() || is_feed() || is_robots() || is_trackback() ) {
			return;
		}

		$contact_forms_enabled = false && $this->settings->isEnabled( 'enabled_contact_forms' );
		$generic_forms_enabled = $this->license_gate->canUse( 'generic_forms' ) && $this->settings->isEnabled( 'enabled_generic_forms' );
		if ( ! $contact_forms_enabled && ! $generic_forms_enabled ) {
			return;
		}

		$texts    = $this->noJsContactTexts( UiLanguage::current( $this->settings ) );
		$logo     = $this->noJsLogoUrl();
		$logoHtml = $logo !== '' ? '<img class="wpsh-nojs-card__logo" src="' . esc_url( $logo ) . '" alt="ProofShield" />' : '';
		$pingUrl  = add_query_arg(
			array(
				'action'     => 'wpsh_log_nojs_view',
				'ctx'        => 'form:no_js_view',
				'wpsh_nonce' => wp_create_nonce( 'wpsh_log_nojs_view' ),
			),
			admin_url( 'admin-ajax.php' )
		);
		$card     = '<div class="wpsh-nojs-card" data-wpsh-nojs-card="1">' . $logoHtml . '<p class="wpsh-nojs-card__title">' . esc_html( $texts['title'] ) . '</p>' . '<p class="wpsh-nojs-card__reason">' . esc_html( $texts['reason'] ) . '</p>' . '</div>';
		$markup   = '<noscript><img src="' . esc_url( $pingUrl ) . '" alt="" width="1" height="1" /><div class="wpsh-nojs-fallback">' . $card . '</div></noscript>';

		echo wp_kses(
			$markup,
			array(
				'noscript' => array(),
				'img'      => array(
					'class'  => true,
					'src'    => true,
					'alt'    => true,
					'width'  => true,
					'height' => true,
				),
				'div'      => array(
					'class'              => true,
					'data-wpsh-nojs-card' => true,
				),
				'p'        => array(
					'class' => true,
				),
			)
		);
	}

	public function injectNoJsFormUi( string $html ): string {

		if ( $html === '' || stripos( $html, '<form' ) === false ) {
			return $html;
		}

		$texts     = $this->noJsContactTexts( UiLanguage::current( $this->settings ) );
		$logo      = $this->noJsLogoUrl();
		$logoHtml  = $logo !== '' ? '<img class="wpsh-nojs-card__logo" src="' . esc_url( $logo ) . '" alt="ProofShield" />' : '';
		$cardInner = '<div class="wpsh-nojs-card" data-wpsh-nojs-card="1">' . $logoHtml . '<p class="wpsh-nojs-card__title">' . esc_html( $texts['title'] ) . '</p>' . '<p class="wpsh-nojs-card__reason">' . esc_html( $texts['reason'] ) . '</p>' . '</div>';
		$pingUrl   = add_query_arg(
			array(
				'action'     => 'wpsh_log_nojs_view',
				'ctx'        => 'form:no_js_view',
				'wpsh_nonce' => wp_create_nonce( 'wpsh_log_nojs_view' ),
			),
			admin_url( 'admin-ajax.php' )
		);
		$card      = '<noscript><img src="' . esc_url( $pingUrl ) . '" alt="" width="1" height="1" style="position:absolute;left:-9999px;opacity:0;" />' . $cardInner . '</noscript>';
		$result    = preg_replace_callback(
			'~<form\b[^>]*>.*?</form>~is',
			function ( array $m ) use ( $card ): string {

				$formHtml = (string) ( $m[0] ?? '' );
				if ( $formHtml === '' ) {
					return $formHtml;
				}

				$hasSubmit                = preg_match( '~<(button|input)\b[^>]*\btype\s*=\s*["\']?submit["\']?~i', $formHtml ) === 1;
				$looksLikeExcluded        = preg_match( '~(id\s*=\s*["\']loginform["\']|name\s*=\s*["\']s["\']|wp-comments-post\.php|comment-form|commentform)~i', $formHtml ) === 1;
				$isLikelyFrontendPostForm = preg_match( '~\bmethod\s*=\s*["\']?post["\']?~i', $formHtml ) === 1 || preg_match( '~(elementor|wpcf7|wpforms|gform|ninja|frm-|jet-form|everest|bricks|et_pb|avia|getresponse|newsletter|subscribe)~i', $formHtml ) === 1;
				if ( ! $hasSubmit || $looksLikeExcluded || ! $isLikelyFrontendPostForm ) {
					return $formHtml;
				}

				$formHtml = preg_replace_callback(
					'~<form\b([^>]*)>~i',
					static function ( array $f ): string {

						$attrs = (string) ( $f[1] ?? '' );
						if ( preg_match( '~\bclass\s*=\s*["\']([^"\']*)["\']~i', $attrs, $cm ) === 1 ) {
							$classes = trim( (string) ( $cm[1] ?? '' ) );
							if ( ! str_contains( ' ' . $classes . ' ', ' wpsh-protected-form ' ) ) {
								$newClasses = trim( $classes . ' wpsh-protected-form' );
								$attrs      = preg_replace( '~\bclass\s*=\s*["\'][^"\']*["\']~i', 'class="' . $newClasses . '"', $attrs, 1 ) ?? $attrs;
							}
							return '<form' . $attrs . '>';
						}
						return '<form class="wpsh-protected-form"' . $attrs . '>';
					},
					$formHtml,
					1
				) ?? $formHtml;
				$withCard = $this->insertCardBeforeSubmitZone( $formHtml, $card );
				if ( $withCard !== $formHtml ) {
					return $withCard;
				}

				$openInjected = preg_replace( '~(<form\b[^>]*>)~i', '$1' . $card, $formHtml, 1 );
				return is_string( $openInjected ) ? $openInjected : $formHtml;
			},
			$html
		);
		return is_string( $result ) ? $result : $html;
	}

	private function recordNoJsBlock(): void {

		$this->stats->record( 'form:no_js', 'wpsh_js_required', 0, null );
	}

	public function handleNoJsViewPing(): void {
		if ( ! $this->hasValidNoJsViewNonce() ) {
			status_header( 204 );
			exit;
		}

		$ctx = RequestInput::getText( 'ctx' );
		$ctx = $ctx !== '' ? $ctx : 'form:no_js_view';
		if ( $ctx !== 'form:no_js_view' ) {
			$ctx = 'form:no_js_view';
		}

		$ip        = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( (string) $_SERVER['REMOTE_ADDR'] ) ) : '0.0.0.0';
		$uri       = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( (string) $_SERVER['REQUEST_URI'] ) ) : '';
		$dedupeKey = 'wpsh_nojs_view_v2_' . md5( $ip . '|' . $uri );
		if ( get_transient( $dedupeKey ) !== false ) {
			status_header( 204 );
			exit;
		}

		set_transient( $dedupeKey, 1, 5 * MINUTE_IN_SECONDS );
		$this->stats->record( $ctx, 'wpsh_js_required', 0, null );
		status_header( 204 );
		exit;
	}

	private function hasValidNoJsViewNonce(): bool {
		return false !== check_ajax_referer( 'wpsh_log_nojs_view', 'wpsh_nonce', false );
	}

	private function insertCardBeforeSubmitZone( string $formHtml, string $card ): string {

		$containerPatterns = array( '~(<div\b[^>]*class=["\'][^"\']*elementor-field-type-submit[^"\']*["\'][^>]*>)~i', '~(<div\b[^>]*class=["\'][^"\']*wpforms-submit-container[^"\']*["\'][^>]*>)~i', '~(<div\b[^>]*class=["\'][^"\']*gform_footer[^"\']*["\'][^>]*>)~i', '~(<div\b[^>]*class=["\'][^"\']*frm_submit[^"\']*["\'][^>]*>)~i', '~(<div\b[^>]*class=["\'][^"\']*fluentform-submit[^"\']*["\'][^>]*>)~i', '~(<div\b[^>]*class=["\'][^"\']*jet-form-builder__submit[^"\']*["\'][^>]*>)~i', '~(<div\b[^>]*class=["\'][^"\']*everest-forms-submit[^"\']*["\'][^>]*>)~i', '~(<div\b[^>]*class=["\'][^"\']*avia-form-submit[^"\']*["\'][^>]*>)~i', '~(<div\b[^>]*class=["\'][^"\']*et_contact_bottom_container[^"\']*["\'][^>]*>)~i', '~(<p\b[^>]*class=["\'][^"\']*form-submit[^"\']*["\'][^>]*>)~i' );
		foreach ( $containerPatterns as $pattern ) {
			$patched = preg_replace( $pattern, $card . '$1', $formHtml, 1 );
			if ( is_string( $patched ) && $patched !== $formHtml ) {
				return $patched;
			}
		}

		$patched = preg_replace( '~(<(button|input)\b[^>]*\btype\s*=\s*["\']?submit["\']?[^>]*>)~i', $card . '$1', $formHtml, 1 );
		if ( is_string( $patched ) && $patched !== $formHtml ) {
			return $patched;
		}

		return $formHtml;
	}

	/**
	 * @return array{title:string,reason:string}
	 */
	private function noJsContactTexts( string $language ): array {

		return match ( $language ) {
			'en' => array(
				'title'  => 'ProofShield contact form protection requires JavaScript.',
				'reason' => 'Reason: JavaScript is disabled. Please enable JavaScript to submit this form.',
			), 'es' => array(
				'title'  => 'La proteccion de formularios de contacto de ProofShield requiere JavaScript.',
				'reason' => 'Motivo: JavaScript esta desactivado. Active JavaScript para enviar este formulario.',
			), 'ru' => array(
				'title'  => 'Zashchita kontaktnykh form ProofShield trebuet JavaScript.',
				'reason' => 'Prichina: JavaScript otklyuchen. Vklyuchite JavaScript, chtoby otpravit formu.',
			), 'fr' => array(
				'title'  => 'La protection des formulaires de contact ProofShield necessite JavaScript.',
				'reason' => 'Raison: JavaScript est desactive. Activez JavaScript pour envoyer ce formulaire.',
			), 'it' => array(
				'title'  => 'La protezione dei moduli di contatto di ProofShield richiede JavaScript.',
				'reason' => 'Motivo: JavaScript e disattivato. Abilita JavaScript per inviare questo modulo.',
			), 'nl' => array(
				'title'  => 'ProofShield-contactformulierbescherming vereist JavaScript.',
				'reason' => 'Reden: JavaScript is uitgeschakeld. Schakel JavaScript in om dit formulier te verzenden.',
			), 'pl' => array(
				'title'  => 'Ochrona formularzy kontaktowych ProofShield wymaga JavaScript.',
				'reason' => 'Powod: JavaScript jest wylaczony. Wlacz JavaScript, aby wyslac ten formularz.',
			), 'tr' => array(
				'title'  => 'ProofShield iletisim formu korumasi JavaScript gerektirir.',
				'reason' => 'Neden: JavaScript devre disi. Bu formu gondermek icin JavaScripti etkinlestirin.',
			), 'ja' => array(
				'title'  => 'ProofShield no contact form hogo ni wa JavaScript ga hitsuyo desu.',
				'reason' => 'Riyuu: JavaScript ga off desu. Kono form o soushin suru ni wa JavaScript o on ni shite kudasai.',
			), 'pt' => array(
				'title'  => 'A protecao de formularios de contato do ProofShield requer JavaScript.',
				'reason' => 'Motivo: JavaScript esta desativado. Ative o JavaScript para enviar este formulario.',
			), 'zh' => array(
				'title'  => 'ProofShield lianxi biaodan baohu xuyao JavaScript.',
				'reason' => 'Yuanyin: JavaScript yi guanbi. Qing qiyong JavaScript hou zai tijiao biaodan.',
			), default => array(
				'title'  => 'Der ProofShield-Kontaktformularschutz benötigt JavaScript.',
				'reason' => 'Grund: JavaScript ist deaktiviert. Bitte aktivieren Sie JavaScript, um dieses Formular zu senden.',
			),
		};
	}

	private function noJsLogoUrl(): string {

		foreach ( array( 'assets/images/proofshield-free.png', 'assets/images/logo-free.png', 'assets/images/logo.png', 'assets/images/proofshield-mark.png' ) as $relative ) {
			$absolutePath = WPSH_DIR . $relative;
			if ( is_readable( $absolutePath ) ) {
				$version = (string) filemtime( $absolutePath );
				return (string) add_query_arg( 'ver', $version, WPSH_URL . $relative );
			}
		}

		return '';
	}

	/**
	 * @param array<string,scalar|null> $context
	 */
	private function logSecurityError( string $event, array $context = array() ): void {

		/**
		 * Allow optional custom logging/instrumentation without shipping direct PHP error_log() calls.
		 *
		 * @param string                    $event   Event name.
		 * @param array<string,scalar|null> $context Event context.
		 */
		do_action( 'wpsh_security_error', $event, $context, 'PostedFormGuard' );
	}

	private function isLikelyNoJsProtectedFormSubmit(): bool {

		$method = isset( $_SERVER['REQUEST_METHOD'] ) ? strtoupper( sanitize_text_field( wp_unslash( (string) $_SERVER['REQUEST_METHOD'] ) ) ) : 'GET';
		if ( $method !== 'POST' ) {
			return false;
		}

		if ( $this->postText( 'wpsh_pow' ) !== '' || $this->postText( 'wpsh_ctx' ) !== '' || $this->postText( 'wpsh_required' ) !== '' ) {
			return false;
		}

		if ( $this->postText( '_wpcf7' ) !== '' || $this->postText( '_wpcf7_unit_tag' ) !== '' ) {
			return true;
		}

		if ( $this->postText( 'form_id' ) !== '' || $this->postText( 'elementor_tag' ) !== '' ) {
			return true;
		}

		if ( $this->postText( 'wpforms' ) !== '' || $this->postText( 'wpforms-id' ) !== '' ) {
			return true;
		}

		if ( $this->postText( 'gform_submit' ) !== '' || $this->postText( 'is_submit_1' ) !== '' ) {
			return true;
		}

		if ( $this->postText( '_ninja_forms_display_submit' ) !== '' || $this->postText( '_ninja_forms_form_id' ) !== '' ) {
			return true;
		}

		if ( $this->postText( 'frm_action' ) !== '' || $this->postText( 'form_key' ) !== '' ) {
			return true;
		}

		if ( $this->postText( '_fluentform' ) !== '' || $this->postText( 'fluent_form_id' ) !== '' ) {
			return true;
		}

		if ( $this->postText( 'jet_form_builder_submit' ) !== '' || $this->postText( '_jet_engine_booking_form_id' ) !== '' ) {
			return true;
		}

		if ( $this->postText( 'everest_forms_form_id' ) !== '' || $this->postText( 'evf_submit' ) !== '' ) {
			return true;
		}

		if ( $this->postText( 'et_pb_contactform_submit' ) !== '' || $this->postText( 'avia_generated_form' ) !== '' ) {
			return true;
		}

		return false;
	}

	private function postText( string $key ): string {
		return RequestInput::postText( $key );
	}

	private function requestText( string $key ): string {
		return RequestInput::requestText( $key );
	}
}
