<?php
/** Provider-assigned Gutenberg identity finalizer for the confirmed Live Editor. */

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

const WPAGENT_LIVE_EDITOR_PROVIDER_FINALIZE_MAX_CANDIDATES = 100;
const WPAGENT_LIVE_EDITOR_PROVIDER_FINALIZE_MAX_BYTES      = 1048576;

function wpagent_companion_live_editor_is_provider_finalize_action( string $action ): bool {
	return 'finalize-provider-attributes' === $action;
}

/** @return array<string,array<string,array{attribute:string,provider:string}>> */
function wpagent_companion_live_editor_provider_finalize_policy(): array {
	/*
	 * Excluded deliberately: GenerateBlocks Element, Kadence Advanced Heading,
	 * Spacer and Count Up, plus Spectra headings. Their generated static markup
	 * embeds the provider ID, so retaining it would require an unsafe markup write.
	 */
	return array(
		'generateblocks' => array(
			'generateblocks/query'              => array( 'attribute' => 'uniqueId', 'provider' => 'generateblocks' ),
			'generateblocks/query-page-numbers' => array( 'attribute' => 'uniqueId', 'provider' => 'generateblocks' ),
			'generateblocks/text'               => array( 'attribute' => 'uniqueId', 'provider' => 'generateblocks' ),
		),
		'kadence' => array(
			'kadence/progress-bar' => array( 'attribute' => 'uniqueID', 'provider' => 'kadence' ),
		),
	);
}

/** @return array<string,mixed>|WP_Error */
function wpagent_companion_live_editor_provider_finalize_command_args( array $input ) {
	$allowed = array( 'action', 'reason', 'post_id', 'expected_hash', 'expected_state_hash' );
	if ( array_diff( array_keys( $input ), $allowed ) ) {
		return new WP_Error( 'wpagent_live_editor_finalize_input', __( 'Die Finalisierung akzeptiert nur Dokument- und Zustandsnachweise, keine Rohdaten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$post_id = absint( $input['post_id'] ?? 0 );
	$reason  = is_string( $input['reason'] ?? null ) ? trim( sanitize_text_field( $input['reason'] ) ) : '';
	$hash    = is_string( $input['expected_hash'] ?? null ) ? $input['expected_hash'] : '';
	$state   = is_string( $input['expected_state_hash'] ?? null ) ? $input['expected_state_hash'] : '';
	if ( ! wpagent_companion_live_editor_is_provider_finalize_action( (string) ( $input['action'] ?? '' ) ) || $post_id < 1 || '' === $reason || strlen( $reason ) > 500 || ! preg_match( '/^[a-f0-9]{64}$/D', $hash ) || ! preg_match( '/^[a-f0-9]{64}$/D', $state ) ) {
		return new WP_Error( 'wpagent_live_editor_finalize_input', __( 'Die Provider-Finalisierung benötigt einen Beitrag, eine Begründung und beide aktuellen Zustands-Hashes.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return array( 'post_id' => $post_id, 'reason' => $reason, 'expected_hash' => $hash, 'expected_state_hash' => $state );
}

/** @return array{attribute:string,provider:string}|WP_Error */
function wpagent_companion_live_editor_provider_finalize_definition( string $block_name, string $attribute ) {
	$namespace = wpagent_native_block_namespace( $block_name );
	$definition = wpagent_companion_live_editor_provider_finalize_policy()[ $namespace ][ $block_name ] ?? null;
	$type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name );
	$schema = $type instanceof WP_Block_Type ? $type->get_attributes()[ $attribute ] ?? null : null;
	if ( ! is_array( $definition ) || $definition['attribute'] !== $attribute || ! $type instanceof WP_Block_Type || ! is_array( $schema ) || 'string' !== ( $schema['type'] ?? null ) || ! is_callable( $type->render_callback ) ) {
		return new WP_Error( 'wpagent_live_editor_finalize_attribute', __( 'Dieser Provider-Identifier ist nicht freigegeben.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$provider = wpagent_builder_block_require_provider( $definition['provider'] );
	return is_wp_error( $provider ) ? $provider : $definition;
}

/** @return true|WP_Error */
function wpagent_companion_live_editor_provider_finalize_valid_identity( string $value ) {
	return 1 === preg_match( '/^[A-Za-z0-9_-]{6,64}$/D', $value )
		? true
		: new WP_Error( 'wpagent_live_editor_finalize_identity', __( 'Der vom Provider gemeldete Identifier ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
}

/** @return array<string,array<string,mixed>>|WP_Error */
function wpagent_companion_live_editor_provider_finalize_capture( $capture, array $args ) {
	if ( ! is_array( $capture ) || array_diff( array_keys( $capture ), array( 'post_id', 'before_state_hash', 'mounted_state_hash', 'candidates' ) )
		|| absint( $capture['post_id'] ?? 0 ) !== absint( $args['post_id'] ?? 0 )
		|| ! is_string( $capture['before_state_hash'] ?? null ) || ! hash_equals( (string) $args['expected_state_hash'], $capture['before_state_hash'] )
		|| ! is_string( $capture['mounted_state_hash'] ?? null ) || ! preg_match( '/^[a-f0-9]{64}$/D', $capture['mounted_state_hash'] )
		|| ! is_array( $capture['candidates'] ?? null ) || count( $capture['candidates'] ) > WPAGENT_LIVE_EDITOR_PROVIDER_FINALIZE_MAX_CANDIDATES ) {
		return new WP_Error( 'wpagent_live_editor_finalize_capture', __( 'Die Provider-Erfassung gehört nicht zum bestätigten Editorzustand.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$patches = array();
	foreach ( $capture['candidates'] as $candidate ) {
		if ( ! is_array( $candidate ) || array_diff( array_keys( $candidate ), array( 'path', 'block_name', 'attribute', 'value' ) )
			|| ! is_array( $candidate['path'] ?? null ) || ! is_string( $candidate['block_name'] ?? null ) || ! is_string( $candidate['attribute'] ?? null ) || ! is_string( $candidate['value'] ?? null ) ) {
			return new WP_Error( 'wpagent_live_editor_finalize_capture', __( 'Die Provider-Erfassung enthält nicht erlaubte Daten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$path_valid = wpagent_native_block_validate_path( $candidate['path'] );
		if ( is_wp_error( $path_valid ) ) {
			return $path_valid;
		}
		$path = $candidate['path'];
		$definition = wpagent_companion_live_editor_provider_finalize_definition( $candidate['block_name'], $candidate['attribute'] );
		$identity = wpagent_companion_live_editor_provider_finalize_valid_identity( $candidate['value'] );
		if ( is_wp_error( $definition ) || is_wp_error( $identity ) ) {
			return is_wp_error( $definition ) ? $definition : $identity;
		}
		$key = implode( '.', $path );
		if ( isset( $patches[ $key ] ) ) {
			return new WP_Error( 'wpagent_live_editor_finalize_capture', __( 'Ein Providerblock darf nur einmal finalisiert werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$patches[ $key ] = array( 'path' => $path, 'block_name' => $candidate['block_name'], 'attribute' => $candidate['attribute'], 'value' => $candidate['value'], 'provider' => $definition['provider'] );
	}
	return $patches;
}

/** @return true|WP_Error */
function wpagent_companion_live_editor_provider_finalize_validate_tree( array $blocks ) {
	foreach ( $blocks as $block ) {
		$name = is_array( $block ) && is_string( $block['blockName'] ?? null ) ? $block['blockName'] : '';
		if ( ! preg_match( '#^(?:core|generateblocks|kadence|uagb)/[a-z0-9-]+$#D', $name ) || in_array( $name, array( 'core/html', 'core/freeform', 'core/shortcode' ), true ) || ! WP_Block_Type_Registry::get_instance()->get_registered( $name ) || ! is_array( $block['innerBlocks'] ?? null ) ) {
			return new WP_Error( 'wpagent_live_editor_finalize_document', __( 'Das Dokument enthält nicht registrierte, unbekannte oder unstrukturierte Blöcke.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$valid = wpagent_companion_live_editor_provider_finalize_validate_tree( $block['innerBlocks'] );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_companion_live_editor_provider_finalize_unique_identities( array $blocks, array &$seen = array() ) {
	foreach ( $blocks as $block ) {
		$name = is_array( $block ) && is_string( $block['blockName'] ?? null ) ? $block['blockName'] : '';
		$namespace = wpagent_native_block_namespace( $name );
		$definition = wpagent_companion_live_editor_provider_finalize_policy()[ $namespace ][ $name ] ?? null;
		if ( is_array( $definition ) ) {
			$attrs = is_array( $block['attrs'] ?? null ) ? $block['attrs'] : array();
			$attribute = $definition['attribute'];
			if ( array_key_exists( $attribute, $attrs ) ) {
				$value = $attrs[ $attribute ];
				$valid = is_string( $value ) ? wpagent_companion_live_editor_provider_finalize_valid_identity( $value ) : new WP_Error( 'wpagent_live_editor_finalize_identity', __( 'Der Provider-Identifier besitzt nicht den erwarteten Typ.', 'wp-agent-companion' ), array( 'status' => 409 ) );
				if ( is_wp_error( $valid ) || isset( $seen[ $definition['provider'] ][ $value ] ) ) {
					return is_wp_error( $valid ) ? $valid : new WP_Error( 'wpagent_live_editor_finalize_identity', __( 'Der Provider-Identifier ist im Dokument nicht eindeutig.', 'wp-agent-companion' ), array( 'status' => 409 ) );
				}
				$seen[ $definition['provider'] ][ $value ] = true;
			}
		}
		$valid = wpagent_companion_live_editor_provider_finalize_unique_identities( is_array( $block['innerBlocks'] ?? null ) ? $block['innerBlocks'] : array(), $seen );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
	}
	return true;
}

/** @param array<string,array<string,mixed>> $patches @param array<string,bool> $applied @return true|WP_Error */
function wpagent_companion_live_editor_provider_finalize_apply( array &$blocks, array $patches, array $path, array &$applied ) {
	foreach ( $blocks as $index => &$block ) {
		$current = array_merge( $path, array( $index ) );
		$key = implode( '.', $current );
		if ( isset( $patches[ $key ] ) ) {
			$patch = $patches[ $key ];
			if ( $patch['block_name'] !== ( $block['blockName'] ?? '' ) || array_key_exists( $patch['attribute'], $block['attrs'] ?? array() ) ) {
				unset( $block );
				return new WP_Error( 'wpagent_live_editor_finalize_conflict', __( 'Der Providerblock ist seit der Erfassung nicht mehr unverändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			}
			$block['attrs'][ $patch['attribute'] ] = $patch['value'];
			$applied[ $key ] = true;
		}
		$valid = wpagent_companion_live_editor_provider_finalize_apply( $block['innerBlocks'], $patches, $current, $applied );
		if ( is_wp_error( $valid ) ) {
			unset( $block );
			return $valid;
		}
	}
	unset( $block );
	return true;
}

/** @param array<string,array<string,mixed>> $patches */
function wpagent_companion_live_editor_provider_finalize_strip( array &$blocks, array $patches, array $path = array() ): void {
	foreach ( $blocks as $index => &$block ) {
		$current = array_merge( $path, array( $index ) );
		$key = implode( '.', $current );
		if ( isset( $patches[ $key ] ) ) {
			unset( $block['attrs'][ $patches[ $key ]['attribute'] ] );
		}
		wpagent_companion_live_editor_provider_finalize_strip( $block['innerBlocks'], $patches, $current );
	}
	unset( $block );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_companion_live_editor_provider_finalize_target( array $blocks, array $path, int $depth = 0 ) {
	$index = $path[ $depth ] ?? null;
	if ( ! is_int( $index ) || ! isset( $blocks[ $index ] ) || ! is_array( $blocks[ $index ] ) ) {
		return new WP_Error( 'wpagent_live_editor_finalize_conflict', __( 'Der Providerblock wurde beim Rücklesen nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( $depth + 1 === count( $path ) ) {
		return $blocks[ $index ];
	}
	$children = $blocks[ $index ]['innerBlocks'] ?? null;
	return is_array( $children )
		? wpagent_companion_live_editor_provider_finalize_target( $children, $path, $depth + 1 )
		: new WP_Error( 'wpagent_live_editor_finalize_conflict', __( 'Der Providerblock wurde beim Rücklesen nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

/** @param array<string,array<string,mixed>> $patches @return true|WP_Error */
function wpagent_companion_live_editor_provider_finalize_targets_valid( array $blocks, array $patches ) {
	foreach ( $patches as $patch ) {
		$target = wpagent_companion_live_editor_provider_finalize_target( $blocks, $patch['path'] );
		if ( is_wp_error( $target ) || ! is_array( $target ) ) {
			return new WP_Error( 'wpagent_live_editor_finalize_conflict', __( 'Der Providerblock wurde beim Rücklesen nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$name = $patch['block_name'];
		$valid = ( 'generateblocks/text' === $name && wpagent_builder_block_generateblocks_text_valid( $target ) )
			|| ( 'generateblocks/query' === $name && wpagent_builder_block_generateblocks_query_branch_valid( $target ) )
			|| ( 'generateblocks/query-page-numbers' === $name && wpagent_builder_block_generateblocks_page_numbers_valid( $target ) )
			|| ( 'kadence/progress-bar' === $name && wpagent_builder_block_kadence_progress_valid( $target ) );
		if ( ! $valid || ! hash_equals( $patch['value'], (string) ( $target['attrs'][ $patch['attribute'] ] ?? '' ) ) ) {
			return new WP_Error( 'wpagent_live_editor_finalize_document', __( 'Der Providerblock erfüllt den eng geprüften Identitätsvertrag nicht.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
	}
	return true;
}

/** @return array{post_type:string,post_status:string}|WP_Error */
function wpagent_companion_live_editor_provider_finalize_target_state( int $post_id ) {
	global $wpdb;
	if ( ! isset( $wpdb ) || ! isset( $wpdb->posts ) ) {
		return new WP_Error( 'wpagent_live_editor_finalize_target_read', __( 'Der Zielbeitrag konnte nicht sicher erneut geprüft werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$row = $wpdb->get_row( $wpdb->prepare( "SELECT post_type, post_status FROM {$wpdb->posts} WHERE ID = %d LIMIT 1", $post_id ), ARRAY_A );
	if ( ! is_array( $row ) || ! is_string( $row['post_type'] ?? null ) || ! is_string( $row['post_status'] ?? null ) ) {
		return new WP_Error( 'wpagent_live_editor_finalize_target_missing', __( 'Der Zielbeitrag existiert nicht mehr.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	if ( ! post_type_exists( $row['post_type'] ) || ! post_type_supports( $row['post_type'], 'editor' ) || ! get_post_status_object( $row['post_status'] ) || in_array( $row['post_status'], array( 'trash', 'auto-draft', 'inherit' ), true ) ) {
		return new WP_Error( 'wpagent_live_editor_finalize_target_changed', __( 'Typ oder Status des Zielbeitrags ist nicht mehr für die Finalisierung gültig.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( ! current_user_can( 'edit_post', $post_id ) ) {
		return new WP_Error( 'wpagent_live_editor_finalize_forbidden', __( 'Der Beitrag darf nicht finalisiert werden.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	return array( 'post_type' => $row['post_type'], 'post_status' => $row['post_status'] );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_companion_live_editor_finalize_provider_attributes( int $post_id, string $expected_hash, array $patches ) {
	if ( ! current_user_can( 'edit_post', $post_id ) ) {
		return new WP_Error( 'wpagent_live_editor_finalize_forbidden', __( 'Der Beitrag darf nicht finalisiert werden.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	$lock = wpagent_site_editor_acquire_lock( 'live-editor-provider-finalize', (string) $post_id );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$target = wpagent_companion_live_editor_provider_finalize_target_state( $post_id );
		if ( is_wp_error( $target ) ) {
			return $target;
		}
		$before = wpagent_native_block_document_state( $post_id );
		if ( is_wp_error( $before ) || ! hash_equals( $expected_hash, (string) ( $before['hash'] ?? '' ) ) ) {
			return new WP_Error( 'wpagent_live_editor_finalize_conflict', __( 'Das Dokument wurde seit der Bestätigung verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		if ( strlen( $before['content'] ) > WPAGENT_LIVE_EDITOR_PROVIDER_FINALIZE_MAX_BYTES ) {
			return new WP_Error( 'wpagent_live_editor_finalize_document', __( 'Das Dokument ist für die sichere Provider-Finalisierung zu groß.', 'wp-agent-companion' ), array( 'status' => 413 ) );
		}
		$blocks = parse_blocks( $before['content'] );
		if ( serialize_blocks( $blocks ) !== $before['content'] ) {
			return new WP_Error( 'wpagent_live_editor_finalize_document', __( 'Das Dokument ist nicht verlustfrei als Blockbaum darstellbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$valid = wpagent_companion_live_editor_provider_finalize_validate_tree( $blocks );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
		$unique = wpagent_companion_live_editor_provider_finalize_unique_identities( $blocks );
		if ( is_wp_error( $unique ) ) {
			return $unique;
		}
		if ( ! $patches ) {
			return array( 'post_id' => $post_id, 'changed' => false, 'verified' => true, 'revision_id' => 0, 'previous_hash' => $before['hash'], 'hash' => $before['hash'], 'finalized_count' => 0, 'providers' => array() );
		}
		$before_report = array( 'block_count' => 0, 'freeform_count' => 0, 'block_types' => array() );
		wpagent_native_block_report_walk( $blocks, $before_report );
		$patched = $blocks;
		$applied = array();
		$valid = wpagent_companion_live_editor_provider_finalize_apply( $patched, $patches, array(), $applied );
		if ( is_wp_error( $valid ) || count( $applied ) !== count( $patches ) ) {
			return is_wp_error( $valid ) ? $valid : new WP_Error( 'wpagent_live_editor_finalize_conflict', __( 'Ein erfasster Providerblock wurde nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$candidate = serialize_blocks( $patched );
		$candidate_blocks = parse_blocks( $candidate );
		$after_report = array( 'block_count' => 0, 'freeform_count' => 0, 'block_types' => array() );
		wpagent_native_block_report_walk( $candidate_blocks, $after_report );
		$without = $candidate_blocks;
		wpagent_companion_live_editor_provider_finalize_strip( $without, $patches );
		$valid = wpagent_companion_live_editor_provider_finalize_validate_tree( $candidate_blocks );
		$unique = wpagent_companion_live_editor_provider_finalize_unique_identities( $candidate_blocks );
		$targets = wpagent_companion_live_editor_provider_finalize_targets_valid( $candidate_blocks, $patches );
		if ( is_wp_error( $valid ) || is_wp_error( $unique ) || is_wp_error( $targets ) ) {
			return is_wp_error( $valid ) ? $valid : ( is_wp_error( $unique ) ? $unique : $targets );
		}
		if ( strlen( $candidate ) > WPAGENT_LIVE_EDITOR_PROVIDER_FINALIZE_MAX_BYTES || serialize_blocks( $candidate_blocks ) !== $candidate || $before_report !== $after_report || serialize_blocks( $without ) !== $before['content'] ) {
			return new WP_Error( 'wpagent_live_editor_finalize_document', __( 'Die Finalisierung würde Blockstruktur, Text oder Markup verändern.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		if ( ! wpagent_site_editor_refresh_lock( 'live-editor-provider-finalize', (string) $post_id, $lock ) ) {
			return new WP_Error( 'wpagent_live_editor_finalize_lock', __( 'Die Dokumentensperre konnte nicht verlängert werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$current_target = wpagent_companion_live_editor_provider_finalize_target_state( $post_id );
		if ( is_wp_error( $current_target ) || $target !== $current_target ) {
			return new WP_Error( 'wpagent_live_editor_finalize_conflict', __( 'Der Zielbeitrag wurde unmittelbar vor dem Speichern verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$current = wpagent_native_block_document_state( $post_id );
		if ( is_wp_error( $current ) || $current['content'] !== $before['content'] || ! hash_equals( $expected_hash, (string) ( $current['hash'] ?? '' ) ) ) {
			return new WP_Error( 'wpagent_live_editor_finalize_conflict', __( 'Das Dokument wurde unmittelbar vor dem Speichern verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$revision_id = wpagent_native_block_create_revision( $post_id, $before['content'] );
		if ( is_wp_error( $revision_id ) ) {
			return $revision_id;
		}
		if ( ! wpagent_site_editor_refresh_lock( 'live-editor-provider-finalize', (string) $post_id, $lock ) ) {
			return new WP_Error( 'wpagent_live_editor_finalize_lock', __( 'Die Dokumentensperre konnte vor dem Speichern nicht verlängert werden.', 'wp-agent-companion' ), array( 'status' => 409, 'revision_id' => $revision_id ) );
		}
		$saved = wpagent_native_block_write_if_current( $post_id, $before['content'], $candidate );
		if ( is_wp_error( $saved ) ) {
			return new WP_Error( $saved->get_error_code(), $saved->get_error_message(), array_merge( is_array( $saved->get_error_data() ) ? $saved->get_error_data() : array(), array( 'revision_id' => $revision_id ) ) );
		}
		if ( ! wpagent_site_editor_refresh_lock( 'live-editor-provider-finalize', (string) $post_id, $lock ) ) {
			return wpagent_native_block_write_failure( 'wpagent_live_editor_finalize_recovery_required', __( 'Die Finalisierung wurde gespeichert, aber die Sperre ging vor der Rückleseprüfung verloren.', 'wp-agent-companion' ), $revision_id, array( 'rolled_back' => false, 'rollback_verified' => false, 'recovery_required' => true ) );
		}
		$verify = wpagent_companion_live_editor_provider_finalize_verify_readback( $post_id, $before['content'], $candidate, $patches );
		if ( is_wp_error( $verify ) ) {
			$rollback = wpagent_native_block_rollback( $post_id, $before['content'], $candidate );
			return wpagent_native_block_write_failure( 'wpagent_live_editor_finalize_verify_failed', __( 'Die Provider-Finalisierung bestand die Rückleseprüfung nicht.', 'wp-agent-companion' ), $revision_id, $rollback );
		}
		$providers = array_values( array_unique( array_map( static fn( array $patch ): string => $patch['provider'], $patches ) ) );
		foreach ( $providers as $provider ) {
			$cache = wpagent_builder_block_refresh_cache( $provider, $post_id );
			if ( empty( $cache['verified'] ) || ! wpagent_site_editor_refresh_lock( 'live-editor-provider-finalize', (string) $post_id, $lock ) ) {
				$rollback = wpagent_companion_live_editor_provider_finalize_rollback_after_cache( $providers, $post_id, $before['content'], $candidate );
				return wpagent_native_block_write_failure( 'wpagent_live_editor_finalize_cache_failed', __( 'Der Provider-Cache konnte nicht verifiziert werden.', 'wp-agent-companion' ), $revision_id, $rollback );
			}
		}
		$verify = wpagent_companion_live_editor_provider_finalize_verify_readback( $post_id, $before['content'], $candidate, $patches );
		if ( is_wp_error( $verify ) ) {
			$rollback = wpagent_companion_live_editor_provider_finalize_rollback_after_cache( $providers, $post_id, $before['content'], $candidate );
			return wpagent_native_block_write_failure( 'wpagent_live_editor_finalize_cache_readback_failed', __( 'Der Provider-Cache veränderte den gespeicherten Dokumentzustand.', 'wp-agent-companion' ), $revision_id, $rollback );
		}
		if ( ! wpagent_site_editor_refresh_lock( 'live-editor-provider-finalize', (string) $post_id, $lock ) ) {
			return wpagent_native_block_write_failure( 'wpagent_live_editor_finalize_recovery_required', __( 'Die Finalisierung wurde gespeichert, aber die Sperre ging nach der Cache-Rückleseprüfung verloren.', 'wp-agent-companion' ), $revision_id, array( 'rolled_back' => false, 'rollback_verified' => false, 'recovery_required' => true ) );
		}
		$final_target = wpagent_companion_live_editor_provider_finalize_target_state( $post_id );
		if ( is_wp_error( $final_target ) || $target !== $final_target ) {
			return wpagent_native_block_write_failure( 'wpagent_live_editor_finalize_recovery_required', __( 'Die Finalisierung wurde gespeichert, aber Zielbeitrag oder Berechtigung konnten nach der Cache-Rückleseprüfung nicht mehr bestätigt werden.', 'wp-agent-companion' ), $revision_id, array( 'rolled_back' => false, 'rollback_verified' => false, 'recovery_required' => true ) );
		}
		if ( ! wpagent_site_editor_refresh_lock( 'live-editor-provider-finalize', (string) $post_id, $lock ) ) {
			return wpagent_native_block_write_failure( 'wpagent_live_editor_finalize_recovery_required', __( 'Die Finalisierung wurde gespeichert, aber die Sperre ging nach der abschließenden Zielprüfung verloren.', 'wp-agent-companion' ), $revision_id, array( 'rolled_back' => false, 'rollback_verified' => false, 'recovery_required' => true ) );
		}
		return array( 'post_id' => $post_id, 'changed' => true, 'verified' => true, 'revision_id' => $revision_id, 'previous_hash' => $before['hash'], 'hash' => $verify['hash'], 'finalized_count' => count( $patches ), 'providers' => $providers );
	} finally {
		wpagent_site_editor_release_lock( 'live-editor-provider-finalize', (string) $post_id, $lock );
	}
}

/** @return array{rolled_back:bool,rollback_verified:bool,recovery_required:bool} */
function wpagent_companion_live_editor_provider_finalize_rollback_after_cache( array $providers, int $post_id, string $before, string $candidate ): array {
	$rollback = wpagent_native_block_rollback( $post_id, $before, $candidate );
	if ( empty( $rollback['rolled_back'] ) ) {
		return $rollback;
	}
	foreach ( $providers as $provider ) {
		$cache = wpagent_builder_block_refresh_cache( $provider, $post_id );
		if ( empty( $cache['verified'] ) ) {
			return array( 'rolled_back' => true, 'rollback_verified' => false, 'recovery_required' => true );
		}
	}
	$raw = wpagent_native_block_raw_content( $post_id );
	$state = wpagent_native_block_document_state( $post_id );
	return ! is_wp_error( $raw ) && $raw === $before && ! is_wp_error( $state ) && $state['content'] === $before
		? $rollback
		: array( 'rolled_back' => true, 'rollback_verified' => false, 'recovery_required' => true );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_companion_live_editor_provider_finalize_verify_readback( int $post_id, string $before, string $candidate, array $patches ) {
	$raw = wpagent_native_block_raw_content( $post_id );
	$state = wpagent_native_block_document_state( $post_id );
	if ( is_wp_error( $raw ) || is_wp_error( $state ) || $raw !== $candidate || $state['content'] !== $candidate ) {
		return new WP_Error( 'wpagent_live_editor_finalize_readback', __( 'Der gespeicherte Dokumentzustand weicht vom Kandidaten ab.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$blocks = parse_blocks( $raw );
	$without = $blocks;
	wpagent_companion_live_editor_provider_finalize_strip( $without, $patches );
	$valid = wpagent_companion_live_editor_provider_finalize_validate_tree( $blocks );
	$unique = wpagent_companion_live_editor_provider_finalize_unique_identities( $blocks );
	$targets = wpagent_companion_live_editor_provider_finalize_targets_valid( $blocks, $patches );
	if ( serialize_blocks( $blocks ) !== $candidate || serialize_blocks( $without ) !== $before || is_wp_error( $valid ) || is_wp_error( $unique ) || is_wp_error( $targets ) ) {
		return new WP_Error( 'wpagent_live_editor_finalize_readback', __( 'Die Rückleseprüfung der Provider-Finalisierung ist fehlgeschlagen.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	return $state;
}

/** @return array<string,mixed> */
function wpagent_companion_live_editor_provider_finalize_error_result( WP_Error $error ): array {
	$data = is_array( $error->get_error_data() ) ? $error->get_error_data() : array();
	return array( 'code' => sanitize_key( $error->get_error_code() ), 'status' => absint( $data['status'] ?? 500 ), 'revision_id' => absint( $data['revision_id'] ?? 0 ) );
}

function wpagent_companion_live_editor_provider_finalize_execution_error_status( WP_Error $error ): string {
	$data = is_array( $error->get_error_data() ) ? $error->get_error_data() : array();
	return true === ( $data['recovery_required'] ?? false ) || in_array( $error->get_error_code(), array( 'wpagent_native_block_recovery_required', 'wpagent_live_editor_finalize_recovery_required' ), true )
		? 'recovery_required'
		: 'failed';
}

/** @return array<string,mixed>|WP_Error */
function wpagent_companion_live_editor_provider_finalize_store_outcome( string $session_id, array $running, string $status, array $result, string $error ) {
	$command_id = (string) $running['command_id'];
	$option = wpagent_companion_live_editor_command_option( $command_id );
	$completed = $running;
	$completed['status'] = $status;
	$completed['result'] = $result;
	$completed['error'] = wp_html_excerpt( wp_strip_all_tags( $error ), 2000, '' );
	$completed['completed_at'] = time();
	if ( ! wpagent_companion_live_editor_compare_and_store_record( $option, $running, $completed ) ) {
		$current = get_option( $option, null );
		if ( is_array( $current ) && in_array( (string) ( $current['status'] ?? '' ), array( 'completed', 'failed', 'denied', 'recovery_required' ), true ) ) {
			return wpagent_companion_live_editor_public_command( $current );
		}
		if ( is_array( $current ) && 'running' === (string) ( $current['status'] ?? '' ) ) {
			$recovery = $current;
			$recovery['status'] = 'recovery_required';
			$recovery['result'] = $result;
			$recovery['error'] = __( 'Die Provider-Finalisierung wurde ausgeführt, ihr Abschluss konnte aber nicht sicher gespeichert werden. Nicht erneut ausführen.', 'wp-agent-companion' );
			$recovery['completed_at'] = time();
			if ( wpagent_companion_live_editor_compare_and_store_record( $option, $current, $recovery ) ) {
				return wpagent_companion_live_editor_public_command( $recovery );
			}
		}
		return new WP_Error( 'wpagent_live_editor_finalize_recovery_write', __( 'Das Ergebnis der Provider-Finalisierung konnte nicht sicher gespeichert werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$lock = wpagent_companion_live_editor_acquire_lock( $session_id );
	if ( ! is_wp_error( $lock ) ) {
		try {
			$session = wpagent_companion_live_editor_get_session( $session_id );
			if ( ! is_wp_error( $session ) ) {
				$session['page']['ready'] = false;
				$session['page']['dirty'] = false;
				$session['page']['state_hash'] = '';
				$session = wpagent_companion_live_editor_archive_command( $session, $command_id );
				wpagent_companion_live_editor_store_record( wpagent_companion_live_editor_session_option( $session_id ), $session );
			}
		} finally {
			wpagent_companion_live_editor_release_lock( $session_id, $lock );
		}
	}
	return wpagent_companion_live_editor_public_command( $completed );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_companion_live_editor_execute_provider_finalize_command( string $session_id, string $command_id, $capture ) {
	$session = wpagent_companion_live_editor_get_session( $session_id );
	if ( is_wp_error( $session ) || ! current_user_can( 'manage_options' ) || get_current_user_id() !== absint( $session['owner_id'] ?? 0 ) || ! preg_match( '/^[a-f0-9]{24}$/D', $command_id ) ) {
		return is_wp_error( $session ) ? $session : new WP_Error( 'wpagent_live_editor_owner_required', __( 'Diese Live-Editor-Sitzung gehört zu einem anderen Administrator.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	$lock = wpagent_companion_live_editor_acquire_lock( $session_id );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$session = wpagent_companion_live_editor_get_session( $session_id );
		$option = wpagent_companion_live_editor_command_option( $command_id );
		$command = get_option( $option, null );
		if ( is_wp_error( $session ) || ! is_array( $command ) || ! hash_equals( $session_id, (string) ( $command['session_id'] ?? '' ) ) ) {
			return new WP_Error( 'wpagent_live_editor_command_missing', __( 'Der Live-Editor-Befehl wurde nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
		}
			if ( in_array( (string) ( $command['status'] ?? '' ), array( 'completed', 'failed', 'denied', 'recovery_required' ), true ) ) {
				return wpagent_companion_live_editor_public_command( $command );
			}
			if ( 'running' === (string) ( $command['status'] ?? '' ) ) {
				$recovery = $command;
				$recovery['status'] = 'recovery_required';
				$recovery['result'] = array( 'code' => 'wpagent_live_editor_finalize_recovery_required', 'status' => 500, 'revision_id' => 0 );
				$recovery['error'] = __( 'Die Provider-Finalisierung wurde bereits begonnen. Ihr Ergebnis ist nicht sicher bekannt und darf nicht erneut ausgeführt werden.', 'wp-agent-companion' );
				$recovery['completed_at'] = time();
				if ( ! wpagent_companion_live_editor_compare_and_store_record( $option, $command, $recovery ) ) {
					return new WP_Error( 'wpagent_live_editor_finalize_recovery_write', __( 'Der unklare Provider-Finalisierungszustand konnte nicht sicher gespeichert werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
				}
				$session = wpagent_companion_live_editor_archive_command( $session, $command_id );
				if ( ! wpagent_companion_live_editor_store_record( wpagent_companion_live_editor_session_option( $session_id ), $session ) ) {
					return new WP_Error( 'wpagent_live_editor_finalize_recovery_write', __( 'Der unklare Provider-Finalisierungszustand wurde markiert, aber die Sitzung konnte nicht sicher aktualisiert werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
				}
				return wpagent_companion_live_editor_public_command( $recovery );
			}
			$args = is_array( $command['args'] ?? null ) ? $command['args'] : array();
			$page = is_array( $session['page'] ?? null ) ? $session['page'] : array();
			if ( 'awaiting_confirmation' !== (string) ( $command['status'] ?? '' ) || empty( $command['requires_confirmation'] ) || ! wpagent_companion_live_editor_is_provider_finalize_action( (string) ( $command['action'] ?? '' ) ) || absint( $command['expires_at'] ?? 0 ) <= time() || absint( $args['post_id'] ?? 0 ) !== absint( $args['expected_post_id'] ?? 0 ) || absint( $args['post_id'] ?? 0 ) !== absint( $page['post_id'] ?? 0 ) || true !== ( $page['ready'] ?? false ) || true === ( $page['dirty'] ?? false ) || 'gutenberg' !== ( $page['kind'] ?? '' ) || ! hash_equals( (string) ( $args['expected_state_hash'] ?? '' ), (string) ( $page['state_hash'] ?? '' ) ) ) {
			return new WP_Error( 'wpagent_live_editor_finalize_state', __( 'Die Provider-Finalisierung ist nicht mehr an den bestätigten Editor gebunden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$access = wpagent_companion_live_editor_check_command_access( $session, $command['action'], $args );
		if ( is_wp_error( $access ) ) {
			return $access;
		}
		$running = $command;
		$running['status'] = 'running';
		$running['started_at'] = time();
		if ( ! wpagent_companion_live_editor_compare_and_store_record( $option, $command, $running ) ) {
			return new WP_Error( 'wpagent_live_editor_command_state', __( 'Der Live-Editor-Befehl wurde bereits übernommen.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
	} finally {
		wpagent_companion_live_editor_release_lock( $session_id, $lock );
	}
	$previous_user_id = get_current_user_id();
	wp_set_current_user( absint( $session['executor_id'] ?? 0 ) );
	try {
		$patches = wpagent_companion_live_editor_provider_finalize_capture( $capture, $args );
		$execution = is_wp_error( $patches ) ? $patches : wpagent_companion_live_editor_finalize_provider_attributes( absint( $args['post_id'] ), (string) $args['expected_hash'], $patches );
	} finally {
		wp_set_current_user( $previous_user_id );
	}
	if ( is_wp_error( $execution ) ) {
		return wpagent_companion_live_editor_provider_finalize_store_outcome( $session_id, $running, wpagent_companion_live_editor_provider_finalize_execution_error_status( $execution ), wpagent_companion_live_editor_provider_finalize_error_result( $execution ), $execution->get_error_message() );
	}
	return wpagent_companion_live_editor_provider_finalize_store_outcome( $session_id, $running, 'completed', $execution, '' );
}
