<?php
/** Transactional Contact Form 7 writes with exact read-back and rollback. */

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

/** @return WPCF7_ContactForm|WP_Error */
function wpagent_cf7_candidate_from_args( array $args ) {
	$candidate = -1 === (int) ( $args['id'] ?? -1 )
		? WPCF7_ContactForm::get_template()
		: wpcf7_contact_form( (int) $args['id'] );
	if ( ! $candidate instanceof WPCF7_ContactForm ) {
		return wpagent_cf7_missing_error();
	}
	if ( array_key_exists( 'title', $args ) ) {
		$candidate->set_title( $args['title'] );
	}
	if ( array_key_exists( 'locale', $args ) ) {
		$candidate->set_locale( $args['locale'] );
	}
	$properties = array();
	if ( array_key_exists( 'form', $args ) ) {
		$properties['form'] = $args['form'];
	}
	if ( array_key_exists( 'mail', $args ) ) {
		$properties['mail']           = wpcf7_sanitize_mail( $args['mail'] );
		$properties['mail']['active'] = true;
	}
	if ( array_key_exists( 'mail_2', $args ) ) {
		$properties['mail_2'] = wpcf7_sanitize_mail( $args['mail_2'] );
	}
	if ( array_key_exists( 'messages', $args ) ) {
		$properties['messages'] = wpcf7_sanitize_messages( $args['messages'] );
	}
	$candidate->set_properties( $properties );
	return $candidate;
}

/** @return array<int,string> */
function wpagent_cf7_special_mail_tags(): array {
	return array(
		'_remote_ip', '_user_agent', '_url', '_date', '_time', '_invalid_fields', '_contact_form_title',
		'_post_id', '_post_name', '_post_title', '_post_url', '_post_author', '_post_author_email',
		'_site_title', '_site_description', '_site_url', '_site_domain', '_site_admin_email',
		'_user_login', '_user_email', '_user_url',
	);
}

/** @return true|WP_Error */
function wpagent_cf7_validate_mail_tags( WPCF7_ContactForm $form, string $template ) {
	$mail = $form->prop( $template );
	if ( ! is_array( $mail ) || ( 'mail_2' === $template && empty( $mail['active'] ) ) ) {
		return true;
	}
	$allowed = array_fill_keys( array_merge( $form->collect_mail_tags(), wpagent_cf7_special_mail_tags() ), true );
	$pattern = '/(\[?)\[[\t ]*([a-zA-Z_][0-9a-zA-Z:._-]*)((?:[\t ]+"[^"]*"|[\t ]+\'[^\']*\')*)[\t ]*\](\]?)/';
	foreach ( array( 'subject', 'sender', 'recipient', 'body', 'additional_headers', 'attachments' ) as $field ) {
		$content = (string) ( $mail[ $field ] ?? '' );
		if ( ! preg_match_all( $pattern, $content, $matches, PREG_SET_ORDER ) ) {
			continue;
		}
		foreach ( $matches as $match ) {
			if ( '[' === $match[1] && ']' === $match[4] ) {
				continue;
			}
			$name = (string) $match[2];
			if ( str_starts_with( $name, '_raw_' ) || str_starts_with( $name, '_format_' ) || ! isset( $allowed[ $name ] ) ) {
				return new WP_Error(
					'wpagent_cf7_unknown_mail_tag',
					/* translators: %s: Contact Form 7 mail tag name. */
					sprintf( __( 'Der Mail-Tag [%s] ist für dieses Formular nicht erlaubt.', 'wp-agent-companion' ), $name ),
					array( 'status' => 400, 'template' => $template, 'field' => $field, 'tag' => $name )
				);
			}
		}
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_cf7_validate_candidate( WPCF7_ContactForm $candidate, array $input, bool $creating ) {
	$all = $creating || array_key_exists( 'form', $input );
	foreach ( array( 'mail', 'mail_2' ) as $template ) {
		if ( $all || array_key_exists( $template, $input ) ) {
			$valid = wpagent_cf7_validate_mail_tags( $candidate, $template );
			if ( is_wp_error( $valid ) ) {
				return $valid;
			}
		}
	}
	if ( ! class_exists( 'WPCF7_ConfigValidator' ) ) {
		return true;
	}
	$validator = new WPCF7_ConfigValidator( $candidate );
	$validator->validate();
	$errors = $validator->collect_error_messages( array( 'decodes_html_entities' => true ) );
	if ( ! is_array( $errors ) || ! $errors ) {
		return true;
	}
	$relevant = array();
	foreach ( $errors as $section => $messages ) {
		$include = $all
			|| ( array_key_exists( 'mail', $input ) && str_starts_with( $section, 'mail.' ) )
			|| ( array_key_exists( 'mail_2', $input ) && str_starts_with( $section, 'mail_2.' ) )
			|| ( array_key_exists( 'messages', $input ) && str_starts_with( $section, 'messages.' ) );
		if ( $include ) {
			$relevant[ $section ] = $messages;
		}
	}
	return $relevant
		? new WP_Error( 'wpagent_cf7_config_invalid', __( 'Contact Form 7 hat die neue Konfiguration als fehlerhaft abgelehnt.', 'wp-agent-companion' ), array( 'status' => 400, 'config_errors' => $relevant ) )
		: true;
}

/** @return string|WP_Error */
function wpagent_cf7_acquire_lock( int $form_id ) {
	$option = 'wp_agent_cf7_lock_' . $form_id;
	$token  = wpagent_companion_acquire_option_lock( $option, 120 );
	if ( is_string( $token ) ) {
		return $token;
	}
	return new WP_Error( 'wpagent_cf7_locked', __( 'Dieses Formular wird gerade durch einen anderen Vorgang geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

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

/** @return array{title:string,locale:string,properties:array<string,mixed>,hash:string} */
function wpagent_cf7_snapshot( WPCF7_ContactForm $form ): array {
	$state         = wpagent_cf7_logical_state( $form );
	$state['hash'] = wpagent_cf7_state_hash( $form );
	return $state;
}

function wpagent_cf7_restore_snapshot( int $form_id, array $snapshot ): bool {
	$form = wpcf7_contact_form( $form_id );
	if ( ! $form instanceof WPCF7_ContactForm ) {
		return false;
	}
	$form->set_title( $snapshot['title'] );
	$form->set_locale( $snapshot['locale'] );
	$form->set_properties( $snapshot['properties'] );
	if ( ! $form->save() ) {
		return false;
	}
	$fresh = wpcf7_contact_form( $form_id );
	return $fresh instanceof WPCF7_ContactForm && hash_equals( $snapshot['hash'], wpagent_cf7_state_hash( $fresh ) );
}

/** @return array{form:array<string,mixed>,verified:bool}|WP_Error */
function wpagent_cf7_create_safe( array $input ) {
	$valid = wpagent_cf7_validate_write_input( $input, true );
	if ( is_wp_error( $valid ) ) {
		return $valid;
	}
	$base = WPCF7_ContactForm::get_template();
	$args = wpagent_cf7_build_save_args( $input, $base, true );
	if ( is_wp_error( $args ) ) {
		return $args;
	}
	$candidate = wpagent_cf7_candidate_from_args( $args );
	if ( is_wp_error( $candidate ) ) {
		return $candidate;
	}
	$valid = wpagent_cf7_validate_candidate( $candidate, $input, true );
	if ( is_wp_error( $valid ) ) {
		return $valid;
	}
	$expected = wpagent_cf7_state_hash( $candidate );
	$saved    = wpcf7_save_contact_form( $args, 'save' );
	$id       = $saved instanceof WPCF7_ContactForm ? (int) $saved->id() : 0;
	$fresh    = $id > 0 ? wpcf7_contact_form( $id ) : null;
	$verified = $fresh instanceof WPCF7_ContactForm && hash_equals( $expected, wpagent_cf7_state_hash( $fresh ) );
	if ( $verified ) {
		$valid    = wpagent_cf7_validate_candidate( $fresh, $input, true );
		$verified = ! is_wp_error( $valid );
	}
	if ( ! $verified ) {
		$rolled_back = $id > 0 && wpagent_cf7_delete_owned_candidate( $id, $expected );
		return new WP_Error(
			'wpagent_cf7_recovery_required',
			$rolled_back
				? __( 'Das neue Formular konnte nicht verifiziert werden und wurde als unveränderter, eigener Kandidat entfernt.', 'wp-agent-companion' )
				: __( 'Das neue Formular konnte nicht verifiziert werden. Sein aktueller Zustand muss vor einer manuellen Bereinigung geprüft werden.', 'wp-agent-companion' ),
			array( 'status' => 409, 'recovery_required' => ! $rolled_back, 'rollback_succeeded' => $rolled_back, 'form_id' => $id )
		);
	}
	$payload = wpagent_cf7_form_payload( $fresh );
	return is_wp_error( $payload ) ? $payload : array( 'form' => $payload, 'verified' => true );
}

/** @return array{form:array<string,mixed>,verified:bool}|WP_Error */
function wpagent_cf7_update_safe( array $input, WPCF7_ContactForm $form ) {
	$valid = wpagent_cf7_validate_write_input( $input );
	if ( is_wp_error( $valid ) ) {
		return $valid;
	}
	$expected = wpagent_cf7_expected_hash( $input['expected_hash'] ?? '' );
	if ( is_wp_error( $expected ) ) {
		return $expected;
	}
	$form_id = (int) $form->id();
	$lock    = wpagent_cf7_acquire_lock( $form_id );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$current = wpcf7_contact_form( $form_id );
		if ( ! $current instanceof WPCF7_ContactForm ) {
			return new WP_Error( 'wpagent_cf7_not_found', __( 'Contact-Form-7-Formular nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
		}
		$current_hash = wpagent_cf7_state_hash( $current );
		if ( ! hash_equals( $current_hash, $expected ) ) {
			return new WP_Error( 'wpagent_cf7_conflict', __( 'Das Formular wurde seit dem Lesen verändert.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $current_hash ) );
		}
		$args = wpagent_cf7_build_save_args( $input, $current, false );
		if ( is_wp_error( $args ) ) {
			return $args;
		}
		if ( 1 === count( $args ) ) {
			return new WP_Error( 'wpagent_no_fields', __( 'Kein zu aktualisierendes Formularfeld übergeben.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$candidate = wpagent_cf7_candidate_from_args( $args );
		if ( is_wp_error( $candidate ) ) {
			return $candidate;
		}
		$valid = wpagent_cf7_validate_candidate( $candidate, $input, false );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
		$expected_next = wpagent_cf7_state_hash( $candidate );
		$saved         = wpcf7_save_contact_form( $args, 'save' );
		$fresh         = $saved instanceof WPCF7_ContactForm ? wpcf7_contact_form( $form_id ) : null;
		$readback_ok   = $fresh instanceof WPCF7_ContactForm && hash_equals( $expected_next, wpagent_cf7_state_hash( $fresh ) );
		if ( $readback_ok ) {
			$valid       = wpagent_cf7_validate_candidate( $fresh, $input, false );
			$readback_ok = ! is_wp_error( $valid );
		}
		if ( ! $readback_ok ) {
			return new WP_Error( 'wpagent_cf7_recovery_required', __( 'Die CF7-Änderung konnte nicht verifiziert werden. Der aktuelle Zustand muss vor einem weiteren Schreibversuch manuell geprüft werden.', 'wp-agent-companion' ), array( 'status' => 409, 'recovery_required' => true, 'form_id' => $form_id ) );
		}
		$payload = wpagent_cf7_form_payload( $fresh );
		return is_wp_error( $payload ) ? $payload : array( 'form' => $payload, 'verified' => true );
	} finally {
		wpagent_cf7_release_lock( $form_id, $lock );
	}
}
