<?php
/** Verified targeted updates for individual Elementor elements. */

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

function wpagent_ability_update_elementor_element(): array {
	return array(
		'label' => __( 'Elementor-Element aktualisieren', 'wp-agent-companion' ),
		'description' => __( 'Ändert oder entfernt einzelne registrierte Einstellungen und streng validierte Desktop-, Tablet- oder Mobil-Styles eines Elementor-Elements, ohne Geschwister zu ersetzen. Konfliktschutz, Revision, Rückleseprüfung, Frontend- und CSS-Prüfung sowie Rollback sind verpflichtend.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object',
			'properties' => array(
				'post_id' => array( 'type' => 'integer' ),
				'element_id' => array( 'type' => 'string', 'pattern' => '^[A-Za-z0-9_-]{1,64}$' ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'settings_patch' => array( 'type' => 'object', 'additionalProperties' => true ),
				'remove_settings' => array( 'type' => 'array', 'maxItems' => 100, 'items' => array( 'type' => 'string', 'pattern' => '^[A-Za-z0-9_-]{1,191}$' ) ),
				'style_patch' => array(
					'type' => 'object',
					'maxProperties' => WPAGENT_ELEMENTOR_MAX_STYLE_PATCHES,
					'additionalProperties' => array(
						'type' => 'object',
						'maxProperties' => 3,
						'properties' => array(
							'desktop' => array( 'type' => array( 'string', 'integer', 'number', 'boolean', 'object', 'null' ) ),
							'tablet' => array( 'type' => array( 'string', 'integer', 'number', 'boolean', 'object', 'null' ) ),
							'mobile' => array( 'type' => array( 'string', 'integer', 'number', 'boolean', 'object', 'null' ) ),
						),
						'additionalProperties' => false,
					),
				),
			),
			'required' => array( 'post_id', 'element_id', 'expected_hash' ),
		),
		'output_schema' => wpagent_elementor_element_write_output_schema(),
		'execute_callback' => 'wpagent_exec_update_elementor_element',
		'permission_callback' => 'wpagent_perm_edit_posts',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => true ) ),
	);
}

/** @return array<string,mixed> */
function wpagent_elementor_element_write_output_schema(): array {
	$schema = wpagent_elementor_write_output_schema();
	$schema['properties']['element_id'] = array( 'type' => 'string' );
	$schema['properties']['path'] = array( 'type' => 'array', 'items' => array( 'type' => 'integer' ) );
	$schema['properties']['element'] = array( 'type' => 'object', 'additionalProperties' => true );
	$schema['properties']['changed'] = array( 'type' => 'boolean' );
	return $schema;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_update_elementor_element( $input ) {
	$input = is_array( $input ) ? $input : array();
	$post_id = absint( $input['post_id'] ?? 0 );
	$element_id = is_string( $input['element_id'] ?? null ) ? $input['element_id'] : '';
	$expected_hash = is_string( $input['expected_hash'] ?? null ) ? $input['expected_hash'] : '';
	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 ) );
	}
	if ( ! preg_match( '/^[a-f0-9]{64}$/', $expected_hash ) ) {
		return new WP_Error( 'wpagent_elementor_hash', __( 'expected_hash muss ein SHA-256-Hash sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$has_patch = array_key_exists( 'settings_patch', $input );
	$has_remove = array_key_exists( 'remove_settings', $input );
	$has_style = array_key_exists( 'style_patch', $input );
	if ( ! $has_patch && ! $has_remove && ! $has_style ) {
		return new WP_Error( 'wpagent_elementor_no_fields', __( 'settings_patch, remove_settings oder style_patch ist erforderlich.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( $has_style && array() === ( $input['style_patch'] ?? null ) && ! $has_patch && ! $has_remove ) {
		return new WP_Error( 'wpagent_elementor_no_fields', __( 'style_patch darf nicht leer sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$patch = $input['settings_patch'] ?? array();
	$remove = $input['remove_settings'] ?? array();
	if ( ! is_array( $patch ) || ( $patch && array_is_list( $patch ) ) || ! is_array( $remove ) || ! array_is_list( $remove ) || count( $remove ) > 100 ) {
		return new WP_Error( 'wpagent_elementor_bad_shape', __( 'settings_patch muss ein Objekt und remove_settings eine Liste mit höchstens 100 Namen sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$remove = array_values( array_unique( array_filter( $remove, 'is_string' ) ) );
	if ( count( $remove ) !== count( $input['remove_settings'] ?? array() ) ) {
		return new WP_Error( 'wpagent_elementor_bad_control', __( 'remove_settings enthält ungültige oder doppelte Control-Namen.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	if ( array_intersect( array_keys( $patch ), $remove ) ) {
		return new WP_Error( 'wpagent_elementor_control_conflict', __( 'Ein Control darf nicht zugleich geändert und entfernt werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	$loaded = wpagent_elementor_editable_document( $post_id );
	if ( is_wp_error( $loaded ) ) {
		return $loaded;
	}
	$lock = wpagent_elementor_acquire_lock( $post_id );
	if ( is_wp_error( $lock ) ) {
		return $lock;
	}
	try {
		$loaded = wpagent_elementor_editable_document( $post_id );
		if ( is_wp_error( $loaded ) ) {
			return $loaded;
		}
		$before = wpagent_elementor_document_state( $post_id, $loaded['document'] );
		if ( ! hash_equals( $before['hash'], $expected_hash ) ) {
			return new WP_Error( 'wpagent_elementor_conflict', __( 'Das Elementor-Dokument wurde seit dem Lesen verändert.', 'wp-agent-companion' ), array( 'status' => 409, 'current_hash' => $before['hash'] ) );
		}
		$located = wpagent_elementor_locate_element( $before['elements'], $element_id );
		if ( is_wp_error( $located ) ) {
			return $located;
		}
		$registered = wpagent_elementor_registered_element( $located['element'] );
		if ( is_wp_error( $registered ) ) {
			return $registered;
		}
		$controls = wpagent_elementor_writable_controls( $registered, $controls_total, true );
		if ( is_wp_error( $controls ) ) {
			return $controls;
		}
		foreach ( array_merge( array_keys( $patch ), $remove ) as $control_name ) {
			if ( ! is_string( $control_name ) || ! isset( $controls[ $control_name ] ) ) {
				return new WP_Error( 'wpagent_elementor_unknown_control', __( 'Das Elementor-Element kennt mindestens eines der angeforderten Controls nicht oder es ist nicht schreibbar.', 'wp-agent-companion' ), array( 'status' => 400, 'control' => is_scalar( $control_name ) ? (string) $control_name : '' ) );
			}
			if ( wpagent_elementor_control_is_style( $controls[ $control_name ] ) ) {
				return new WP_Error( 'wpagent_elementor_style_requires_patch', __( 'CSS-nahe Elementor-Controls müssen über style_patch mit Geräteebene geändert oder entfernt werden.', 'wp-agent-companion' ), array( 'status' => 400, 'control' => $control_name ) );
			}
		}
		$style = wpagent_elementor_normalize_style_patch( $input['style_patch'] ?? array(), $controls );
		if ( is_wp_error( $style ) ) {
			return $style;
		}
		if ( array_intersect( array_keys( $patch ), $style['remove'] ) || array_intersect( $remove, array_keys( $style['patch'] ) ) ) {
			return new WP_Error( 'wpagent_elementor_control_conflict', __( 'Ein Control darf nicht zugleich geändert und entfernt werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		}
		$patch = array_merge( $patch, $style['patch'] );
		$remove = array_merge( $remove, $style['remove'] );
		$valid = wpagent_elementor_validate_settings( $patch );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
		$settings = is_array( $located['element']['settings'] ?? null ) ? $located['element']['settings'] : array();
		foreach ( $patch as $name => $value ) {
			$settings[ $name ] = $value;
		}
		foreach ( $remove as $name ) {
			unset( $settings[ $name ] );
		}
		if ( $settings === ( $located['element']['settings'] ?? array() ) ) {
			$frontend = wpagent_elementor_verify_frontend( $post_id, $before['element_count'] );
			if ( is_wp_error( $frontend ) ) {
				return $frontend;
			}
			if ( $style['has_values'] ) {
				$style_frontend = wpagent_elementor_verify_style_render( $post_id, $element_id, $style['render_tokens'] );
				if ( is_wp_error( $style_frontend ) ) {
					return $style_frontend;
				}
			}
			$before['verified'] = true;
			$before['frontend_verified'] = true;
			$before['previous_hash'] = $before['hash'];
			$before['revision_id'] = 0;
			$before['element_id'] = $element_id;
			$before['path'] = $located['path'];
			$before['element'] = $located['element'];
			$before['changed'] = false;
			return $before;
		}
		$elements = $before['elements'];
		if ( ! wpagent_elementor_replace_element_settings( $elements, $element_id, $settings ) ) {
			return new WP_Error( 'wpagent_elementor_element_not_found', __( 'Elementor-Element nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
		}
		$normalized = wpagent_elementor_normalize_elements( $loaded['document'], $elements );
		if ( is_wp_error( $normalized ) ) {
			return $normalized;
		}
		$revision_id = wpagent_elementor_save_revision( $post_id, $before['hash'] );
		if ( is_wp_error( $revision_id ) ) {
			return $revision_id;
		}
		try {
			$saved = wpagent_elementor_save_document( $loaded['document'], array( 'elements' => $elements ), false );
		} catch ( Throwable ) {
			$rolled_back = wpagent_elementor_restore_revision_internal( $post_id, $revision_id, $before['hash'] );
			return new WP_Error( 'wpagent_elementor_save_failed', __( 'Elementor konnte das Dokument nicht speichern.', 'wp-agent-companion' ), array( 'status' => 500, 'rolled_back' => $rolled_back ) );
		}
		$fresh_document = \Elementor\Plugin::$instance->documents->get( $post_id, false );
		$after = is_object( $fresh_document ) ? wpagent_elementor_document_state( $post_id, $fresh_document ) : null;
		$fresh_element = is_array( $after ) ? wpagent_elementor_locate_element( $after['elements'], $element_id ) : null;
		$verified = true === $saved && is_array( $after ) && wpagent_elementor_elements_contain_requested( $after['elements'], $normalized ) && is_array( $fresh_element );
		$frontend = $verified ? wpagent_elementor_verify_frontend( $post_id, $after['element_count'] ) : false;
		$style_frontend = $verified && $style['has_values'] ? wpagent_elementor_verify_style_render( $post_id, $element_id, $style['render_tokens'] ) : true;
		if ( ! $verified || is_wp_error( $frontend ) || is_wp_error( $style_frontend ) ) {
			$rolled_back = wpagent_elementor_restore_revision_internal( $post_id, $revision_id, $before['hash'] );
			return new WP_Error(
				'wpagent_elementor_verify_failed',
				__( 'Die gezielte Elementor-Änderung bestand die Rücklese- oder Frontend-Prüfung nicht.', 'wp-agent-companion' ),
				array( 'status' => 500, 'rolled_back' => $rolled_back, 'revision_id' => $revision_id )
			);
		}
		$after['verified'] = true;
		$after['frontend_verified'] = true;
		$after['previous_hash'] = $before['hash'];
		$after['revision_id'] = $revision_id;
		$after['element_id'] = $element_id;
		$after['path'] = $fresh_element['path'];
		$after['element'] = $fresh_element['element'];
		$after['changed'] = true;
		return $after;
	} finally {
		wpagent_elementor_release_lock( $post_id, $lock );
	}
}
