<?php
/** Shared helpers for verified structural Elementor mutations. */

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

/** @param array<int,array<string,mixed>> $elements @return array<string,bool>|WP_Error */
function wpagent_elementor_tree_ids( array $elements ) {
	$valid = wpagent_elementor_validate_elements( $elements );
	if ( is_wp_error( $valid ) ) {
		return $valid;
	}
	$ids = array();
	$walk = static function ( array $items ) use ( &$walk, &$ids ): void {
		foreach ( $items as $element ) {
			$ids[ (string) $element['id'] ] = true;
			$walk( $element['elements'] );
		}
	};
	$walk( $elements );
	return $ids;
}

/** @param array<string,bool> $ids @return string|WP_Error */
function wpagent_elementor_generate_element_id( array &$ids ) {
	for ( $attempt = 0; $attempt < 100; ++$attempt ) {
		$id = strtolower( wp_generate_password( 12, false, false ) );
		if ( preg_match( '/^[a-z0-9]{12}$/', $id ) && ! isset( $ids[ $id ] ) ) {
			$ids[ $id ] = true;
			return $id;
		}
	}
	return new WP_Error( 'wpagent_elementor_id_generation_failed', __( 'Für das neue Elementor-Element konnte keine eindeutige ID erzeugt werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
}

/**
 * Determine whether an Elementor element explicitly supports child elements.
 * Legacy layout elements are structural by definition. Widgets and atomic
 * elements must advertise their container capability in Elementor's public
 * element configuration; an unknown shape fails closed.
 *
 * @param array<string,mixed> $element
 * @param object|null         $registered
 * @return true|WP_Error
 */
function wpagent_elementor_element_accepts_children( array $element, $registered = null ) {
	$el_type = is_string( $element['elType'] ?? null ) ? $element['elType'] : '';
	if ( in_array( $el_type, array( 'container', 'section', 'column' ), true ) ) {
		return true;
	}
	if ( ! is_object( $registered ) ) {
		$registered = wpagent_elementor_registered_element( $element );
		if ( is_wp_error( $registered ) ) {
			return $registered;
		}
	}
	if ( ! method_exists( $registered, 'get_config' ) ) {
		return new WP_Error( 'wpagent_elementor_bad_parent', __( 'Das Ziel-Element kann keine untergeordneten Elemente aufnehmen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	try {
		$config = $registered->get_config();
	} catch ( Throwable ) {
		return new WP_Error( 'wpagent_elementor_bad_parent', __( 'Das Ziel-Element kann keine untergeordneten Elemente aufnehmen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if (
		is_array( $config )
		&& (
			! empty( $config['support_nesting'] )
			|| ( is_array( $config['meta'] ?? null ) && ! empty( $config['meta']['is_container'] ) )
		)
	) {
		return true;
	}
	return new WP_Error( 'wpagent_elementor_bad_parent', __( 'Das Ziel-Element kann keine untergeordneten Elemente aufnehmen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
}

/**
 * Build a new subtree from a deliberately small public shape. Caller supplied
 * IDs are never trusted, including IDs of descendants.
 *
 * @param array<string,mixed> $element
 * @param array<string,bool>  $ids
 * @return array<string,mixed>|WP_Error
 */
function wpagent_elementor_prepare_new_subtree( array $element, array &$ids, int $depth = 0, int &$count = 0 ) {
	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' => 400 ) );
	}
	if ( array_is_list( $element ) ) {
		return new WP_Error( 'wpagent_elementor_bad_element', __( 'Das neue Elementor-Element muss ein Objekt sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$allowed_keys = array_flip( array( 'id', 'elType', 'widgetType', 'settings', 'elements', 'isInner' ) );
	foreach ( array_keys( $element ) as $key ) {
		if ( ! is_string( $key ) || ! isset( $allowed_keys[ $key ] ) ) {
			return new WP_Error( 'wpagent_elementor_unknown_element_field', __( 'Das neue Elementor-Element enthält ein nicht unterstütztes Strukturfeld.', 'wp-agent-companion' ), array( 'status' => 400, 'field' => is_scalar( $key ) ? (string) $key : '' ) );
		}
	}
	++$count;
	if ( $count > WPAGENT_ELEMENTOR_MAX_ELEMENTS ) {
		return new WP_Error( 'wpagent_elementor_element_limit', __( 'Ein neuer Elementor-Teilbaum darf höchstens 2.000 Elemente enthalten.', 'wp-agent-companion' ), array( 'status' => 413 ) );
	}
	$el_type = is_string( $element['elType'] ?? null ) ? $element['elType'] : '';
	if ( ! preg_match( '/^[A-Za-z0-9_-]{1,64}$/', $el_type ) ) {
		return new WP_Error( 'wpagent_elementor_bad_type', __( 'Das neue Elementor-Element enthält keinen gültigen elType.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$settings = $element['settings'] ?? array();
	$children = $element['elements'] ?? array();
	if ( ! is_array( $settings ) || ( $settings && array_is_list( $settings ) ) || ! is_array( $children ) || ! array_is_list( $children ) ) {
		return new WP_Error( 'wpagent_elementor_bad_shape', __( 'settings muss ein Objekt und elements eine Liste sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( array_key_exists( 'isInner', $element ) && ! is_bool( $element['isInner'] ) ) {
		return new WP_Error( 'wpagent_elementor_bad_shape', __( 'isInner muss ein boolescher Wert sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$widget_type = null;
	if ( 'widget' === $el_type ) {
		$widget_type = is_string( $element['widgetType'] ?? null ) ? $element['widgetType'] : '';
		if ( '' === $widget_type ) {
			return new WP_Error( 'wpagent_elementor_bad_widget', __( 'Ein neues Widget benötigt einen gültigen widgetType.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
	} elseif ( array_key_exists( 'widgetType', $element ) ) {
		return new WP_Error( 'wpagent_elementor_bad_widget', __( 'widgetType ist nur für Elementor-Widgets erlaubt.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$registered = wpagent_elementor_registered_element( array( 'elType' => $el_type, 'widgetType' => $widget_type ) );
	if ( is_wp_error( $registered ) ) {
		return $registered;
	}
	if ( $children ) {
		$accepts_children = wpagent_elementor_element_accepts_children( $element, $registered );
		if ( is_wp_error( $accepts_children ) ) {
			return $accepts_children;
		}
	}
	$controls = wpagent_elementor_writable_controls( $registered );
	if ( is_wp_error( $controls ) ) {
		return $controls;
	}
	foreach ( array_keys( $settings ) as $control_name ) {
		if ( ! is_string( $control_name ) || ! isset( $controls[ $control_name ] ) ) {
			return new WP_Error( 'wpagent_elementor_unknown_control', __( 'Das neue Elementor-Element enthält ein unbekanntes oder nicht schreibbares Control.', 'wp-agent-companion' ), array( 'status' => 400, 'control' => is_scalar( $control_name ) ? (string) $control_name : '' ) );
		}
	}
	$settings_valid = wpagent_elementor_validate_settings( $settings );
	if ( is_wp_error( $settings_valid ) ) {
		return $settings_valid;
	}
	$id = wpagent_elementor_generate_element_id( $ids );
	if ( is_wp_error( $id ) ) {
		return $id;
	}
	$prepared_children = array();
	foreach ( $children as $child ) {
		if ( ! is_array( $child ) ) {
			return new WP_Error( 'wpagent_elementor_bad_element', __( 'Jedes untergeordnete Elementor-Element muss ein Objekt sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$prepared_child = wpagent_elementor_prepare_new_subtree( $child, $ids, $depth + 1, $count );
		if ( is_wp_error( $prepared_child ) ) {
			return $prepared_child;
		}
		$prepared_children[] = $prepared_child;
	}
	$prepared = array( 'id' => $id, 'elType' => $el_type, 'settings' => $settings, 'elements' => $prepared_children );
	if ( null !== $widget_type ) {
		$prepared['widgetType'] = $widget_type;
	}
	if ( array_key_exists( 'isInner', $element ) ) {
		$prepared['isInner'] = $element['isInner'];
	}
	return $prepared;
}

/** @param array<string,mixed> $element @param array<string,bool> $ids @return array<string,mixed>|WP_Error */
function wpagent_elementor_duplicate_subtree( array $element, array &$ids, int $depth = 0 ) {
	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' => 400 ) );
	}
	$id = wpagent_elementor_generate_element_id( $ids );
	if ( is_wp_error( $id ) ) {
		return $id;
	}
	$copy = $element;
	$copy['id'] = $id;
	$copy['elements'] = array();
	foreach ( $element['elements'] ?? array() as $child ) {
		$child_copy = wpagent_elementor_duplicate_subtree( $child, $ids, $depth + 1 );
		if ( is_wp_error( $child_copy ) ) {
			return $child_copy;
		}
		$copy['elements'][] = $child_copy;
	}
	return $copy;
}

/** @param int[] $prefix @param int[] $path */
function wpagent_elementor_path_is_prefix( array $prefix, array $path ): bool {
	return count( $prefix ) <= count( $path ) && $prefix === array_slice( $path, 0, count( $prefix ) );
}

/** @param array<int,array<string,mixed>> $elements @param int[] $path @return array<string,mixed>|WP_Error */
function wpagent_elementor_remove_at_path( array &$elements, array $path, int $offset = 0 ) {
	$index = $path[ $offset ] ?? null;
	if ( ! is_int( $index ) || $index < 0 || ! array_key_exists( $index, $elements ) || ! is_array( $elements[ $index ] ) ) {
		return new WP_Error( 'wpagent_elementor_bad_path', __( 'Der gespeicherte Elementor-Pfad ist nicht mehr gültig.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( $offset === count( $path ) - 1 ) {
		$removed = $elements[ $index ];
		array_splice( $elements, $index, 1 );
		return $removed;
	}
	if ( ! is_array( $elements[ $index ]['elements'] ?? null ) ) {
		return new WP_Error( 'wpagent_elementor_bad_path', __( 'Der gespeicherte Elementor-Pfad ist nicht mehr gültig.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return wpagent_elementor_remove_at_path( $elements[ $index ]['elements'], $path, $offset + 1 );
}

/** @param array<int,array<string,mixed>> $elements @param int[]|null $parent_path @param array<string,mixed> $element @return int[]|WP_Error */
function wpagent_elementor_insert_under_parent( array &$elements, ?array $parent_path, ?int $index, array $element ) {
	$children =& $elements;
	$base_path = array();
	if ( null !== $parent_path ) {
		foreach ( $parent_path as $parent_index ) {
			if ( ! is_int( $parent_index ) || $parent_index < 0 || ! isset( $children[ $parent_index ] ) || ! is_array( $children[ $parent_index ] ) ) {
				return new WP_Error( 'wpagent_elementor_bad_path', __( 'Der gespeicherte Elementor-Elternpfad ist nicht mehr gültig.', 'wp-agent-companion' ), array( 'status' => 409 ) );
			}
			$accepts_children = wpagent_elementor_element_accepts_children( $children[ $parent_index ] );
			if ( is_wp_error( $accepts_children ) ) {
				return $accepts_children;
			}
			$base_path[] = $parent_index;
			if ( ! isset( $children[ $parent_index ]['elements'] ) || ! is_array( $children[ $parent_index ]['elements'] ) || ! array_is_list( $children[ $parent_index ]['elements'] ) ) {
				return new WP_Error( 'wpagent_elementor_bad_parent', __( 'Das Ziel-Element kann keine untergeordneten Elemente aufnehmen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			}
			$children =& $children[ $parent_index ]['elements'];
		}
	}
	$target_index = null === $index ? count( $children ) : $index;
	if ( $target_index < 0 || $target_index > count( $children ) ) {
		return new WP_Error( 'wpagent_elementor_bad_index', __( 'index liegt außerhalb der Zielpositionen.', 'wp-agent-companion' ), array( 'status' => 400, 'maximum' => count( $children ) ) );
	}
	array_splice( $children, $target_index, 0, array( $element ) );
	return array_merge( $base_path, array( $target_index ) );
}

/** @return array{parent_id:?string,index:?int}|WP_Error */
function wpagent_elementor_parse_tree_target( array $input ) {
	$parent_id = null;
	if ( array_key_exists( 'parent_id', $input ) ) {
		if ( ! is_string( $input['parent_id'] ) || ! preg_match( '/^[A-Za-z0-9_-]{1,64}$/', $input['parent_id'] ) ) {
			return new WP_Error( 'wpagent_elementor_bad_parent', __( 'parent_id ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$parent_id = $input['parent_id'];
	}
	$index = null;
	if ( array_key_exists( 'index', $input ) ) {
		if ( ! is_int( $input['index'] ) || $input['index'] < 0 || $input['index'] > WPAGENT_ELEMENTOR_MAX_ELEMENTS ) {
			return new WP_Error( 'wpagent_elementor_bad_index', __( 'index muss eine ganze Zahl zwischen 0 und 2.000 sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$index = $input['index'];
	}
	return array( 'parent_id' => $parent_id, 'index' => $index );
}

/** @param array<int,array<string,mixed>> $elements @return int[]|null|WP_Error */
function wpagent_elementor_parent_path( array $elements, ?string $parent_id ) {
	if ( null === $parent_id ) {
		return null;
	}
	$located = wpagent_elementor_locate_element( $elements, $parent_id );
	return is_wp_error( $located ) ? $located : $located['path'];
}
