<?php
/** Shared helpers for Elementor widget discovery and targeted element access. */

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

define( 'WPAGENT_ELEMENTOR_MAX_CONTROLS', 500 );
define( 'WPAGENT_ELEMENTOR_MAX_PROVIDER_CONTROLS', 5000 );
define( 'WPAGENT_ELEMENTOR_MAX_WIDGET_CATEGORIES', 100 );
define( 'WPAGENT_ELEMENTOR_MAX_WIDGET_KEYWORDS', 200 );
define( 'WPAGENT_ELEMENTOR_MAX_STYLE_PATCHES', 50 );
define( 'WPAGENT_ELEMENTOR_MAX_CONTROL_OPTIONS', 100 );

/** @param mixed $value */
function wpagent_elementor_bounded_text( $value, int $max_bytes, int $max_characters, bool $sanitize = true ): string {
	if ( ! is_scalar( $value ) ) {
		return '';
	}
	$text = (string) $value;
	if ( strlen( $text ) > $max_bytes ) {
		$text = substr( $text, 0, $max_bytes );
	}
	$text = wp_check_invalid_utf8( $text, true );
	return wp_html_excerpt( $sanitize ? sanitize_text_field( $text ) : $text, $max_characters, '' );
}

/** @return object|WP_Error */
function wpagent_elementor_widget_manager() {
	if ( ! wpagent_elementor_available()
		|| ! isset( \Elementor\Plugin::$instance->widgets_manager )
		|| ! is_object( \Elementor\Plugin::$instance->widgets_manager )
		|| ! method_exists( \Elementor\Plugin::$instance->widgets_manager, 'get_widget_types' ) ) {
		return wpagent_elementor_unavailable_error();
	}
	return \Elementor\Plugin::$instance->widgets_manager;
}

/** @return object|WP_Error */
function wpagent_elementor_widget( string $widget_type ) {
	if ( ! preg_match( '/^[A-Za-z0-9_-]{1,128}$/', $widget_type ) ) {
		return new WP_Error( 'wpagent_elementor_bad_widget', __( 'widget_type ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( wpagent_elementor_widget_type_is_unsafe( $widget_type ) ) {
		return new WP_Error(
			'wpagent_elementor_unsafe_widget',
			__( 'HTML-, Shortcode- und ungefilterte Legacy-Widgets sind über diesen sicheren Pfad nicht erlaubt.', 'wp-agent-companion' ),
			array( 'status' => 400, 'widget_type' => $widget_type )
		);
	}
	$manager = wpagent_elementor_widget_manager();
	if ( is_wp_error( $manager ) ) {
		return $manager;
	}
	try {
		$widget = $manager->get_widget_types( $widget_type );
	} catch ( Throwable ) {
		return new WP_Error( 'wpagent_elementor_widget_failed', __( 'Elementor konnte den Widget-Typ nicht laden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( ! is_object( $widget ) || ! method_exists( $widget, 'get_controls' ) ) {
		return new WP_Error( 'wpagent_elementor_unknown_widget', __( 'Elementor kennt diesen Widget-Typ nicht.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	return $widget;
}

function wpagent_elementor_control_is_writable( array $control ): bool {
	$type = is_string( $control['type'] ?? null ) ? sanitize_key( $control['type'] ) : '';
	return '' !== $type && ! in_array( $type, array( 'section', 'tabs', 'tab', 'divider', 'heading', 'raw_html', 'button' ), true );
}

function wpagent_elementor_control_is_style( array $control ): bool {
	return true === ( $control['_wpagent_style'] ?? false );
}

function wpagent_elementor_style_type_is_supported( array $control ): bool {
	$type = is_string( $control['type'] ?? null ) ? sanitize_key( $control['type'] ) : '';
	return in_array( $type, array( 'color', 'slider', 'dimensions', 'number', 'select', 'choose', 'switcher' ), true );
}

/**
 * Elementor 4 may keep optimized style controls outside get_controls(). Both
 * get_controls() and get_stack() are public Controls_Stack APIs.
 *
 * @return array<string,array<string,mixed>>|WP_Error
 */
function wpagent_elementor_registered_controls( $element ) {
	if ( ! is_object( $element ) || ! method_exists( $element, 'get_controls' ) ) {
		return new WP_Error( 'wpagent_elementor_controls_unavailable', __( 'Elementor stellt für dieses Element keine Controls bereit.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	try {
		$controls = $element->get_controls();
		$stack = method_exists( $element, 'get_stack' ) ? $element->get_stack() : array();
	} catch ( Throwable ) {
		return new WP_Error( 'wpagent_elementor_controls_failed', __( 'Elementor konnte die Control-Liste nicht laden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( ! is_array( $controls ) || ! is_array( $stack ) ) {
		return new WP_Error( 'wpagent_elementor_controls_unavailable', __( 'Elementor lieferte keine gültige Control-Liste.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$style_controls = is_array( $stack['style_controls'] ?? null ) ? $stack['style_controls'] : array();
	if ( count( $controls ) + count( $style_controls ) > WPAGENT_ELEMENTOR_MAX_PROVIDER_CONTROLS ) {
		return new WP_Error( 'wpagent_elementor_controls_limit', __( 'Elementor lieferte zu viele Controls für eine sichere Verarbeitung.', 'wp-agent-companion' ), array( 'status' => 413 ) );
	}
	foreach ( $style_controls as $name => $control ) {
		if ( is_string( $name ) && is_array( $control ) ) {
			$control['_wpagent_style'] = true;
			$controls[ $name ] = $control;
		}
	}
	foreach ( $controls as $name => &$control ) {
		if ( is_array( $control ) && ! empty( $control['selectors'] ) ) {
			$control['_wpagent_style'] = true;
		}
	}
	unset( $control );
	return $controls;
}

/**
 * Read bounded, public catalog metadata without trusting third-party widget implementations.
 *
 * @return array{title:string,categories:string[],keywords:string[]}|WP_Error
 */
function wpagent_elementor_widget_metadata( $widget, string $fallback_name ) {
	if ( ! is_object( $widget ) ) {
		return new WP_Error( 'wpagent_elementor_widget_metadata_failed', __( 'Elementor konnte die Widget-Metadaten nicht laden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	try {
		$raw_title = method_exists( $widget, 'get_title' ) ? $widget->get_title() : $fallback_name;
		$raw_categories = method_exists( $widget, 'get_categories' ) ? $widget->get_categories() : array();
		$raw_keywords = method_exists( $widget, 'get_keywords' ) ? $widget->get_keywords() : array();
	} catch ( Throwable ) {
		return new WP_Error( 'wpagent_elementor_widget_metadata_failed', __( 'Elementor konnte die Widget-Metadaten nicht laden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$title = is_scalar( $raw_title ) ? wpagent_elementor_bounded_text( $raw_title, 4096, 500 ) : $fallback_name;
	$categories = array();
	$seen_categories = array();
	if ( is_array( $raw_categories ) ) {
		$inspected_categories = 0;
		foreach ( $raw_categories as $raw_category ) {
			++$inspected_categories;
			if ( $inspected_categories > WPAGENT_ELEMENTOR_MAX_WIDGET_CATEGORIES ) {
				break;
			}
			if ( ! is_string( $raw_category ) ) {
				continue;
			}
			$category = sanitize_key( $raw_category );
			$category_key = 'category:' . $category;
			if ( '' !== $category && ! isset( $seen_categories[ $category_key ] ) ) {
				$seen_categories[ $category_key ] = true;
				$categories[] = $category;
			}
		}
	}
	$keywords = array();
	$seen_keywords = array();
	if ( is_array( $raw_keywords ) ) {
		$inspected_keywords = 0;
		foreach ( $raw_keywords as $raw_keyword ) {
			++$inspected_keywords;
			if ( $inspected_keywords > WPAGENT_ELEMENTOR_MAX_WIDGET_KEYWORDS ) {
				break;
			}
			if ( ! is_scalar( $raw_keyword ) ) {
				continue;
			}
			$keyword = wpagent_elementor_bounded_text( $raw_keyword, 2048, 200 );
			$keyword_key = 'keyword:' . $keyword;
			if ( '' !== $keyword && ! isset( $seen_keywords[ $keyword_key ] ) ) {
				$seen_keywords[ $keyword_key ] = true;
				$keywords[] = $keyword;
			}
		}
	}
	return array(
		'title' => $title,
		'categories' => $categories,
		'keywords' => $keywords,
	);
}

/**
 * @param int|null $total_count Receives the total number of writable controls before truncation.
 * @param bool $include_responsive_children Include Elementor's provider-generated device aliases for write validation.
 * @return array<string,array<string,mixed>>|WP_Error
 */
function wpagent_elementor_writable_controls( $element, ?int &$total_count = null, bool $include_responsive_children = false ): array|WP_Error {
	$total_count = 0;
	$controls = wpagent_elementor_registered_controls( $element );
	if ( is_wp_error( $controls ) ) {
		return $controls;
	}
	$result = array();
	foreach ( $controls as $name => $control ) {
		if ( ! is_string( $name ) || ! preg_match( '/^[A-Za-z0-9_-]{1,191}$/', $name ) || ! is_array( $control ) || ! wpagent_elementor_control_is_writable( $control ) ) {
			continue;
		}
		if ( ! $include_responsive_children && wpagent_elementor_control_is_style( $control ) && ! empty( $control['parent'] ) ) {
			continue;
		}
		++$total_count;
		if ( count( $result ) < WPAGENT_ELEMENTOR_MAX_CONTROLS ) {
			$result[ $name ] = $control;
		}
	}
	return $result;
}

/** @return string[] */
function wpagent_elementor_control_devices( string $name, array $control, array $controls ): array {
	$devices = array( 'desktop' );
	$is_responsive = true === ( $control['is_responsive'] ?? false ) || is_array( $control['responsive'] ?? null ) && ! empty( $control['responsive'] );
	if ( ! $is_responsive ) {
		return $devices;
	}
	$active = array();
	try {
		$manager = \Elementor\Plugin::$instance->breakpoints ?? null;
		$active = is_object( $manager ) && method_exists( $manager, 'get_active_breakpoints' ) ? $manager->get_active_breakpoints() : array();
	} catch ( Throwable ) {
		$active = array();
	}
	$limited = is_array( $control['responsive']['devices'] ?? null ) ? $control['responsive']['devices'] : null;
	foreach ( array( 'tablet', 'mobile' ) as $device ) {
		if ( ! isset( $active[ $device ] ) || ( null !== $limited && ! in_array( $device, $limited, true ) ) ) {
			continue;
		}
		$child = $controls[ $name . '_' . $device ] ?? null;
		if ( true === ( $control['is_responsive'] ?? false ) || is_array( $child ) ) {
			$devices[] = $device;
		}
	}
	return $devices;
}

/** @return array<string,mixed> */
function wpagent_elementor_control_summary( string $name, array $control, array $all_controls = array() ): array {
	$type = is_string( $control['type'] ?? null ) ? sanitize_key( $control['type'] ) : '';
	$label = wpagent_elementor_bounded_text( $control['label'] ?? null, 4096, 500 );
	$tab = is_string( $control['tab'] ?? null ) ? sanitize_key( $control['tab'] ) : '';
	$section = is_string( $control['section'] ?? null ) ? sanitize_key( $control['section'] ) : '';
	$default_json = '';
	if ( array_key_exists( 'default', $control ) ) {
		$encoded = wp_json_encode( $control['default'], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
		if ( is_string( $encoded ) && strlen( $encoded ) <= 16384 ) {
			$default_json = $encoded;
		}
	}
	$options = array();
	if ( is_array( $control['options'] ?? null ) ) {
		foreach ( $control['options'] as $value => $option_label ) {
			if ( count( $options ) >= WPAGENT_ELEMENTOR_MAX_CONTROL_OPTIONS ) {
				break;
			}
			if ( ( ! is_scalar( $value ) && null !== $value ) || ( ! is_scalar( $option_label ) && null !== $option_label ) ) {
				continue;
			}
			$options[] = array(
				'value' => wpagent_elementor_bounded_text( $value, 4096, 500, false ),
				'label' => wpagent_elementor_bounded_text( $option_label, 4096, 500 ),
			);
		}
	}
	return array(
		'name'         => $name,
		'type'         => $type,
		'label'        => $label,
		'tab'          => $tab,
		'section'      => $section,
		'responsive'   => count( wpagent_elementor_control_devices( $name, $control, is_array( $all_controls ) ? $all_controls : array() ) ) > 1,
		'style'        => wpagent_elementor_control_is_style( $control ),
		'devices'      => wpagent_elementor_control_devices( $name, $control, is_array( $all_controls ) ? $all_controls : array() ),
		'value_type'   => $type,
		'dynamic'      => is_array( $control['dynamic'] ?? null ) && ! empty( $control['dynamic']['active'] ),
		'default_json' => $default_json,
		'options'      => $options,
	);
}

/** @return true|WP_Error */
function wpagent_elementor_style_number_is_valid( $value ) {
	if ( ! is_int( $value ) && ! is_float( $value ) ) {
		return new WP_Error( 'wpagent_elementor_style_bad_number', __( 'Numerische Elementor-Styles müssen als Zahl übergeben werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$number = (float) $value;
	if ( ! is_finite( $number ) || abs( $number ) > 10000 ) {
		return new WP_Error( 'wpagent_elementor_style_bad_number', __( 'Der numerische Elementor-Style liegt außerhalb des erlaubten Bereichs.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return true;
}

function wpagent_elementor_style_unit_is_valid( $unit ): bool {
	return is_string( $unit ) && in_array( $unit, array( '', 'px', '%', 'em', 'rem', 'vw', 'vh', 'vmin', 'vmax', 's', 'ms', 'deg' ), true );
}

/** @return string|WP_Error */
function wpagent_elementor_style_token( $value, array $control ) {
	if ( ! is_string( $value ) || strlen( $value ) > 100 || 1 !== preg_match( '/^[A-Za-z0-9_.%+\-]+(?: [A-Za-z0-9_.%+\-]+)*$/', $value ) ) {
		return new WP_Error( 'wpagent_elementor_style_bad_token', __( 'Der Elementor-Style muss ein begrenztes CSS-Schlüsselwort sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$options = is_array( $control['options'] ?? null ) ? $control['options'] : array();
	$dictionary = is_array( $control['selectors_dictionary'] ?? null ) ? $control['selectors_dictionary'] : array();
	if ( $options && ! array_key_exists( $value, $options ) && ! array_key_exists( $value, $dictionary ) ) {
		return new WP_Error( 'wpagent_elementor_style_bad_token', __( 'Der Wert gehört nicht zu den registrierten Elementor-Control-Optionen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return $value;
}

/** @return mixed|WP_Error */
function wpagent_elementor_normalize_style_value( $value, array $control ) {
	$type = is_string( $control['type'] ?? null ) ? sanitize_key( $control['type'] ) : '';
	if ( ! wpagent_elementor_style_type_is_supported( $control ) ) {
		return new WP_Error( 'wpagent_elementor_style_unsupported', __( 'Dieser Elementor-Style-Typ ist über den sicheren Pfad nicht schreibbar.', 'wp-agent-companion' ), array( 'status' => 400, 'type' => $type ) );
	}
	if ( 'color' === $type ) {
		$color = function_exists( 'wpagent_elementor_design_color_value' ) ? wpagent_elementor_design_color_value( $value ) : new WP_Error();
		return is_wp_error( $color )
			? new WP_Error( 'wpagent_elementor_style_bad_color', __( 'Der Elementor-Style enthält keinen erlaubten CSS-Farbwert.', 'wp-agent-companion' ), array( 'status' => 400 ) )
			: $color;
	}
	if ( in_array( $type, array( 'select', 'choose', 'switcher' ), true ) ) {
		return wpagent_elementor_style_token( $value, $control );
	}
	if ( 'number' === $type ) {
		$valid = wpagent_elementor_style_number_is_valid( $value );
		return is_wp_error( $valid ) ? $valid : $value;
	}
	if ( ! is_array( $value ) || array_is_list( $value ) ) {
		return new WP_Error( 'wpagent_elementor_style_bad_shape', __( 'Slider und Abstände müssen als begrenztes Objekt übergeben werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( 'slider' === $type ) {
		if ( array_diff( array_keys( $value ), array( 'size', 'unit', 'sizes' ) ) || ! array_key_exists( 'size', $value ) || ! array_key_exists( 'unit', $value ) || ( isset( $value['sizes'] ) && array() !== $value['sizes'] ) ) {
			return new WP_Error( 'wpagent_elementor_style_bad_shape', __( 'Ein Elementor-Slider benötigt ausschließlich size, unit und optional eine leere sizes-Liste.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$valid = wpagent_elementor_style_number_is_valid( $value['size'] );
		if ( is_wp_error( $valid ) || ! wpagent_elementor_style_unit_is_valid( $value['unit'] ) ) {
			return is_wp_error( $valid ) ? $valid : new WP_Error( 'wpagent_elementor_style_bad_unit', __( 'Die CSS-Einheit des Elementor-Styles ist nicht erlaubt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		return array( 'unit' => $value['unit'], 'size' => $value['size'], 'sizes' => array() );
	}
	$keys = array( 'top', 'right', 'bottom', 'left' );
	if ( array_diff( array_keys( $value ), array( 'top', 'right', 'bottom', 'left', 'unit', 'isLinked' ) ) || ! array_key_exists( 'unit', $value ) || array_diff( $keys, array_keys( $value ) ) || ! wpagent_elementor_style_unit_is_valid( $value['unit'] ) ) {
		return new WP_Error( 'wpagent_elementor_style_bad_shape', __( 'Responsive Elementor-Abstände benötigen top, right, bottom, left und eine erlaubte unit.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	foreach ( $keys as $key ) {
		$valid = wpagent_elementor_style_number_is_valid( $value[ $key ] );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
	}
	if ( isset( $value['isLinked'] ) && ! is_bool( $value['isLinked'] ) ) {
		return new WP_Error( 'wpagent_elementor_style_bad_shape', __( 'isLinked muss ein boolescher Wert sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return array(
		'unit' => $value['unit'],
		'top' => $value['top'],
		'right' => $value['right'],
		'bottom' => $value['bottom'],
		'left' => $value['left'],
		'isLinked' => $value['isLinked'] ?? false,
	);
}

/**
 * @return array{patch:array<string,mixed>,remove:string[],has_values:bool,render_tokens:string[]}|WP_Error
 */
function wpagent_elementor_normalize_style_patch( $raw, array $controls ) {
	if ( ! is_array( $raw ) || ( $raw && array_is_list( $raw ) ) || count( $raw ) > WPAGENT_ELEMENTOR_MAX_STYLE_PATCHES ) {
		return new WP_Error( 'wpagent_elementor_style_bad_shape', __( 'style_patch muss ein Objekt mit höchstens 50 registrierten Styles sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$styles = array();
	foreach ( $controls as $name => $control ) {
		if ( wpagent_elementor_control_is_style( $control ) && wpagent_elementor_style_type_is_supported( $control ) && empty( $control['parent'] ) ) {
			$styles[ $name ] = $control;
		}
	}
	$patch = array();
	$remove = array();
	$render_tokens = array();
	foreach ( $raw as $name => $device_values ) {
		if ( ! is_string( $name ) || ! isset( $styles[ $name ] ) ) {
			return new WP_Error( 'wpagent_elementor_unknown_style', __( 'style_patch enthält ein nicht registriertes oder nicht sicher schreibbares Style-Control.', 'wp-agent-companion' ), array( 'status' => 400, 'control' => is_scalar( $name ) ? (string) $name : '' ) );
		}
		if ( ! is_array( $device_values ) || array_is_list( $device_values ) || array() === $device_values || count( $device_values ) > 3 ) {
			return new WP_Error( 'wpagent_elementor_style_bad_shape', __( 'Jeder Elementor-Style benötigt ein Objekt mit Desktop-, Tablet- oder Mobilwerten.', 'wp-agent-companion' ), array( 'status' => 400, 'control' => $name ) );
		}
		$allowed_devices = wpagent_elementor_control_devices( $name, $styles[ $name ], $controls );
		foreach ( $device_values as $device => $value ) {
			if ( ! is_string( $device ) || ! in_array( $device, $allowed_devices, true ) ) {
				return new WP_Error( 'wpagent_elementor_style_bad_device', __( 'Das registrierte Elementor-Control unterstützt dieses Gerät nicht.', 'wp-agent-companion' ), array( 'status' => 400, 'control' => $name, 'device' => is_scalar( $device ) ? (string) $device : '' ) );
			}
			$storage_name = 'desktop' === $device ? $name : $name . '_' . $device;
			if ( null === $value ) {
				$remove[] = $storage_name;
				continue;
			}
			$device_control = is_array( $controls[ $storage_name ] ?? null ) ? $controls[ $storage_name ] : $styles[ $name ];
			$normalized = wpagent_elementor_normalize_style_value( $value, $device_control );
			if ( is_wp_error( $normalized ) ) {
				return $normalized;
			}
			$patch[ $storage_name ] = $normalized;
			$type = sanitize_key( (string) ( $device_control['type'] ?? '' ) );
			if ( 'color' === $type ) {
				$render_tokens[] = $normalized;
			} elseif ( 'slider' === $type ) {
				$render_tokens[] = (string) $normalized['size'] . $normalized['unit'];
			} elseif ( 'dimensions' === $type ) {
				foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) {
					$render_tokens[] = (string) $normalized[ $side ] . $normalized['unit'];
				}
			} elseif ( in_array( $type, array( 'select', 'choose', 'switcher', 'number' ), true ) ) {
				$dictionary = is_array( $device_control['selectors_dictionary'] ?? null ) ? $device_control['selectors_dictionary'] : array();
				$render_tokens[] = (string) ( $dictionary[ $normalized ] ?? $normalized );
			}
			$group_prefix = is_string( $device_control['groupPrefix'] ?? null ) ? $device_control['groupPrefix'] : '';
			$group_type = is_string( $device_control['groupType'] ?? null ) ? $device_control['groupType'] : '';
			$activation_name = $group_prefix . $group_type;
			$activation = $controls[ $activation_name ] ?? null;
			if ( '' !== $group_prefix && '' !== $group_type && is_array( $activation ) && 'popover_toggle' === ( $activation['type'] ?? '' ) && wpagent_elementor_control_is_style( $activation ) && is_scalar( $activation['return_value'] ?? null ) ) {
				$activation_value = wpagent_elementor_style_token( (string) $activation['return_value'], $activation );
				if ( is_wp_error( $activation_value ) ) {
					return $activation_value;
				}
				$patch[ $activation_name ] = $activation_value;
			}
		}
	}
	return array( 'patch' => $patch, 'remove' => $remove, 'has_values' => array() !== $patch, 'render_tokens' => array_values( array_unique( $render_tokens ) ) );
}

function wpagent_elementor_css_contains_target_tokens( string $css, string $element_id, array $expected_tokens ): bool {
	if ( '' === $css || strlen( $css ) > 5 * MB_IN_BYTES || ! preg_match( '/^[A-Za-z0-9_-]{1,64}$/', $element_id ) ) {
		return false;
	}
	$rules = array();
	if ( false === preg_match_all( '/([^{}]+)\{([^{}]*)\}/', $css, $rules, PREG_SET_ORDER ) ) {
		return false;
	}
	$selector_pattern = '/\.elementor-element-' . preg_quote( $element_id, '/' ) . '(?![A-Za-z0-9_-])/';
	$declarations = '';
	foreach ( $rules as $rule ) {
		if ( 1 === preg_match( $selector_pattern, $rule[1] ) ) {
			$declarations .= $rule[2];
		}
	}
	if ( '' === $declarations ) {
		return false;
	}
	foreach ( $expected_tokens as $token ) {
		if ( ! is_string( $token ) || '' === $token || ! str_contains( $declarations, $token ) ) {
			return false;
		}
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_elementor_verify_style_render( int $post_id, string $element_id, array $expected_tokens = array() ) {
	if ( ! class_exists( '\\Elementor\\Core\\Files\\CSS\\Post' ) ) {
		return new WP_Error( 'wpagent_elementor_style_render_failed', __( 'Elementors öffentliche CSS-Kompilierung ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	try {
		$css = ( new \Elementor\Core\Files\CSS\Post( $post_id ) )->get_content();
	} catch ( Throwable ) {
		return new WP_Error( 'wpagent_elementor_style_render_failed', __( 'Elementor konnte die responsiven Styles nicht für das Frontend kompilieren.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( ! is_string( $css ) || ! wpagent_elementor_css_contains_target_tokens( $css, $element_id, array() ) ) {
		return new WP_Error( 'wpagent_elementor_style_render_failed', __( 'Die gespeicherten Elementor-Styles fehlen in der kompilierten Frontend-Ausgabe.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( ! wpagent_elementor_css_contains_target_tokens( $css, $element_id, $expected_tokens ) ) {
		return new WP_Error( 'wpagent_elementor_style_render_failed', __( 'Mindestens ein gespeicherter Elementor-Style fehlt in der kompilierten Frontend-Ausgabe.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	return true;
}

/** @return object|WP_Error */
function wpagent_elementor_registered_element( array $element ) {
	$el_type = is_string( $element['elType'] ?? null ) ? $element['elType'] : '';
	$widget_type = is_string( $element['widgetType'] ?? null ) ? $element['widgetType'] : null;
	if ( 'widget' === $el_type && null !== $widget_type ) {
		return wpagent_elementor_widget( $widget_type );
	}
	if ( ! preg_match( '/^[A-Za-z0-9_-]{1,64}$/', $el_type ) ) {
		return new WP_Error( 'wpagent_elementor_bad_type', __( 'Das Elementor-Element enthält keinen gültigen elType.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$registered = \Elementor\Plugin::$instance->elements_manager->get_element( $el_type, $widget_type );
	if ( ! is_object( $registered ) ) {
		return new WP_Error( 'wpagent_elementor_unknown_element', __( 'Elementor kennt diesen Elementtyp nicht.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return $registered;
}

/**
 * @param array<int,array<string,mixed>> $elements
 * @return array{element:array<string,mixed>,path:int[]}|WP_Error
 */
function wpagent_elementor_locate_element( array $elements, string $element_id ) {
	if ( ! preg_match( '/^[A-Za-z0-9_-]{1,64}$/', $element_id ) ) {
		return new WP_Error( 'wpagent_elementor_bad_id', __( 'element_id ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$matches = array();
	$count = 0;
	$walk = static function ( array $items, array $path, int $depth ) use ( &$walk, &$matches, &$count, $element_id ) {
		if ( $depth > WPAGENT_ELEMENTOR_MAX_DEPTH ) {
			return new WP_Error( 'wpagent_elementor_depth', __( 'Die Elementor-Struktur ist zu tief verschachtelt.', 'wp-agent-companion' ), array( 'status' => 413 ) );
		}
		foreach ( $items as $index => $element ) {
			if ( ! is_array( $element ) ) {
				continue;
			}
			++$count;
			if ( $count > WPAGENT_ELEMENTOR_MAX_ELEMENTS ) {
				return new WP_Error( 'wpagent_elementor_element_limit', __( 'Ein Elementor-Dokument darf über diesen Pfad höchstens 2.000 Elemente enthalten.', 'wp-agent-companion' ), array( 'status' => 413 ) );
			}
			$current_path = array_merge( $path, array( (int) $index ) );
			if ( $element_id === ( $element['id'] ?? null ) ) {
				$matches[] = array( 'element' => $element, 'path' => $current_path );
			}
			if ( is_array( $element['elements'] ?? null ) ) {
				$walked = $walk( $element['elements'], $current_path, $depth + 1 );
				if ( is_wp_error( $walked ) ) {
					return $walked;
				}
			}
		}
		return true;
	};
	$walked = $walk( $elements, array(), 0 );
	if ( is_wp_error( $walked ) ) {
		return $walked;
	}
	if ( 0 === count( $matches ) ) {
		return new WP_Error( 'wpagent_elementor_element_not_found', __( 'Elementor-Element nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	}
	if ( 1 !== count( $matches ) ) {
		return new WP_Error( 'wpagent_elementor_duplicate_id', __( 'Das Elementor-Dokument enthält die Element-ID mehrfach.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return $matches[0];
}

/** @param array<int,array<string,mixed>> $elements */
function wpagent_elementor_replace_element_settings( array &$elements, string $element_id, array $settings ): bool {
	foreach ( $elements as &$element ) {
		if ( ! is_array( $element ) ) {
			continue;
		}
		if ( $element_id === ( $element['id'] ?? null ) ) {
			$element['settings'] = $settings;
			unset( $element );
			return true;
		}
		if ( is_array( $element['elements'] ?? null ) && wpagent_elementor_replace_element_settings( $element['elements'], $element_id, $settings ) ) {
			unset( $element );
			return true;
		}
	}
	unset( $element );
	return false;
}
