<?php
/** Shared WPForms confirmation and notification transaction helpers. */

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

define( 'WPAGENT_WPFORMS_DELIVERY_LOCK_TTL', 60 );
define( 'WPAGENT_WPFORMS_DELIVERY_MAX_BYTES', 1048576 );
define( 'WPAGENT_WPFORMS_DELIVERY_MAX_RECORDS', 50 );

/** @param mixed $input */
function wpagent_perm_wpforms_delivery( $input = null ): bool {
	if ( ! function_exists( 'wpforms' ) ) {
		return false;
	}
	if ( current_user_can( 'manage_options' ) ) {
		return true;
	}
	$user = wp_get_current_user();
	if ( ! $user instanceof WP_User || ! $user->exists() || ! wpagent_companion_is_safe_connection_user( $user ) ) {
		return false;
	}
	$capability = wpagent_companion_wpforms_access_capabilities()['write'];
	return user_can( $user, $capability );
}

/** @return object|WP_Error */
function wpagent_wpforms_delivery_provider() {
	if ( ! function_exists( 'wpforms' ) ) {
		return new WP_Error( 'wpagent_wpforms_missing', __( 'WPForms ist nicht aktiv.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	try {
		$app      = wpforms();
		$provider = is_object( $app ) && is_callable( array( $app, 'obj' ) ) ? $app->obj( 'form' ) : null;
		if ( ! is_object( $provider ) || ! is_callable( array( $provider, 'get' ) ) || ! is_callable( array( $provider, 'update' ) ) ) {
			throw new RuntimeException( 'missing form handler' );
		}
		return $provider;
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_wpforms_api_unavailable', __( 'Die native WPForms-Formular-API ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
}

/** @param mixed $value @return mixed */
function wpagent_wpforms_delivery_canonicalize( $value ) {
	if ( ! is_array( $value ) ) {
		return $value;
	}
	if ( ! array_is_list( $value ) ) {
		ksort( $value, SORT_STRING );
	}
	foreach ( $value as $key => $item ) {
		$value[ $key ] = wpagent_wpforms_delivery_canonicalize( $item );
	}
	return $value;
}

/** @param mixed $value */
function wpagent_wpforms_delivery_hash( $value ): string {
	$encoded = wp_json_encode( wpagent_wpforms_delivery_canonicalize( $value ), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
	return hash( 'sha256', false === $encoded ? serialize( $value ) : $encoded );
}

/** Returns the persisted value without consulting WordPress's object cache. */
function wpagent_wpforms_delivery_stored_raw( int $form_id ): ?string {
	global $wpdb;
	$value = $wpdb->get_var(
		$wpdb->prepare(
			"SELECT post_content FROM {$wpdb->posts} WHERE ID = %d AND post_type = %s LIMIT 1",
			$form_id,
			'wpforms'
		)
	);
	return is_string( $value ) ? $value : null;
}

/** @return string|WP_Error */
function wpagent_wpforms_delivery_expected_hash( $value ) {
	$hash = is_string( $value ) ? strtolower( $value ) : '';
	return preg_match( '/^[a-f0-9]{64}$/', $hash )
		? $hash
		: new WP_Error( 'wpagent_wpforms_delivery_hash', __( 'expected_hash muss ein vollständiger SHA-256-Hash sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
}

/** @param array<string,mixed> $data @return array<string,mixed> */
function wpagent_wpforms_delivery_material( array $data ): array {
	$settings = is_array( $data['settings'] ?? null ) ? $data['settings'] : array();
	return array(
		'notification_enable' => (string) ( $settings['notification_enable'] ?? '' ),
		'confirmations'       => is_array( $settings['confirmations'] ?? null ) ? $settings['confirmations'] : array(),
		'notifications'       => is_array( $settings['notifications'] ?? null ) ? $settings['notifications'] : array(),
	);
}

/** @param array<string,mixed> $data @return array<string,mixed> */
function wpagent_wpforms_delivery_preserved_material( array $data ): array {
	$copy = $data;
	if ( isset( $copy['settings'] ) && is_array( $copy['settings'] ) ) {
		unset( $copy['settings']['notification_enable'], $copy['settings']['confirmations'], $copy['settings']['notifications'] );
	}
	return $copy;
}

/** @param mixed $rows @return array<int,array<string,mixed>>|WP_Error */
function wpagent_wpforms_delivery_records( $rows ) {
	if ( ! is_array( $rows ) ) {
		return new WP_Error( 'wpagent_wpforms_delivery_invalid', __( 'WPForms hat ungültige Zustellungsdaten geliefert.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( count( $rows ) > WPAGENT_WPFORMS_DELIVERY_MAX_RECORDS ) {
		return new WP_Error( 'wpagent_wpforms_delivery_too_large', __( 'Das Formular enthält zu viele Bestätigungen oder Benachrichtigungen für eine sichere Bearbeitung.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$records = array();
	$max_id_text = (string) PHP_INT_MAX;
	foreach ( $rows as $record_id => $row ) {
		$id_text = (string) $record_id;
		$id_fits = strlen( $id_text ) < strlen( $max_id_text )
			|| ( strlen( $id_text ) === strlen( $max_id_text ) && strcmp( $id_text, $max_id_text ) <= 0 );
		if ( 1 !== preg_match( '/^[1-9][0-9]*$/D', $id_text ) || ! $id_fits || ! is_array( $row ) ) {
			return new WP_Error( 'wpagent_wpforms_delivery_invalid', __( 'WPForms hat ungültige Zustellungsdaten geliefert.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		}
		$id = (int) $id_text;
		if ( array_key_exists( $id, $records ) ) {
			return new WP_Error( 'wpagent_wpforms_delivery_invalid', __( 'WPForms hat ungültige Zustellungsdaten geliefert.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		}
		$records[ $id ] = $row;
	}
	ksort( $records, SORT_NUMERIC );
	return $records;
}

/** @param array<string,mixed> $record @return array<string,mixed> */
function wpagent_wpforms_confirmation_public( int $id, array $record ): array {
	$type = in_array( $record['type'] ?? '', array( 'message', 'page', 'redirect' ), true ) ? $record['type'] : 'message';
	return array(
		'id'                  => $id,
		'name'                => sanitize_text_field( (string) ( $record['name'] ?? '' ) ),
		'type'                => $type,
		'message'             => wp_strip_all_tags( (string) ( $record['message'] ?? '' ) ),
		'message_scroll'      => rest_sanitize_boolean( $record['message_scroll'] ?? false ),
		'page_id'             => absint( $record['page'] ?? 0 ),
		'page_url_parameters' => sanitize_text_field( (string) ( $record['page_url_parameters'] ?? '' ) ),
		'redirect'            => esc_url_raw( (string) ( $record['redirect'] ?? '' ) ),
		'redirect_new_tab'    => rest_sanitize_boolean( $record['redirect_new_tab'] ?? false ),
		'hash'                => wpagent_wpforms_delivery_hash( $record ),
	);
}

/** @param array<int,array<string,mixed>> $records @return int|WP_Error */
function wpagent_wpforms_delivery_next_id( array $records ) {
	if ( count( $records ) >= WPAGENT_WPFORMS_DELIVERY_MAX_RECORDS ) {
		return new WP_Error( 'wpagent_wpforms_delivery_too_many', __( 'Das Formular hat die sichere Höchstzahl von 50 Bestätigungen oder Benachrichtigungen erreicht.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( ! $records ) {
		return 1;
	}
	$last_id = max( array_keys( $records ) );
	if ( $last_id >= PHP_INT_MAX ) {
		return new WP_Error( 'wpagent_wpforms_delivery_id_exhausted', __( 'Für eine weitere WPForms-Zustellungsregel ist keine sichere numerische ID verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return $last_id + 1;
}

/** WPForms Lite executes only one notification and lacks safe multi-confirmation selection. */
function wpagent_wpforms_delivery_supports_multiple(): bool {
	try {
		$app = function_exists( 'wpforms' ) ? wpforms() : null;
		return is_object( $app ) && is_callable( array( $app, 'is_pro' ) ) && true === $app->is_pro();
	} catch ( Throwable $error ) {
		return false;
	}
}

/** @param array<int,array<string,mixed>> $records @return true|WP_Error */
function wpagent_wpforms_delivery_can_create( array $records ) {
	if ( ! $records || wpagent_wpforms_delivery_supports_multiple() ) {
		return true;
	}
	return new WP_Error(
		'wpagent_wpforms_delivery_multiple_requires_pro',
		__( 'WPForms Lite verarbeitet nur eine wirksame Bestätigung und E-Mail-Benachrichtigung. Eine weitere Regel benötigt WPForms Pro.', 'wp-agent-companion' ),
		array( 'status' => 409 )
	);
}

/** @param array<string,mixed> $record @return array<string,mixed> */
function wpagent_wpforms_notification_public( int $id, array $record, bool $include_details ): array {
	$result = array(
		'id'                => $id,
		'name'              => sanitize_text_field( (string) ( $record['notification_name'] ?? '' ) ),
		'subject'           => sanitize_text_field( (string) ( $record['subject'] ?? '' ) ),
		'hash'              => wpagent_wpforms_delivery_hash( $record ),
	);
	if ( $include_details ) {
		$result['email']          = sanitize_text_field( (string) ( $record['email'] ?? '' ) );
		$result['sender_name']    = sanitize_text_field( (string) ( $record['sender_name'] ?? '' ) );
		$result['sender_address'] = sanitize_text_field( (string) ( $record['sender_address'] ?? '' ) );
		$result['reply_to']       = sanitize_text_field( (string) ( $record['replyto'] ?? '' ) );
		$result['carbon_copy']    = sanitize_text_field( (string) ( $record['carboncopy'] ?? '' ) );
		$result['message']        = wp_strip_all_tags( (string) ( $record['message'] ?? '' ) );
	}
	return $result;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_wpforms_delivery_state( int $form_id ) {
	if ( $form_id <= 0 ) {
		return new WP_Error( 'wpagent_wpforms_form_id', __( 'form_id muss größer als null sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$provider = wpagent_wpforms_delivery_provider();
	if ( is_wp_error( $provider ) ) {
		return $provider;
	}
	if ( ! wpforms_current_user_can( 'edit_form_single', $form_id ) ) {
		return new WP_Error( 'wpagent_wpforms_forbidden', __( 'Du darfst dieses WPForms-Formular nicht bearbeiten.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	$post = null;
	$raw  = '';
	$data = false;
	for ( $attempt = 0; $attempt < 2; $attempt++ ) {
		clean_post_cache( $form_id );
		$post = get_post( $form_id );
		if ( ! $post instanceof WP_Post || 'wpforms' !== $post->post_type || 'trash' === $post->post_status ) {
			return new WP_Error( 'wpagent_wpforms_not_found', __( 'Das WPForms-Formular wurde nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
		}
		$raw = (string) $post->post_content;
		if ( strlen( $raw ) > WPAGENT_WPFORMS_DELIVERY_MAX_BYTES ) {
			return new WP_Error( 'wpagent_wpforms_delivery_too_large', __( 'Das Formular ist größer als 1 MiB und wird aus Sicherheitsgründen nicht bearbeitet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		try {
			$data = $provider->get( $form_id, array( 'content_only' => true, 'cap' => 'edit_form_single' ) );
		} catch ( Throwable $error ) {
			$data = false;
		}
		if ( ! is_array( $data ) ) {
			return new WP_Error( 'wpagent_wpforms_delivery_read_failed', __( 'Die WPForms-Zustellungsdaten konnten nicht gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		}
		$stored_raw = wpagent_wpforms_delivery_stored_raw( $form_id );
		if ( is_string( $stored_raw ) && hash_equals( $raw, $stored_raw ) ) {
			break;
		}
		$data = false;
	}
	if ( ! is_array( $data ) ) {
		return new WP_Error( 'wpagent_wpforms_delivery_conflict', __( 'Das WPForms-Formular wurde während des Lesens verändert. Lies den Zustand erneut.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$settings      = is_array( $data['settings'] ?? null ) ? $data['settings'] : array();
	$confirmations = wpagent_wpforms_delivery_records( $settings['confirmations'] ?? array() );
	$notifications = wpagent_wpforms_delivery_records( $settings['notifications'] ?? array() );
	if ( is_wp_error( $confirmations ) || is_wp_error( $notifications ) ) {
		return is_wp_error( $confirmations ) ? $confirmations : $notifications;
	}
	return array(
		'form_id'          => $form_id,
		'provider'         => $provider,
		'post'             => $post,
		'raw'              => $raw,
		'data'             => $data,
		'confirmations'    => $confirmations,
		'notifications'    => $notifications,
		'state_hash'       => hash( 'sha256', "wpagently-wpforms-delivery-v1\0" . $form_id . "\0" . $raw ),
		'delivery_hash'    => wpagent_wpforms_delivery_hash( wpagent_wpforms_delivery_material( $data ) ),
		'preserved_hash'   => wpagent_wpforms_delivery_hash( wpagent_wpforms_delivery_preserved_material( $data ) ),
	);
}

/** @param array<string,mixed> $state @return array<string,mixed> */
function wpagent_wpforms_delivery_public( array $state, bool $include_details = false ): array {
	$confirmations = array();
	foreach ( $state['confirmations'] as $id => $record ) {
		$confirmations[] = wpagent_wpforms_confirmation_public( $id, $record );
	}
	$notifications = array();
	foreach ( $state['notifications'] as $id => $record ) {
		$notifications[] = wpagent_wpforms_notification_public( $id, $record, $include_details );
	}
	return array(
		'form_id'                       => $state['form_id'],
		'notifications_enabled'          => rest_sanitize_boolean( $state['data']['settings']['notification_enable'] ?? false ),
		'confirmations'                  => $confirmations,
		'notifications'                  => $notifications,
		'state_hash'                     => $state['state_hash'],
		'notification_details_included'  => $include_details,
	);
}

/** @return string|WP_Error */
function wpagent_wpforms_delivery_acquire_lock( int $form_id ) {
	$option = 'wp_agent_wpforms_delivery_lock_' . $form_id;
	$token  = wpagent_companion_acquire_option_lock( $option, WPAGENT_WPFORMS_DELIVERY_LOCK_TTL );
	if ( is_string( $token ) ) {
		return $token;
	}
	return new WP_Error( 'wpagent_wpforms_delivery_busy', __( 'Die WPForms-Zustellung dieses Formulars wird gerade geändert. Versuche es gleich erneut.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

function wpagent_wpforms_delivery_release_lock( int $form_id, string $token ): void {
	wpagent_companion_release_option_lock( 'wp_agent_wpforms_delivery_lock_' . $form_id, $token );
}

function wpagent_wpforms_delivery_lock_is_owned( int $form_id, string $token ): bool {
	return $form_id > 0
		&& '' !== $token
		&& wpagent_companion_option_lock_is_owned( 'wp_agent_wpforms_delivery_lock_' . $form_id, $token );
}

/** @param array<string,mixed> $state */
function wpagent_wpforms_delivery_restore( array $state ): bool {
	// WPForms has no conditional restore. Rewriting post_content could erase a
	// native editor's concurrent delivery or form change.
	return false;
}
