<?php
/** Bounded, native Spectra One 1.2.3 design snapshot import/export. */

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

const WPAGENT_SPECTRA_ONE_SNAPSHOT_SCHEMA_VERSION = 'spectra-one-design-snapshot-v1';
const WPAGENT_SPECTRA_ONE_SNAPSHOT_THEME_VERSION  = '1.2.3';
const WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES  = 20;
const WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_BYTES      = 524288;
const WPAGENT_SPECTRA_ONE_SNAPSHOT_LOCK           = 'wp_agent_spectra_one_snapshot_lock';
const WPAGENT_SPECTRA_ONE_SNAPSHOT_LOCK_TTL       = 300;

/** @return array<string,mixed> */
function wpagent_spectra_one_snapshot_identity(): array {
	$theme = wp_get_theme();
	$blog  = (int) get_current_blog_id();
	$site  = (string) get_option( 'siteurl', '' );
	$home  = (string) get_option( 'home', '' );
	$slug  = (string) get_stylesheet();
	$version = $theme->exists() ? (string) $theme->get( 'Version' ) : '';
	return array(
		'blog_id'   => $blog,
		'site_hash' => hash( 'sha256', "wpagently-spectra-one-site-v1\0{$blog}\0{$site}\0{$home}" ),
		'theme'     => array(
			'slug'    => $slug,
			'version' => $version,
			'hash'    => hash( 'sha256', "wpagently-spectra-one-theme-v1\0" . (string) get_template() . "\0{$slug}\0{$version}" ),
		),
	);
}

/** @return true|WP_Error */
function wpagent_spectra_one_snapshot_require_theme() {
	$identity = wpagent_spectra_one_snapshot_identity();
	if (
		'spectra-one' !== $identity['theme']['slug']
		|| 'spectra-one' !== (string) get_template()
		|| WPAGENT_SPECTRA_ONE_SNAPSHOT_THEME_VERSION !== $identity['theme']['version']
		|| ! function_exists( 'wp_is_block_theme' )
		|| ! wp_is_block_theme()
	) {
		return new WP_Error( 'wpagent_spectra_one_snapshot_theme', __( 'Dieser Design-Snapshot ist ausschließlich für das aktive Spectra One 1.2.3 Blocktheme verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_spectra_one_snapshot_require_capability() {
	return current_user_can( 'edit_theme_options' )
		? true
		: new WP_Error( 'wpagent_spectra_one_snapshot_forbidden', __( 'Für Spectra-One-Design-Snapshots ist edit_theme_options erforderlich.', 'wp-agent-companion' ), array( 'status' => 403 ) );
}

/** @return string|WP_Error */
function wpagent_spectra_one_snapshot_acquire_lock() {
	$token = wpagent_companion_acquire_option_lock( WPAGENT_SPECTRA_ONE_SNAPSHOT_LOCK, WPAGENT_SPECTRA_ONE_SNAPSHOT_LOCK_TTL );
	return is_string( $token ) ? $token : new WP_Error( 'wpagent_spectra_one_snapshot_busy', __( 'Ein Spectra-One-Design-Snapshot wird bereits verarbeitet. Versuche es gleich erneut.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

function wpagent_spectra_one_snapshot_release_lock( string $token ): void {
	wpagent_companion_release_option_lock( WPAGENT_SPECTRA_ONE_SNAPSHOT_LOCK, $token );
}

function wpagent_spectra_one_snapshot_signature( string $state_hash ): string {
	return hash_hmac( 'sha256', WPAGENT_SPECTRA_ONE_SNAPSHOT_SCHEMA_VERSION . "\0{$state_hash}", wp_salt( 'auth' ) );
}

/** @return true|WP_Error */
function wpagent_spectra_one_snapshot_guard( string $token, array $identity ) {
	if ( ! current_user_can( 'edit_theme_options' ) ) {
		return new WP_Error( 'wpagent_spectra_one_snapshot_capability_changed', __( 'Die erforderliche Berechtigung ging während des Imports verloren.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	}
	if ( ! wpagent_companion_refresh_option_lock( WPAGENT_SPECTRA_ONE_SNAPSHOT_LOCK, $token, WPAGENT_SPECTRA_ONE_SNAPSHOT_LOCK_TTL ) ) {
		return new WP_Error( 'wpagent_spectra_one_snapshot_lock_lost', __( 'Die Snapshot-Sperre ging während des Imports verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$current = wpagent_spectra_one_snapshot_identity();
	return $current === $identity
		? true
		: new WP_Error( 'wpagent_spectra_one_snapshot_identity_changed', __( 'Blog oder Theme haben sich während des Imports geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

/** @return array<string,mixed> */
function wpagent_spectra_one_snapshot_global_styles_schema(): array {
	return array(
		'type' => 'object', 'properties' => array(
			'colors' => array( 'type' => 'array', 'maxItems' => 60, 'items' => array( 'type' => 'object' ) ),
			'fontSizes' => array( 'type' => 'array', 'maxItems' => 60, 'items' => array( 'type' => 'object' ) ),
			'spacingSizes' => array( 'type' => 'array', 'maxItems' => 60, 'items' => array( 'type' => 'object' ) ),
			'fontFamilies' => array( 'type' => 'array', 'maxItems' => 60, 'items' => array( 'type' => 'object' ) ),
			'styles' => array( 'type' => 'object' ),
		), 'required' => array( 'colors', 'fontSizes', 'spacingSizes', 'fontFamilies', 'styles' ), 'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_spectra_one_snapshot_resource_schema( bool $part ): array {
	$properties = array(
		'slug' => array( 'type' => 'string' ), 'title' => array( 'type' => 'string' ), 'description' => array( 'type' => 'string' ),
		'container' => array( 'type' => 'string', 'enum' => array( 'none', 'group', 'main', 'header', 'footer' ) ),
		'components' => wpagent_site_editor_components_schema(), 'source_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
	);
	if ( $part ) $properties['area'] = array( 'type' => 'string' );
	return array( 'type' => 'object', 'properties' => $properties, 'required' => array_keys( $properties ), 'additionalProperties' => false );
}

/** @return array<string,mixed> */
function wpagent_spectra_one_snapshot_navigation_schema(): array {
	return array( 'type' => 'object', 'properties' => array(
		'source_id' => array( 'type' => 'integer', 'minimum' => 1 ), 'title' => array( 'type' => 'string' ),
		'items' => wpagent_site_editor_navigation_items_schema(), 'source_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
	), 'required' => array( 'source_id', 'title', 'items', 'source_hash' ), 'additionalProperties' => false );
}

function wpagent_ability_export_spectra_one_design_snapshot(): array {
	return array(
		'label' => __( 'Spectra-One-Design-Snapshot exportieren', 'wp-agent-companion' ),
		'description' => __( 'Exportiert einen begrenzten, typisierten Spectra-One-1.2.3-Snapshot aus erlaubten Global-Styles-Tokens, kanonischen eigenen Site-Editor-Ressourcen und Blocknavigationen. Rohmarkup, Theme-Dateien und nicht verlustfrei darstellbare Entitäten werden abgelehnt.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array( 'type' => 'object', 'properties' => array(
			'global_styles' => array( 'type' => 'boolean', 'default' => true ),
			'site_templates' => array( 'type' => 'array', 'maxItems' => WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES, 'items' => array( 'type' => 'string', 'maxLength' => 200 ) ),
			'template_parts' => array( 'type' => 'array', 'maxItems' => WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES, 'items' => array( 'type' => 'string', 'maxLength' => 200 ) ),
			'navigation_ids' => array( 'type' => 'array', 'maxItems' => WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES, 'items' => array( 'type' => 'integer', 'minimum' => 1 ) ),
		), 'additionalProperties' => false, 'default' => array() ),
		'output_schema' => wpagent_spectra_one_snapshot_export_schema(),
		'execute_callback' => 'wpagent_exec_export_spectra_one_design_snapshot', 'permission_callback' => 'wpagent_perm_edit_theme_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => true, 'destructive' => false, 'idempotent' => true ) ),
	);
}

/** @return array<string,mixed> */
function wpagent_spectra_one_snapshot_export_schema(): array {
	return array( 'type' => 'object', 'properties' => array(
		'schema' => array( 'type' => 'string' ), 'identity' => array( 'type' => 'object' ), 'global_styles_state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
		'snapshot' => array( 'type' => 'object' ), 'state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ), 'signature' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ), 'bytes' => array( 'type' => 'integer' ),
	), 'required' => array( 'schema', 'identity', 'global_styles_state_hash', 'snapshot', 'state_hash', 'signature', 'bytes' ), 'additionalProperties' => false );
}

function wpagent_ability_import_spectra_one_design_snapshot(): array {
	return array(
		'label' => __( 'Spectra-One-Design-Snapshot importieren', 'wp-agent-companion' ),
		'description' => __( 'Importiert bewusst ausgewählte Snapshot-Komponenten ausschließlich als neue native Ressourcen. Vor jedem Schritt prüft der Import Blog-, Theme-, Hash-, Sperr- und Berechtigungszustand; bei Unsicherheit stoppt er und meldet erforderliche Wiederherstellung.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array( 'type' => 'object', 'properties' => array(
			'snapshot' => wpagent_spectra_one_snapshot_export_schema(),
			'selection' => array( 'type' => 'object', 'properties' => array(
				'global_styles' => array( 'type' => 'boolean' ), 'expected_global_styles_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'site_templates' => array( 'type' => 'array', 'maxItems' => WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES, 'items' => array( 'type' => 'integer', 'minimum' => 0 ) ),
				'template_parts' => array( 'type' => 'array', 'maxItems' => WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES, 'items' => array( 'type' => 'integer', 'minimum' => 0 ) ),
				'navigations' => array( 'type' => 'array', 'maxItems' => WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES, 'items' => array( 'type' => 'integer', 'minimum' => 0 ) ),
			), 'required' => array( 'global_styles', 'site_templates', 'template_parts', 'navigations' ), 'additionalProperties' => false ),
		), 'required' => array( 'snapshot', 'selection' ), 'additionalProperties' => false ),
		'output_schema' => array( 'type' => 'object', 'properties' => array(
			'verified' => array( 'type' => 'boolean' ), 'state_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
			'created' => array( 'type' => 'object' ), 'global_styles' => array( 'type' => array( 'object', 'null' ) ),
		), 'required' => array( 'verified', 'state_hash', 'created', 'global_styles' ), 'additionalProperties' => false ),
		'execute_callback' => 'wpagent_exec_import_spectra_one_design_snapshot', 'permission_callback' => 'wpagent_perm_edit_theme_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_export_spectra_one_design_snapshot( $input ) {
	$capability = wpagent_spectra_one_snapshot_require_capability();
	if ( is_wp_error( $capability ) ) return $capability;
	$theme = wpagent_spectra_one_snapshot_require_theme();
	if ( is_wp_error( $theme ) ) return $theme;
	$input = is_array( $input ) ? $input : array();
	$state = wpagent_gs_state();
	if ( is_wp_error( $state ) ) return $state;
	$selection = wpagent_spectra_one_snapshot_export_selection( $input );
	if ( is_wp_error( $selection ) ) return $selection;
	$snapshot = array( 'global_styles' => null, 'site_templates' => array(), 'template_parts' => array(), 'navigations' => array() );
	if ( $selection['global_styles'] ) {
		$snapshot['global_styles'] = wpagent_spectra_one_snapshot_global_styles( $state['snapshot'] );
		if ( is_wp_error( $snapshot['global_styles'] ) ) return $snapshot['global_styles'];
	}
	foreach ( $selection['site_templates'] as $slug ) {
		$record = wpagent_spectra_one_snapshot_template_record( 'wp_template', $slug, $selection['template_parts'], $selection['navigation_ids'] );
		if ( is_wp_error( $record ) ) return $record;
		$snapshot['site_templates'][] = $record;
	}
	foreach ( $selection['template_parts'] as $slug ) {
		$record = wpagent_spectra_one_snapshot_template_record( 'wp_template_part', $slug, $selection['template_parts'], $selection['navigation_ids'] );
		if ( is_wp_error( $record ) ) return $record;
		$snapshot['template_parts'][] = $record;
	}
	foreach ( $selection['navigation_ids'] as $id ) {
		$record = wpagent_spectra_one_snapshot_navigation_record( $id );
		if ( is_wp_error( $record ) ) return $record;
		$snapshot['navigations'][] = $record;
	}
	$identity = wpagent_spectra_one_snapshot_identity();
	$payload = array( 'schema' => WPAGENT_SPECTRA_ONE_SNAPSHOT_SCHEMA_VERSION, 'identity' => $identity, 'global_styles_state_hash' => $state['state_hash'], 'snapshot' => $snapshot );
	$json = wp_json_encode( $payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
	if ( false === $json || strlen( $json ) > WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_BYTES ) return new WP_Error( 'wpagent_spectra_one_snapshot_size', __( 'Der ausgewählte Spectra-One-Snapshot überschreitet die feste Grenze von 512 KiB.', 'wp-agent-companion' ), array( 'status' => 413 ) );
	$payload['state_hash'] = hash( 'sha256', "wpagently-spectra-one-snapshot-v1\0{$json}" );
	$payload['signature']  = wpagent_spectra_one_snapshot_signature( $payload['state_hash'] );
	$payload['bytes'] = strlen( $json );
	return $payload;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_spectra_one_snapshot_export_selection( array $input ) {
	$result = array( 'global_styles' => ! array_key_exists( 'global_styles', $input ) || true === $input['global_styles'], 'site_templates' => array(), 'template_parts' => array(), 'navigation_ids' => array() );
	foreach ( array( 'site_templates', 'template_parts' ) as $key ) {
		$list = $input[ $key ] ?? array();
		if ( ! is_array( $list ) || count( $list ) > WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES ) return new WP_Error( 'wpagent_spectra_one_snapshot_selection', __( 'Die Snapshot-Auswahl ist ungültig oder zu groß.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		foreach ( $list as $value ) {
			$slug = wpagent_site_editor_slug( $value );
			if ( is_wp_error( $slug ) || isset( $result[ $key ][ $slug ] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_selection', __( 'Jeder Template-Slug darf höchstens einmal ausgewählt werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			$result[ $key ][ $slug ] = true;
		}
		$result[ $key ] = array_keys( $result[ $key ] );
	}
	$list = $input['navigation_ids'] ?? array();
	if ( ! is_array( $list ) || count( $list ) > WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES ) return new WP_Error( 'wpagent_spectra_one_snapshot_selection', __( 'Die Navigation-Auswahl ist ungültig oder zu groß.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	foreach ( $list as $value ) {
		$id = absint( $value );
		if ( $id < 1 || isset( $result['navigation_ids'][ $id ] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_selection', __( 'Jede Navigation darf höchstens einmal ausgewählt werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		$result['navigation_ids'][ $id ] = true;
	}
	$result['navigation_ids'] = array_map( 'intval', array_keys( $result['navigation_ids'] ) );
	if ( ! $result['global_styles'] && array() === $result['site_templates'] && array() === $result['template_parts'] && array() === $result['navigation_ids'] ) return new WP_Error( 'wpagent_spectra_one_snapshot_empty', __( 'Mindestens eine Spectra-One-Snapshot-Komponente muss ausgewählt werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	return $result;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_spectra_one_snapshot_global_styles( array $source ) {
	$settings = isset( $source['settings'] ) && is_array( $source['settings'] ) ? $source['settings'] : array();
	$styles = isset( $source['styles'] ) && is_array( $source['styles'] ) ? $source['styles'] : array();
	$map = array(
		'colors' => array( $settings['color']['palette'] ?? array(), 'color', 'colors' ),
		'fontSizes' => array( $settings['typography']['fontSizes'] ?? array(), 'size', 'fontSizes' ),
		'spacingSizes' => array( $settings['spacing']['spacingSizes'] ?? array(), 'size', 'spacingSizes' ),
		'fontFamilies' => array( $settings['typography']['fontFamilies'] ?? array(), 'fontFamily', 'fontFamilies' ),
	);
	$out = array();
	foreach ( $map as $key => $item ) {
		$entries = wpagent_gs_preset_entries_by_slug( $item[0] );
		$list = array();
		foreach ( $entries as $entry ) {
			if ( ! is_array( $entry ) || array_diff( array_keys( $entry ), array( 'slug', 'name', $item[1] ) ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_global_styles', __( 'Ein Global-Styles-Preset enthält nicht unterstützte Daten und wird nicht exportiert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			$list[] = $entry;
		}
		$validated = wpagent_gs_validate_preset_list( $list, $item[1], 'wpagent_gs_validate_' . ( 'color' === $item[1] ? 'color' : ( 'size' === $item[1] ? 'size' : 'font_family' ) ), $item[2] );
		if ( is_wp_error( $validated ) ) return $validated;
		$out[ $key ] = $validated;
	}
	$allowed_styles = array();
	foreach ( array( 'color', 'spacing', 'elements' ) as $key ) if ( array_key_exists( $key, $styles ) ) $allowed_styles[ $key ] = $styles[ $key ];
	$validated_styles = wpagent_gs_validate_styles( $allowed_styles );
	return is_wp_error( $validated_styles ) ? $validated_styles : array_merge( $out, array( 'styles' => $validated_styles ) );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_spectra_one_snapshot_template_record( string $type, string $slug, array $parts, array $navigation_ids ) {
	$template = wpagent_site_editor_get_template( $type, $slug );
	if ( is_wp_error( $template ) ) return $template;
	if ( 'custom' !== $template->source || ! $template->wp_id ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Nur eigene Datenbank-Templates und Template-Teile können create-only exportiert werden.', 'wp-agent-companion' ), array( 'status' => 409, 'slug' => $slug ) );
	$state = wpagent_site_editor_resource_state( $template );
	$parsed = wpagent_spectra_one_snapshot_components_from_content( wpagent_site_editor_template_raw_content( $template ), $parts, $navigation_ids );
	if ( is_wp_error( $parsed ) ) return $parsed;
	$record = array( 'slug' => $slug, 'title' => $state['title'], 'description' => $state['description'], 'container' => $parsed['container'], 'components' => $parsed['components'], 'source_hash' => $state['hash'] );
	if ( 'wp_template_part' === $type ) $record['area'] = $state['area'];
	return $record;
}

/** @return array{container:string,components:array<int,array<string,mixed>>}|WP_Error */
function wpagent_spectra_one_snapshot_components_from_content( string $raw, array $parts, array $navigation_ids ) {
	if ( '' === $raw || serialize_blocks( parse_blocks( $raw ) ) !== $raw ) return new WP_Error( 'wpagent_spectra_one_snapshot_markup', __( 'Der Snapshot akzeptiert nur kanonisches, strukturiertes WordPress-Blockmaterial.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$blocks = parse_blocks( $raw );
	$container = 'none';
	if ( 1 === count( $blocks ) && 'core/group' === ( $blocks[0]['blockName'] ?? '' ) ) {
		$attrs = is_array( $blocks[0]['attrs'] ?? null ) ? $blocks[0]['attrs'] : array();
		$tag = isset( $attrs['tagName'] ) ? (string) $attrs['tagName'] : 'div';
		if ( array_diff( array_keys( $attrs ), array( 'layout', 'tagName' ) ) || array( 'type' => 'constrained' ) !== ( $attrs['layout'] ?? null ) || ! in_array( $tag, array( 'div', 'main', 'header', 'footer' ), true ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Der Template-Container verwendet nicht unterstützte Eigenschaften.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$container = 'div' === $tag ? 'group' : $tag;
		$blocks = $blocks[0]['innerBlocks'] ?? array();
	}
	$components = array();
	foreach ( $blocks as $block ) {
		$component = wpagent_spectra_one_snapshot_component_from_block( $block, $parts, $navigation_ids );
		if ( is_wp_error( $component ) ) return $component;
		$components[] = $component;
	}
	if ( array() === $components || count( $components ) > WPAGENT_SITE_EDITOR_MAX_COMPONENTS ) return new WP_Error( 'wpagent_spectra_one_snapshot_components', __( 'Die Template-Ressource enthält keine exportierbaren Komponenten.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$rebuilt = wpagent_site_editor_build_content( $components, $container );
	return is_wp_error( $rebuilt ) || ! hash_equals( $raw, $rebuilt )
		? new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Die Template-Ressource kann nicht bytegenau aus erlaubten Komponenten wiederhergestellt werden.', 'wp-agent-companion' ), array( 'status' => 409 ) )
		: array( 'container' => $container, 'components' => $components );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_spectra_one_snapshot_component_from_block( array $block, array $parts, array $navigation_ids ) {
	$name = (string) ( $block['blockName'] ?? '' ); $attrs = is_array( $block['attrs'] ?? null ) ? $block['attrs'] : array();
	if ( ! empty( $block['innerBlocks'] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Verschachtelte freie Blockstrukturen werden nicht exportiert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$allowed = array(); $out = array();
	if ( 'core/site-title' === $name || 'core/post-title' === $name ) {
		$allowed = array( 'level', 'isLink' ); $out = array( 'type' => 'core/site-title' === $name ? 'site-title' : 'post-title', 'level' => absint( $attrs['level'] ?? ( 'core/site-title' === $name ? 2 : 1 ) ) );
		if ( isset( $attrs['isLink'] ) ) $out['is_link'] = (bool) $attrs['isLink'];
	} elseif ( 'core/site-logo' === $name ) { $allowed = array( 'width' ); $out = array( 'type' => 'site-logo' ); if ( isset( $attrs['width'] ) ) $out['width'] = absint( $attrs['width'] );
	} elseif ( 'core/template-part' === $name ) { $allowed = array( 'slug', 'theme' ); $slug = wpagent_site_editor_slug( $attrs['slug'] ?? '' ); if ( is_wp_error( $slug ) || ! in_array( $slug, $parts, true ) || (string) ( $attrs['theme'] ?? '' ) !== get_stylesheet() ) return new WP_Error( 'wpagent_spectra_one_snapshot_reference', __( 'Jede Template-Teil-Referenz muss im selben Snapshot ausgewählt sein.', 'wp-agent-companion' ), array( 'status' => 409 ) ); $out = array( 'type' => 'template-part', 'slug' => $slug );
	} elseif ( 'core/navigation' === $name ) { $allowed = array( 'ref' ); $id = absint( $attrs['ref'] ?? 0 ); if ( $id < 1 || ! in_array( $id, $navigation_ids, true ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_reference', __( 'Jede Navigation-Referenz muss im selben Snapshot ausgewählt sein.', 'wp-agent-companion' ), array( 'status' => 409 ) ); $out = array( 'type' => 'navigation', 'navigation_id' => $id );
	} elseif ( 'core/post-featured-image' === $name || 'core/post-excerpt' === $name || 'core/post-date' === $name || 'core/post-author-name' === $name ) { $allowed = array( 'isLink' ); $out = array( 'type' => str_replace( 'core/', '', $name ) ); if ( isset( $attrs['isLink'] ) ) $out['is_link'] = (bool) $attrs['isLink'];
	} elseif ( 'core/post-content' === $name ) { $allowed = array( 'layout' ); if ( array( 'type' => 'constrained' ) !== ( $attrs['layout'] ?? null ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'post-content enthält nicht unterstützte Attribute.', 'wp-agent-companion' ), array( 'status' => 409 ) ); $out = array( 'type' => 'post-content' );
	} elseif ( 'core/post-terms' === $name ) { $allowed = array( 'term' ); $out = array( 'type' => 'post-terms', 'taxonomy' => (string) ( $attrs['term'] ?? '' ) );
	} elseif ( 'core/query-title' === $name ) { $allowed = array( 'type', 'showPrefix' ); if ( false !== ( $attrs['showPrefix'] ?? false ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'query-title enthält nicht unterstützte Attribute.', 'wp-agent-companion' ), array( 'status' => 409 ) ); $out = array( 'type' => 'query-title', 'query_title_type' => (string) ( $attrs['type'] ?? 'archive' ) );
	} elseif ( 'core/separator' === $name ) { $out = array( 'type' => 'separator' );
	} elseif ( 'core/spacer' === $name ) { $allowed = array( 'height' ); if ( ! preg_match( '/^(\d{1,3})px$/', (string) ( $attrs['height'] ?? '' ), $match ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'spacer enthält keine unterstützte Pixelhöhe.', 'wp-agent-companion' ), array( 'status' => 409 ) ); $out = array( 'type' => 'spacer', 'height' => (int) $match[1] );
	} else return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Der Snapshot unterstützt diesen Blocktyp nicht.', 'wp-agent-companion' ), array( 'status' => 409, 'block' => $name ) );
	return array_diff( array_keys( $attrs ), $allowed ) ? new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Ein Block enthält nicht unterstützte Attribute.', 'wp-agent-companion' ), array( 'status' => 409, 'block' => $name ) ) : $out;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_spectra_one_snapshot_navigation_record( int $id ) {
	$state = wpagent_site_editor_navigation_state( $id );
	if ( is_wp_error( $state ) || 'publish' !== $state['status'] ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Nur veröffentlichte Blocknavigationen können exportiert werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$items = wpagent_spectra_one_snapshot_navigation_items_from_blocks( parse_blocks( (string) get_post( $id )->post_content ) );
	if ( is_wp_error( $items ) ) return $items;
	$rebuilt = wpagent_site_editor_build_navigation_content( $items );
	if ( is_wp_error( $rebuilt ) || ! hash_equals( (string) get_post( $id )->post_content, $rebuilt ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Die Navigation kann nicht bytegenau aus erlaubten Einträgen wiederhergestellt werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	return array( 'source_id' => $id, 'title' => $state['title'], 'items' => $items, 'source_hash' => $state['hash'] );
}

/** @return array<int,array<string,mixed>>|WP_Error */
function wpagent_spectra_one_snapshot_navigation_items_from_blocks( array $blocks, int $depth = 0 ) {
	if ( $depth > 2 || array() === $blocks ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Die Navigation ist leer oder zu tief verschachtelt.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$items = array();
	foreach ( $blocks as $block ) {
		$name = (string) ( $block['blockName'] ?? '' ); $attrs = is_array( $block['attrs'] ?? null ) ? $block['attrs'] : array(); $submenu = 'core/navigation-submenu' === $name;
		if ( ! in_array( $name, array( 'core/navigation-link', 'core/navigation-submenu' ), true ) || array_diff( array_keys( $attrs ), array( 'label', 'url', 'kind', 'type', 'id', 'opensInNewTab', 'description' ) ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Die Navigation enthält einen nicht unterstützten Eintrag.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$type = 'custom'; $kind = (string) ( $attrs['kind'] ?? '' ); $entity = (string) ( $attrs['type'] ?? '' );
		if ( 'post-type' === $kind && in_array( $entity, array( 'page', 'post' ), true ) ) $type = $entity;
		elseif ( 'taxonomy' === $kind && in_array( $entity, array( 'category', 'post_tag' ), true ) ) $type = 'post_tag' === $entity ? 'tag' : 'category';
		elseif ( 'custom' !== $kind || 'custom' !== $entity ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Die Navigation enthält einen unbekannten Entitätstyp.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$item = array( 'type' => $submenu ? 'submenu' : $type, 'label' => (string) ( $attrs['label'] ?? '' ), 'url' => (string) ( $attrs['url'] ?? '' ) );
		if ( ! $submenu && 'custom' !== $type ) { $item['id'] = absint( $attrs['id'] ?? 0 ); unset( $item['url'] ); }
		if ( ! empty( $attrs['opensInNewTab'] ) ) $item['opens_in_new_tab'] = true;
		if ( isset( $attrs['description'] ) ) $item['description'] = (string) $attrs['description'];
		if ( $submenu ) { $children = wpagent_spectra_one_snapshot_navigation_items_from_blocks( $block['innerBlocks'] ?? array(), $depth + 1 ); if ( is_wp_error( $children ) ) return $children; $item['items'] = $children; }
		elseif ( ! empty( $block['innerBlocks'] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Ein Navigationseintrag enthält unerwartete Kindblöcke.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$items[] = $item;
	}
	return $items;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_import_spectra_one_design_snapshot( $input ) {
	$capability = wpagent_spectra_one_snapshot_require_capability();
	if ( is_wp_error( $capability ) ) return $capability;
	$theme = wpagent_spectra_one_snapshot_require_theme();
	if ( is_wp_error( $theme ) ) return $theme;
	$input = is_array( $input ) ? $input : array();
	$plan = wpagent_spectra_one_snapshot_import_plan( $input );
	if ( is_wp_error( $plan ) ) return $plan;
	$lock = wpagent_spectra_one_snapshot_acquire_lock();
	if ( is_wp_error( $lock ) ) return $lock;
	$identity = wpagent_spectra_one_snapshot_identity();
	$global_lock = null;
	$applied = array();
	try {
		$guard = wpagent_spectra_one_snapshot_guard( $lock, $identity );
		if ( is_wp_error( $guard ) ) return $guard;
		$preflight = wpagent_spectra_one_snapshot_preflight_targets( $plan );
		if ( is_wp_error( $preflight ) ) return $preflight;
		if ( $plan['selection']['global_styles'] ) {
			$global_lock = wpagent_gs_acquire_lock();
			if ( is_wp_error( $global_lock ) ) return $global_lock;
			$guard = wpagent_spectra_one_snapshot_guard( $lock, $identity );
			if ( is_wp_error( $guard ) ) return $guard;
			$before = wpagent_gs_state();
			if ( is_wp_error( $before ) ) return $before;
			if ( ! hash_equals( $before['state_hash'], $plan['selection']['expected_global_styles_hash'] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_global_styles_conflict', __( 'Die Global Styles wurden seit der Zielauswahl geändert.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $before['state_hash'] ) );
			$result = wpagent_exec_set_global_styles_locked( array_merge( $plan['snapshot']['global_styles'], array( 'expected_hash' => $before['state_hash'], 'replace' => false ) ), $before['state_hash'] );
			if ( is_wp_error( $result ) || ! is_array( $result ) || ! is_string( $result['state_hash'] ?? null ) ) {
				$failure = is_wp_error( $result ) ? $result : new WP_Error( 'wpagent_spectra_one_snapshot_global_styles_verify', __( 'Die Global Styles wurden nicht vollständig verifiziert.', 'wp-agent-companion' ), array( 'status' => 500 ) );
				$fresh   = wpagent_gs_state();
				if ( ! is_wp_error( $fresh ) && hash_equals( $before['state_hash'], $fresh['state_hash'] ) ) return $failure;
				$applied[] = array( 'kind' => 'global_styles', 'before' => $before, 'after_hash' => is_wp_error( $fresh ) ? '' : $fresh['state_hash'] );
				return wpagent_spectra_one_snapshot_fail_after_mutation( $failure, $applied, $lock, $identity, $global_lock );
			}
			$applied[] = array( 'kind' => 'global_styles', 'before' => $before, 'after_hash' => $result['state_hash'] );
			$guard = wpagent_spectra_one_snapshot_guard( $lock, $identity );
			if ( is_wp_error( $guard ) ) return wpagent_spectra_one_snapshot_fail_after_mutation( $guard, $applied, $lock, $identity, $global_lock );
		}
		$navigation_map = array();
		foreach ( $plan['navigations'] as $record ) {
			$guard = wpagent_spectra_one_snapshot_guard( $lock, $identity );
			if ( is_wp_error( $guard ) ) return wpagent_spectra_one_snapshot_fail_after_mutation( $guard, $applied, $lock, $identity, $global_lock );
			$result = wpagent_exec_create_block_navigation( array( 'title' => $record['title'], 'items' => $record['items'] ) );
			if ( is_wp_error( $result ) ) return wpagent_spectra_one_snapshot_fail_after_mutation( $result, $applied, $lock, $identity, $global_lock );
			$state = $result['navigation'] ?? null;
			if ( ! is_array( $state ) || empty( $result['verified'] ) || ! isset( $state['id'], $state['hash'] ) ) return wpagent_spectra_one_snapshot_fail_after_mutation( new WP_Error( 'wpagent_spectra_one_snapshot_navigation_verify', __( 'Die importierte Navigation wurde nicht vollständig verifiziert.', 'wp-agent-companion' ), array( 'status' => 500 ) ), $applied, $lock, $identity, $global_lock );
			$navigation_map[ (int) $record['source_id'] ] = (int) $state['id'];
			$applied[] = array( 'kind' => 'navigation', 'id' => (int) $state['id'], 'hash' => (string) $state['hash'] );
			$guard = wpagent_spectra_one_snapshot_guard( $lock, $identity );
			if ( is_wp_error( $guard ) ) return wpagent_spectra_one_snapshot_fail_after_mutation( $guard, $applied, $lock, $identity, $global_lock );
		}
		$created = array( 'site_templates' => array(), 'template_parts' => array(), 'navigations' => array() );
		foreach ( $plan['template_parts'] as $record ) {
			$result = wpagent_spectra_one_snapshot_create_template( 'wp_template_part', $record, $navigation_map, $lock, $identity );
			if ( is_wp_error( $result ) ) return wpagent_spectra_one_snapshot_fail_after_mutation( $result, $applied, $lock, $identity, $global_lock );
			$applied[] = array( 'kind' => 'template', 'type' => 'wp_template_part', 'slug' => $result['resource']['slug'], 'hash' => $result['resource']['hash'] );
			$created['template_parts'][] = $result['resource'];
		}
		foreach ( $plan['site_templates'] as $record ) {
			$result = wpagent_spectra_one_snapshot_create_template( 'wp_template', $record, $navigation_map, $lock, $identity );
			if ( is_wp_error( $result ) ) return wpagent_spectra_one_snapshot_fail_after_mutation( $result, $applied, $lock, $identity, $global_lock );
			$applied[] = array( 'kind' => 'template', 'type' => 'wp_template', 'slug' => $result['resource']['slug'], 'hash' => $result['resource']['hash'] );
			$created['site_templates'][] = $result['resource'];
		}
		foreach ( $applied as $record ) if ( 'navigation' === $record['kind'] ) $created['navigations'][] = wpagent_site_editor_navigation_state( $record['id'] );
		if ( in_array( true, array_map( 'is_wp_error', $created['navigations'] ), true ) ) return wpagent_spectra_one_snapshot_fail_after_mutation( new WP_Error( 'wpagent_spectra_one_snapshot_navigation_verify', __( 'Eine importierte Navigation konnte abschließend nicht gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) ), $applied, $lock, $identity, $global_lock );
		$guard = wpagent_spectra_one_snapshot_guard( $lock, $identity );
		if ( is_wp_error( $guard ) ) return wpagent_spectra_one_snapshot_fail_after_mutation( $guard, $applied, $lock, $identity, $global_lock );
		$global = $plan['selection']['global_styles'] ? wpagent_gs_state() : null;
		if ( is_wp_error( $global ) ) return wpagent_spectra_one_snapshot_fail_after_mutation( $global, $applied, $lock, $identity, $global_lock );
		return array( 'verified' => true, 'state_hash' => wpagent_spectra_one_snapshot_import_hash( $created, $global ), 'created' => $created, 'global_styles' => $global ? array( 'state_hash' => $global['state_hash'], 'theme' => $global['theme'] ) : null );
	} finally {
		if ( is_string( $global_lock ) ) wpagent_gs_release_lock( $global_lock );
		wpagent_spectra_one_snapshot_release_lock( $lock );
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_spectra_one_snapshot_import_plan( array $input ) {
	$snapshot = $input['snapshot'] ?? null; $selection = $input['selection'] ?? null;
	if ( ! is_array( $snapshot ) || ! is_array( $selection ) || array_diff( array_keys( $snapshot ), array( 'schema', 'identity', 'global_styles_state_hash', 'snapshot', 'state_hash', 'signature', 'bytes' ) ) || array_diff( array_keys( $selection ), array( 'global_styles', 'expected_global_styles_hash', 'site_templates', 'template_parts', 'navigations' ) ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_invalid', __( 'Das Snapshot-Format oder die Importauswahl ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	if ( WPAGENT_SPECTRA_ONE_SNAPSHOT_SCHEMA_VERSION !== ( $snapshot['schema'] ?? '' ) || ! is_array( $snapshot['identity'] ?? null ) || ! is_array( $snapshot['snapshot'] ?? null ) || ! is_string( $snapshot['state_hash'] ?? null ) || ! is_string( $snapshot['signature'] ?? null ) || ! is_int( $snapshot['bytes'] ?? null ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_invalid', __( 'Das Snapshot-Format ist unvollständig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$unsigned = array( 'schema' => $snapshot['schema'], 'identity' => $snapshot['identity'], 'global_styles_state_hash' => $snapshot['global_styles_state_hash'] ?? '', 'snapshot' => $snapshot['snapshot'] );
	$json = wp_json_encode( $unsigned, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
	if ( false === $json || strlen( $json ) !== $snapshot['bytes'] || ! hash_equals( hash( 'sha256', "wpagently-spectra-one-snapshot-v1\0{$json}" ), $snapshot['state_hash'] ) || ! hash_equals( wpagent_spectra_one_snapshot_signature( $snapshot['state_hash'] ), $snapshot['signature'] ) || strlen( $json ) > WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_BYTES ) return new WP_Error( 'wpagent_spectra_one_snapshot_integrity', __( 'Der Snapshot ist beschädigt, verändert oder zu groß.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	if ( $snapshot['identity'] !== wpagent_spectra_one_snapshot_identity() ) return new WP_Error( 'wpagent_spectra_one_snapshot_identity', __( 'Der Snapshot stammt nicht aus demselben Blog, derselben Site oder demselben Spectra-One-Theme.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$body = $snapshot['snapshot'];
	if ( array_diff( array_keys( $body ), array( 'global_styles', 'site_templates', 'template_parts', 'navigations' ) ) || ! is_array( $body['site_templates'] ?? null ) || ! is_array( $body['template_parts'] ?? null ) || ! is_array( $body['navigations'] ?? null ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_invalid', __( 'Die Snapshot-Komponenten sind ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	foreach ( array( 'site_templates', 'template_parts', 'navigations' ) as $key ) if ( count( $body[ $key ] ) > WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES ) return new WP_Error( 'wpagent_spectra_one_snapshot_size', __( 'Der Snapshot enthält zu viele Ressourcen.', 'wp-agent-companion' ), array( 'status' => 413 ) );
	$plan = array( 'snapshot' => $body, 'selection' => $selection, 'site_templates' => array(), 'template_parts' => array(), 'navigations' => array() );
	$plan['navigations'] = wpagent_spectra_one_snapshot_selected_records( $body['navigations'], $selection['navigations'] ?? array(), 'navigation' );
	$plan['template_parts'] = wpagent_spectra_one_snapshot_selected_records( $body['template_parts'], $selection['template_parts'] ?? array(), 'part' );
	$plan['site_templates'] = wpagent_spectra_one_snapshot_selected_records( $body['site_templates'], $selection['site_templates'] ?? array(), 'template' );
	foreach ( array( 'navigations', 'template_parts', 'site_templates' ) as $key ) if ( is_wp_error( $plan[ $key ] ) ) return $plan[ $key ];
	if ( ! is_bool( $selection['global_styles'] ?? null ) || ( $selection['global_styles'] && ( ! is_string( $selection['expected_global_styles_hash'] ?? null ) || ! preg_match( '/^[a-f0-9]{64}$/', $selection['expected_global_styles_hash'] ) || ! is_array( $body['global_styles'] ?? null ) ) ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_selection', __( 'Die Global-Styles-Auswahl benötigt einen frischen vollständigen Ziel-Hash.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	if ( $selection['global_styles'] ) { $global = wpagent_spectra_one_snapshot_validate_global_styles( $body['global_styles'] ); if ( is_wp_error( $global ) ) return $global; $plan['snapshot']['global_styles'] = $global; }
	$valid = wpagent_spectra_one_snapshot_validate_records( $plan );
	if ( is_wp_error( $valid ) ) return $valid;
	return $plan;
}

/** @return array<int,array<string,mixed>>|WP_Error */
function wpagent_spectra_one_snapshot_selected_records( array $records, $indexes, string $kind ) {
	if ( ! is_array( $indexes ) || ! array_is_list( $indexes ) || count( $indexes ) > WPAGENT_SPECTRA_ONE_SNAPSHOT_MAX_RESOURCES ) return new WP_Error( 'wpagent_spectra_one_snapshot_selection', __( 'Die ausgewählten Snapshot-Indizes sind ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$out = array();
	foreach ( $indexes as $index ) { if ( ! is_int( $index ) || $index < 0 || ! array_key_exists( $index, $records ) || isset( $out[ $index ] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_selection', __( 'Ein Snapshot-Index ist ungültig oder doppelt.', 'wp-agent-companion' ), array( 'status' => 400 ) ); $out[ $index ] = $records[ $index ]; }
	return array_values( $out );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_spectra_one_snapshot_validate_global_styles( array $value ) {
	if ( array_diff( array_keys( $value ), array( 'colors', 'fontSizes', 'spacingSizes', 'fontFamilies', 'styles' ) ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_global_styles', __( 'Der Global-Styles-Snapshot enthält unbekannte Daten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$map = array( 'colors' => array( 'color', 'wpagent_gs_validate_color' ), 'fontSizes' => array( 'size', 'wpagent_gs_validate_size' ), 'spacingSizes' => array( 'size', 'wpagent_gs_validate_size' ), 'fontFamilies' => array( 'fontFamily', 'wpagent_gs_validate_font_family' ) );
	$out = array();
	foreach ( $map as $key => $config ) { $result = wpagent_gs_validate_preset_list( $value[ $key ] ?? null, $config[0], $config[1], $key ); if ( is_wp_error( $result ) ) return $result; $out[ $key ] = $result; }
	$styles = wpagent_gs_validate_styles( $value['styles'] ?? null );
	return is_wp_error( $styles ) ? $styles : array_merge( $out, array( 'styles' => $styles ) );
}

/** @return true|WP_Error */
function wpagent_spectra_one_snapshot_validate_records( array &$plan ) {
	$selected_parts = array(); $selected_navigation = array();
	foreach ( $plan['navigations'] as $record ) {
		if ( ! is_array( $record ) || array_diff( array_keys( $record ), array( 'source_id', 'title', 'items', 'source_hash' ) ) || ! is_int( $record['source_id'] ?? null ) || isset( $selected_navigation[ $record['source_id'] ] ) || ! is_string( $record['source_hash'] ?? null ) || ! preg_match( '/^[a-f0-9]{64}$/', $record['source_hash'] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_invalid', __( 'Ein Navigation-Snapshot ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		$title = wpagent_site_editor_plain_text( $record['title'] ?? '', 'title', 200 ); $items = wpagent_site_editor_build_navigation_content( $record['items'] ?? null );
		if ( is_wp_error( $title ) || is_wp_error( $items ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_invalid', __( 'Ein Navigation-Snapshot enthält ungültige Einträge.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		$record['title'] = $title; $selected_navigation[ $record['source_id'] ] = true;
	}
	foreach ( array( 'template_parts' => 'wp_template_part', 'site_templates' => 'wp_template' ) as $key => $type ) {
		foreach ( $plan[ $key ] as &$record ) {
			$valid = wpagent_spectra_one_snapshot_validate_resource_record( $record, 'wp_template_part' === $type );
			if ( is_wp_error( $valid ) ) return $valid;
			if ( 'wp_template_part' === $type ) { if ( isset( $selected_parts[ $record['slug'] ] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_invalid', __( 'Ein Template-Teil-Slug ist doppelt vorhanden.', 'wp-agent-companion' ), array( 'status' => 400 ) ); $selected_parts[ $record['slug'] ] = true; }
		}
		unset( $record );
	}
	foreach ( array( 'template_parts', 'site_templates' ) as $key ) foreach ( $plan[ $key ] as $record ) foreach ( $record['components'] as $component ) {
		if ( 'template-part' === $component['type'] && ! isset( $selected_parts[ $component['slug'] ] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_reference', __( 'Jede Template-Teil-Referenz muss ebenfalls importiert werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		if ( 'navigation' === $component['type'] && ! isset( $selected_navigation[ $component['navigation_id'] ] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_reference', __( 'Jede Navigation-Referenz muss ebenfalls importiert werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	};
	$ordered = wpagent_spectra_one_snapshot_order_parts( $plan['template_parts'] );
	if ( is_wp_error( $ordered ) ) return $ordered;
	$plan['template_parts'] = $ordered;
	return true;
}

/** @return true|WP_Error */
function wpagent_spectra_one_snapshot_validate_resource_record( array &$record, bool $part ) {
	$keys = array( 'slug', 'title', 'description', 'container', 'components', 'source_hash' ); if ( $part ) $keys[] = 'area';
	if ( array_diff( array_keys( $record ), $keys ) || ! is_string( $record['source_hash'] ?? null ) || ! preg_match( '/^[a-f0-9]{64}$/', $record['source_hash'] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_invalid', __( 'Eine Template-Ressource ist unvollständig oder enthält unbekannte Daten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$slug = wpagent_site_editor_slug( $record['slug'] ?? '' ); $title = wpagent_site_editor_plain_text( $record['title'] ?? '', 'title', 200 ); $description = wpagent_site_editor_plain_text( $record['description'] ?? '', 'description', 1000, true );
	if ( is_wp_error( $slug ) || is_wp_error( $title ) || is_wp_error( $description ) || ! in_array( $record['container'] ?? '', array( 'none', 'group', 'main', 'header', 'footer' ), true ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_invalid', __( 'Eine Template-Ressource enthält ungültige Metadaten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	if ( $part ) { $area = wpagent_site_editor_validate_area( $record['area'] ?? '' ); if ( is_wp_error( $area ) ) return $area; $record['area'] = $area; }
	$components = wpagent_spectra_one_snapshot_validate_components( $record['components'] ?? null );
	if ( is_wp_error( $components ) ) return $components;
	$record['components'] = $components;
	// Import preflight must not manufacture target references. It uses the
	// native structural builder without resolving references, then the regular
	// native writer resolves and verifies each remapped reference after creation.
	$content = wpagent_site_editor_build_content( $record['components'], $record['container'], false );
	if ( is_wp_error( $content ) || strlen( $content ) > WPAGENT_SITE_EDITOR_MAX_CONTENT_BYTES ) return new WP_Error( 'wpagent_spectra_one_snapshot_invalid', __( 'Eine Template-Ressource enthält nicht unterstützte Komponenten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$record['slug'] = $slug; $record['title'] = $title; $record['description'] = $description;
	return true;
}

/** @return array<int,array<string,mixed>>|WP_Error */
function wpagent_spectra_one_snapshot_validate_components( $components ) {
	if ( ! is_array( $components ) || ! array_is_list( $components ) || array() === $components || count( $components ) > WPAGENT_SITE_EDITOR_MAX_COMPONENTS ) return new WP_Error( 'wpagent_spectra_one_snapshot_invalid', __( 'Eine Template-Ressource enthält keine begrenzte Komponentenliste.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$keys = array(
		'site-title' => array( 'type', 'level', 'is_link' ), 'post-title' => array( 'type', 'level', 'is_link' ), 'site-logo' => array( 'type', 'width' ),
		'template-part' => array( 'type', 'slug' ), 'navigation' => array( 'type', 'navigation_id' ),
		'post-featured-image' => array( 'type', 'is_link' ), 'post-excerpt' => array( 'type', 'is_link' ), 'post-date' => array( 'type', 'is_link' ), 'post-author-name' => array( 'type', 'is_link' ),
		'post-content' => array( 'type' ), 'post-terms' => array( 'type', 'taxonomy' ), 'query-title' => array( 'type', 'query_title_type' ),
		'separator' => array( 'type' ), 'spacer' => array( 'type', 'height' ),
	);
	foreach ( $components as $component ) {
		$type = is_array( $component ) && is_string( $component['type'] ?? null ) ? $component['type'] : '';
		if ( ! isset( $keys[ $type ] ) || array_diff( array_keys( $component ), $keys[ $type ] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_unsupported_entity', __( 'Ein Snapshot enthält eine unbekannte oder nicht erlaubte Template-Komponente.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return $components;
}

/** @return array<int,array<string,mixed>>|WP_Error */
function wpagent_spectra_one_snapshot_order_parts( array $parts ) {
	$by_slug = array(); foreach ( $parts as $part ) $by_slug[ $part['slug'] ] = $part;
	$ordered = array(); $visiting = array(); $visited = array();
	$visit = static function ( string $slug ) use ( &$visit, &$by_slug, &$ordered, &$visiting, &$visited ) {
		if ( isset( $visited[ $slug ] ) ) return true;
		if ( isset( $visiting[ $slug ] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_cycle', __( 'Die importierten Template-Teile enthalten einen Zyklus.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$visiting[ $slug ] = true;
		foreach ( $by_slug[ $slug ]['components'] as $component ) if ( 'template-part' === $component['type'] ) { $result = $visit( $component['slug'] ); if ( is_wp_error( $result ) ) return $result; }
		unset( $visiting[ $slug ] ); $visited[ $slug ] = true; $ordered[] = $by_slug[ $slug ]; return true;
	};
	foreach ( array_keys( $by_slug ) as $slug ) { $result = $visit( $slug ); if ( is_wp_error( $result ) ) return $result; }
	return $ordered;
}

/** @return true|WP_Error */
function wpagent_spectra_one_snapshot_preflight_targets( array $plan ) {
	foreach ( array( 'template_parts' => 'wp_template_part', 'site_templates' => 'wp_template' ) as $key => $type ) foreach ( $plan[ $key ] as $record ) if ( get_block_template( wpagent_site_editor_template_id( $record['slug'] ), $type ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_target_exists', __( 'Eine create-only Zielressource existiert bereits.', 'wp-agent-companion' ), array( 'status' => 409, 'slug' => $record['slug'] ) );
	foreach ( $plan['navigations'] as $record ) if ( wpagent_spectra_one_snapshot_navigation_title_exists( $record['title'] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_target_exists', __( 'Eine create-only Zielnavigation existiert bereits.', 'wp-agent-companion' ), array( 'status' => 409, 'title' => $record['title'] ) );
	return true;
}

function wpagent_spectra_one_snapshot_navigation_title_exists( string $title ): bool {
	global $wpdb;
	$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(1) FROM {$wpdb->posts} WHERE post_type = %s AND post_status = %s AND post_title = %s", 'wp_navigation', 'publish', $title ) ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
	return (int) $count > 0;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_spectra_one_snapshot_create_template( string $type, array $record, array $navigation_map, string $lock, array $identity ) {
	$guard = wpagent_spectra_one_snapshot_guard( $lock, $identity );
	if ( is_wp_error( $guard ) ) return $guard;
	$components = wpagent_spectra_one_snapshot_remap_components( $record['components'], $navigation_map );
	if ( is_wp_error( $components ) ) return $components;
	$input = array( 'slug' => $record['slug'], 'title' => $record['title'], 'description' => $record['description'], 'container' => $record['container'], 'components' => $components );
	if ( 'wp_template_part' === $type ) $input['area'] = $record['area'];
	$result = 'wp_template_part' === $type ? wpagent_exec_create_template_part( $input ) : wpagent_exec_create_site_template( $input );
	if ( is_wp_error( $result ) || empty( $result['verified'] ) || ! is_array( $result['resource'] ?? null ) ) return is_wp_error( $result ) ? $result : new WP_Error( 'wpagent_spectra_one_snapshot_template_verify', __( 'Die importierte Template-Ressource wurde nicht verifiziert.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	$guard = wpagent_spectra_one_snapshot_guard( $lock, $identity );
	return is_wp_error( $guard ) ? $guard : $result;
}

/** @return array<int,array<string,mixed>>|WP_Error */
function wpagent_spectra_one_snapshot_remap_components( array $components, array $navigation_map ) {
	$out = array();
	foreach ( $components as $component ) {
		if ( 'navigation' === $component['type'] ) { $source = (int) $component['navigation_id']; if ( ! isset( $navigation_map[ $source ] ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_reference', __( 'Eine Navigation-Referenz konnte nicht auf die neue Ressource abgebildet werden.', 'wp-agent-companion' ), array( 'status' => 409 ) ); $component['navigation_id'] = $navigation_map[ $source ]; }
		$out[] = $component;
	}
	return $out;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_spectra_one_snapshot_fail_after_mutation( WP_Error $failure, array $applied, string $lock, array $identity, $global_lock ) {
	if ( array() === $applied ) return $failure;
	foreach ( array_reverse( $applied ) as $record ) {
		$guard = wpagent_spectra_one_snapshot_guard( $lock, $identity );
		if ( is_wp_error( $guard ) || ! wpagent_spectra_one_snapshot_rollback_record( $record, is_string( $global_lock ) ? $global_lock : '' ) ) return new WP_Error( 'wpagent_spectra_one_snapshot_recovery_required', __( 'Der Import wurde unterbrochen. Der Zustand ist unsicher und darf nicht automatisch weiter verändert werden. Prüfe die bereits angelegten Ressourcen manuell.', 'wp-agent-companion' ), array( 'status' => 500, 'recovery_required' => true, 'original_error' => $failure->get_error_code() ) );
	}
	return new WP_Error( 'wpagent_spectra_one_snapshot_import_rolled_back', __( 'Der Import wurde nicht vollständig verifiziert. Alle zuvor verifizierten Änderungen wurden zurückgerollt.', 'wp-agent-companion' ), array( 'status' => 500, 'rollback_verified' => true, 'original_error' => $failure->get_error_code() ) );
}

function wpagent_spectra_one_snapshot_rollback_record( array $record, string $global_lock ): bool {
	if ( ! current_user_can( 'edit_theme_options' ) ) return false;
	if ( 'global_styles' === $record['kind'] ) {
		if ( '' === $global_lock || ! wpagent_companion_option_lock_is_owned( WPAGENT_GLOBAL_STYLES_LOCK_OPTION, $global_lock ) ) return false;
		$current = wpagent_gs_state();
		if ( is_wp_error( $current ) || ! hash_equals( $record['after_hash'], $current['state_hash'] ) || ! wpagent_gs_restore_snapshot( $record['before'] ) ) return false;
		$fresh = wpagent_gs_state(); return ! is_wp_error( $fresh ) && hash_equals( $record['before']['state_hash'], $fresh['state_hash'] );
	}
	if ( 'template' === $record['kind'] ) {
		$token = wpagent_site_editor_acquire_lock( $record['type'], $record['slug'] ); if ( is_wp_error( $token ) ) return false;
		try { $current = wpagent_site_editor_get_template( $record['type'], $record['slug'] ); return ! is_wp_error( $current ) && 'custom' === $current->source && hash_equals( $record['hash'], wpagent_site_editor_resource_state( $current )['hash'] ) && wpagent_site_editor_restore_template_snapshot( $record['type'], $record['slug'], null ); } finally { wpagent_site_editor_release_lock( $record['type'], $record['slug'], $token ); }
	}
	if ( 'navigation' === $record['kind'] ) {
		$token = wpagent_site_editor_acquire_lock( 'wp_navigation', (string) $record['id'] ); if ( is_wp_error( $token ) ) return false;
		try { $current = wpagent_site_editor_navigation_state( $record['id'] ); if ( is_wp_error( $current ) || ! hash_equals( $record['hash'], $current['hash'] ) ) return false; $deleted = wp_delete_post( $record['id'], true ); return $deleted instanceof WP_Post && ! get_post( $record['id'] ); } finally { wpagent_site_editor_release_lock( 'wp_navigation', (string) $record['id'], $token ); }
	}
	return false;
}

function wpagent_spectra_one_snapshot_import_hash( array $created, $global ): string {
	return hash( 'sha256', wp_json_encode( array( 'blog_id' => get_current_blog_id(), 'theme' => wpagent_spectra_one_snapshot_identity()['theme'], 'created' => $created, 'global_styles_hash' => is_array( $global ) ? $global['state_hash'] : null ), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) );
}
