<?php
/** Conflict-safe writes for bounded ACPT Lite meta groups and values. */

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

/** @return array<string,mixed> */
function wpagent_acpt_meta_definition_properties( bool $create ): array {
	$properties = array(
		'name' => array( 'type' => 'string', 'pattern' => '^[a-z][a-z0-9_]{2,39}$' ),
		'label' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 100 ),
		'target' => array(
			'type' => 'object',
			'properties' => array(
				'type' => array( 'type' => 'string', 'enum' => array( 'post_type', 'taxonomy', 'user', 'comment' ) ),
				'name' => array( 'type' => 'string', 'maxLength' => 32 ),
			),
			'required' => array( 'type', 'name' ), 'additionalProperties' => false,
		),
		'context' => array( 'type' => 'string', 'enum' => array( 'normal', 'advanced', 'side' ) ),
		'priority' => array( 'type' => 'string', 'enum' => array( 'default', 'high', 'low' ) ),
		'boxes' => array(
			'type' => 'array', 'minItems' => 1, 'maxItems' => WPAGENT_ACPT_META_MAX_BOXES,
			'items' => array(
				'type' => 'object',
				'properties' => array(
					'name' => array( 'type' => 'string', 'pattern' => '^[a-z][a-z0-9_]{2,39}$' ),
					'label' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 100 ),
					'fields' => array(
						'type' => 'array', 'minItems' => 1, 'maxItems' => WPAGENT_ACPT_META_MAX_FIELDS,
						'items' => array(
							'type' => 'object',
							'properties' => array(
								'name' => array( 'type' => 'string', 'pattern' => '^[a-z][a-z0-9_]{2,39}$' ),
								'label' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 100 ),
								'type' => array( 'type' => 'string', 'enum' => array( 'date', 'email', 'select', 'text', 'textarea' ) ),
								'required' => array( 'type' => 'boolean' ),
								'options' => array(
									'type' => 'array', 'maxItems' => 20,
									'items' => array(
										'type' => 'object',
										'properties' => array( 'label' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 100 ), 'value' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 100 ) ),
										'required' => array( 'label', 'value' ), 'additionalProperties' => false,
									),
								),
							),
							'required' => array( 'name', 'label', 'type', 'required' ), 'additionalProperties' => false,
						),
					),
				),
				'required' => array( 'name', 'label', 'fields' ), 'additionalProperties' => false,
			),
		),
	);
	if ( ! $create ) $properties['expected_hash'] = array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' );
	return $properties;
}

/** @return array<string,mixed> */
function wpagent_acpt_meta_definition_mutation_schema(): array {
	return array(
		'type' => 'object', 'properties' => array(
			'verified' => array( 'type' => 'boolean' ), 'changed' => array( 'type' => 'boolean' ), 'group' => wpagent_acpt_meta_group_schema(),
		),
		'required' => array( 'verified', 'changed', 'group' ), 'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_ability_create_acpt_meta_group(): array {
	return array(
		'label' => __( 'ACPT-Lite-Metagruppe erstellen', 'wp-agent-companion' ),
		'description' => __( 'Erstellt eine kleine ACPT-Lite-Metagruppe mit genau einer Zielbindung, bis zu 20 einfachen Feldern und vollständigem Provider-Readback.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array( 'type' => 'object', 'properties' => wpagent_acpt_meta_definition_properties( true ), 'required' => array( 'name', 'label', 'target', 'context', 'priority', 'boxes' ), 'additionalProperties' => false ),
		'output_schema' => wpagent_acpt_meta_definition_mutation_schema(), 'execute_callback' => 'wpagent_exec_create_acpt_meta_group', 'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

/** @return array<string,mixed> */
function wpagent_ability_update_acpt_meta_group(): array {
	return array(
		'label' => __( 'ACPT-Lite-Metagruppe aktualisieren', 'wp-agent-companion' ),
		'description' => __( 'Ändert nur Beschriftungen, Position und Pflichtstatus einer strukturell unveränderten ACPT-Lite-Metagruppe mit Konflikthash und exaktem Readback.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array( 'type' => 'object', 'properties' => wpagent_acpt_meta_definition_properties( false ), 'required' => array( 'name', 'expected_hash' ), 'minProperties' => 3, 'additionalProperties' => false ),
		'output_schema' => wpagent_acpt_meta_definition_mutation_schema(), 'execute_callback' => 'wpagent_exec_update_acpt_meta_group', 'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => true ) ),
	);
}

/** @return array<string,mixed> */
function wpagent_ability_delete_acpt_meta_group(): array {
	return array(
		'label' => __( 'ACPT-Lite-Metagruppe löschen', 'wp-agent-companion' ),
		'description' => __( 'Löscht nach Bestätigung nur die ACPT-Lite-Felddefinition. Bereits gespeicherte Objektwerte bleiben erhalten.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object', 'properties' => array(
				'name' => array( 'type' => 'string', 'pattern' => '^[a-z][a-z0-9_]{2,39}$' ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ), 'confirm' => array( 'type' => 'boolean' ),
			),
			'required' => array( 'name', 'expected_hash', 'confirm' ), 'additionalProperties' => false,
		),
		'output_schema' => array(
			'type' => 'object', 'properties' => array(
				'deleted' => array( 'type' => 'boolean' ), 'name' => array( 'type' => 'string' ), 'retained_values' => array( 'type' => 'boolean' ),
			),
			'required' => array( 'deleted', 'name', 'retained_values' ), 'additionalProperties' => false,
		),
		'execute_callback' => 'wpagent_exec_delete_acpt_meta_group', 'permission_callback' => 'wpagent_perm_manage_options',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => true ) ),
	);
}

/** @return array<string,mixed> */
function wpagent_ability_update_acpt_field_values(): array {
	$values_schema = wpagent_acpt_meta_values_schema();
	$values_schema['properties']['verified'] = array( 'type' => 'boolean' );
	$values_schema['properties']['changed'] = array( 'type' => 'boolean' );
	$values_schema['required'][] = 'verified'; $values_schema['required'][] = 'changed';
	return array(
		'label' => __( 'ACPT-Lite-Feldwerte aktualisieren', 'wp-agent-companion' ),
		'description' => __( 'Schreibt bis zu 20 registrierte einfache ACPT-Lite-Werte nativ mit Objektberechtigung, Konflikthash, Sperre und exaktem Roh-Readback. Unklare Teilwrites verlangen manuelle Wiederherstellung.', 'wp-agent-companion' ),
		'category' => 'wp-agent',
		'input_schema' => array(
			'type' => 'object', 'properties' => array(
				'group' => array( 'type' => 'string', 'pattern' => '^[a-z][a-z0-9_]{2,39}$' ),
				'object_type' => array( 'type' => 'string', 'enum' => array( 'post', 'term', 'user', 'comment' ) ),
				'object_id' => array( 'type' => 'integer', 'minimum' => 1 ), 'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'values' => array( 'type' => 'object', 'minProperties' => 1, 'maxProperties' => WPAGENT_ACPT_META_MAX_FIELDS, 'additionalProperties' => array( 'type' => 'string', 'maxLength' => 5000 ) ),
			),
			'required' => array( 'group', 'object_type', 'object_id', 'expected_hash', 'values' ), 'additionalProperties' => false,
		),
		'output_schema' => $values_schema, 'execute_callback' => 'wpagent_exec_update_acpt_field_values', 'permission_callback' => 'wpagent_perm_acpt_meta_values',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => true ) ),
	);
}

/** @return WP_Error */
function wpagent_acpt_meta_recovery_error( string $message, array $current = array() ) {
	return new WP_Error( 'wpagent_acpt_meta_recovery_required', $message, array( 'status' => 409, 'recovery_required' => true, 'current' => $current ) );
}

/** @return true|WP_Error */
function wpagent_acpt_meta_check_new_names( array $definition, array $provider ) {
	global $wpdb;
	try {
		$box_table = $provider['database']::prefixedTableName( $provider['database']::TABLE_META_BOX );
		$field_table = $provider['database']::prefixedTableName( $provider['database']::TABLE_META_FIELD );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_acpt_meta_names', __( 'Die ACPT-Lite-Bezeichnertabellen konnten nicht sicher aufgelöst werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	if ( ! is_string( $box_table ) || ! is_string( $field_table ) || ! preg_match( '/^[A-Za-z0-9_]+$/D', $box_table ) || ! preg_match( '/^[A-Za-z0-9_]+$/D', $field_table ) ) {
		return new WP_Error( 'wpagent_acpt_meta_names', __( 'Die ACPT-Lite-Bezeichnertabellen sind nicht kanonisch.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$wpdb->last_error = '';
	$box_names = $wpdb->get_col( "SELECT meta_box_name FROM `{$box_table}` ORDER BY meta_box_name ASC LIMIT 10001" );
	$box_error = $wpdb->last_error;
	$wpdb->last_error = '';
	$field_names = $wpdb->get_col( "SELECT field_name FROM `{$field_table}` ORDER BY field_name ASC LIMIT 10001" );
	$field_error = $wpdb->last_error;
	if ( '' !== $box_error || '' !== $field_error || ! is_array( $box_names ) || ! is_array( $field_names ) || count( $box_names ) > 10000 || count( $field_names ) > 10000 ) {
		return new WP_Error( 'wpagent_acpt_meta_names', __( 'Die vorhandenen ACPT-Lite-Bezeichner konnten nicht vollständig und begrenzt gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	$box_names = array_values( array_filter( $box_names, 'is_string' ) );
	$field_names = array_values( array_filter( $field_names, 'is_string' ) );
	foreach ( $definition['boxes'] as $box ) {
		if ( in_array( $box['name'], $box_names, true ) ) return new WP_Error( 'wpagent_acpt_meta_name_conflict', __( 'Ein ACPT-Lite-Metaboxname wird bereits verwendet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		foreach ( $box['fields'] as $field ) if ( in_array( $field['name'], $field_names, true ) ) return new WP_Error( 'wpagent_acpt_meta_name_conflict', __( 'Ein ACPT-Lite-Feldname wird bereits verwendet.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return true;
}

function wpagent_acpt_meta_structurally_equal( array $before, array $after ): bool {
	$shape = static function ( array $definition ): array {
		return array(
			'target' => $definition['target'],
			'boxes' => array_map( static fn( array $box ): array => array(
				'name' => $box['name'],
				'fields' => array_map( static fn( array $field ): array => array( 'name' => $field['name'], 'type' => $field['type'], 'options' => array_column( $field['options'], 'value' ) ), $box['fields'] ),
			), $definition['boxes'] ),
		);
	};
	return wpagent_acpt_hash( $shape( $before ) ) === wpagent_acpt_hash( $shape( $after ) );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_meta_save_definition( array $definition, ?array $existing, string $scope, string $token ) {
	$provider = wpagent_acpt_meta_provider();
	if ( is_wp_error( $provider ) ) return $provider;
	if ( ! current_user_can( 'manage_options' ) ) return new WP_Error( 'wpagent_acpt_meta_forbidden', __( 'Du darfst ACPT-Lite-Metadaten nicht mehr verwalten.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	$target = wpagent_acpt_meta_target( $definition['target'] ?? null );
	if ( is_wp_error( $target ) || $target !== $definition['target'] ) return is_wp_error( $target ) ? $target : new WP_Error( 'wpagent_acpt_meta_target', __( 'Das ACPT-Lite-Ziel wurde vor dem Providerwrite geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	if ( ! wpagent_acpt_meta_refresh_lock( $scope, $token ) ) return new WP_Error( 'wpagent_acpt_meta_lock', __( 'Die ACPT-Lite-Sperre wurde vor dem Providerwrite verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$started = false;
	try {
		$payload = wpagent_acpt_meta_provider_payload( $definition, $existing );
		$command_class = $provider['command'];
		$command = new $command_class( $payload );
		$started = true;
		$result_id = $command->execute();
		if ( ! is_string( $result_id ) || '' === $result_id ) throw new UnexpectedValueException( 'Invalid ACPT write result.' );
		do_action( 'wpagent_acpt_meta_after_definition_write', $definition['name'], $result_id );
		if ( ! wpagent_acpt_meta_refresh_lock( $scope, $token ) ) throw new RuntimeException( 'ACPT lock lost after write.' );
		$cache = wpagent_acpt_flush_provider_cache( $provider );
		if ( is_wp_error( $cache ) ) throw new RuntimeException( $cache->get_error_message() );
		$current = wpagent_acpt_meta_group_state( $definition['name'] );
		if ( is_wp_error( $current ) || $result_id !== $current['id'] || wpagent_acpt_hash( wpagent_acpt_meta_public_definition( $current ) ) !== wpagent_acpt_hash( $definition ) ) throw new RuntimeException( 'ACPT readback mismatch.' );
		$current_target = wpagent_acpt_meta_target( $current['target'] );
		if ( is_wp_error( $current_target ) || $current_target !== $current['target'] ) throw new RuntimeException( 'ACPT target drift after write.' );
		if ( ! current_user_can( 'manage_options' ) ) throw new RuntimeException( 'Capability lost after ACPT write.' );
		if ( ! wpagent_acpt_meta_refresh_lock( $scope, $token ) ) throw new RuntimeException( 'ACPT lock lost after readback.' );
		return $current;
	} catch ( Throwable $error ) {
		$current = wpagent_acpt_meta_group_state( $definition['name'] );
		$current = is_wp_error( $current ) ? array() : $current;
		return $started
			? wpagent_acpt_meta_recovery_error( __( 'Der ACPT-Lite-Providerwrite wurde begonnen, aber nicht vollständig bestätigt. Es erfolgt keine automatische Folgemutation.', 'wp-agent-companion' ), $current )
			: new WP_Error( 'wpagent_acpt_meta_write', __( 'Die ACPT-Lite-Metagruppe konnte nicht geschrieben werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_create_acpt_meta_group( $input ) {
	$input = is_array( $input ) ? $input : array();
	$definition = wpagent_acpt_meta_validate_definition( $input, true );
	if ( is_wp_error( $definition ) ) return $definition;
	$scope = 'definitions'; $token = wpagent_acpt_meta_acquire_lock( $scope );
	if ( false === $token ) return new WP_Error( 'wpagent_acpt_meta_locked', __( 'Eine andere ACPT-Lite-Metadatenänderung läuft bereits.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	try {
		if ( ! current_user_can( 'manage_options' ) ) return new WP_Error( 'wpagent_acpt_meta_forbidden', __( 'Du darfst ACPT-Lite-Metadaten nicht mehr verwalten.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		$provider = wpagent_acpt_meta_provider();
		if ( is_wp_error( $provider ) ) return $provider;
		$current = wpagent_acpt_meta_group_state( $definition['name'] );
		if ( ! is_wp_error( $current ) || ! in_array( 'wpagent_acpt_meta_not_found', $current->get_error_codes(), true ) ) return is_wp_error( $current ) ? $current : new WP_Error( 'wpagent_acpt_meta_exists', __( 'Die ACPT-Lite-Metagruppe existiert bereits.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$names = wpagent_acpt_meta_check_new_names( $definition, $provider );
		if ( is_wp_error( $names ) ) return $names;
		$saved = wpagent_acpt_meta_save_definition( $definition, null, $scope, $token );
		return is_wp_error( $saved ) ? $saved : array( 'verified' => true, 'changed' => true, 'group' => $saved );
	} finally { wpagent_acpt_meta_release_lock( $scope, $token ); }
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_update_acpt_meta_group( $input ) {
	$input = is_array( $input ) ? $input : array();
	$changes = wpagent_acpt_meta_validate_definition( $input, false );
	if ( is_wp_error( $changes ) ) return $changes;
	$expected_hash = is_string( $input['expected_hash'] ?? null ) ? $input['expected_hash'] : '';
	$scope = 'definitions'; $token = wpagent_acpt_meta_acquire_lock( $scope );
	if ( false === $token ) return new WP_Error( 'wpagent_acpt_meta_locked', __( 'Eine andere ACPT-Lite-Metadatenänderung läuft bereits.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	try {
		if ( ! current_user_can( 'manage_options' ) ) return new WP_Error( 'wpagent_acpt_meta_forbidden', __( 'Du darfst ACPT-Lite-Metadaten nicht mehr verwalten.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		$current = wpagent_acpt_meta_group_state( $changes['name'] );
		if ( is_wp_error( $current ) ) return $current;
		if ( ! preg_match( '/^[a-f0-9]{64}$/D', $expected_hash ) || ! hash_equals( $current['definition_hash'], $expected_hash ) ) return new WP_Error( 'wpagent_acpt_meta_conflict', __( 'Die ACPT-Lite-Metagruppe wurde seit dem Lesen geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$definition = array_replace( wpagent_acpt_meta_public_definition( $current ), $changes );
		$definition['boxes'] = wpagent_acpt_meta_validate_boxes( $definition['boxes'] );
		if ( is_wp_error( $definition['boxes'] ) ) return $definition['boxes'];
		if ( ! wpagent_acpt_meta_structurally_equal( wpagent_acpt_meta_public_definition( $current ), $definition ) ) return new WP_Error( 'wpagent_acpt_meta_structure', __( 'Dieser Updatepfad erlaubt keine Änderungen an Ziel, Boxnamen, Feldnamen, Feldtypen oder Select-Werten.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		if ( wpagent_acpt_hash( wpagent_acpt_meta_public_definition( $current ) ) === wpagent_acpt_hash( $definition ) ) return array( 'verified' => true, 'changed' => false, 'group' => $current );
		$saved = wpagent_acpt_meta_save_definition( $definition, $current, $scope, $token );
		return is_wp_error( $saved ) ? $saved : array( 'verified' => true, 'changed' => true, 'group' => $saved );
	} finally { wpagent_acpt_meta_release_lock( $scope, $token ); }
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_delete_acpt_meta_group( $input ) {
	$input = is_array( $input ) ? $input : array();
	$name = wpagent_acpt_meta_identifier( $input['name'] ?? '', 'name' );
	if ( is_wp_error( $name ) ) return $name;
	if ( true !== ( $input['confirm'] ?? false ) ) return new WP_Error( 'wpagent_acpt_meta_confirm', __( 'Das Löschen der ACPT-Lite-Metagruppe muss ausdrücklich bestätigt werden.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$expected_hash = is_string( $input['expected_hash'] ?? null ) ? $input['expected_hash'] : '';
	$scope = 'definitions'; $token = wpagent_acpt_meta_acquire_lock( $scope );
	if ( false === $token ) return new WP_Error( 'wpagent_acpt_meta_locked', __( 'Eine andere ACPT-Lite-Metadatenänderung läuft bereits.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$started = false;
	try {
		if ( ! current_user_can( 'manage_options' ) ) return new WP_Error( 'wpagent_acpt_meta_forbidden', __( 'Du darfst ACPT-Lite-Metadaten nicht mehr verwalten.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		$current = wpagent_acpt_meta_group_state( $name );
		if ( is_wp_error( $current ) ) return $current;
		if ( ! preg_match( '/^[a-f0-9]{64}$/D', $expected_hash ) || ! hash_equals( $current['definition_hash'], $expected_hash ) ) return new WP_Error( 'wpagent_acpt_meta_conflict', __( 'Die ACPT-Lite-Metagruppe wurde seit dem Lesen geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		if ( ! wpagent_acpt_meta_refresh_lock( $scope, $token ) ) return new WP_Error( 'wpagent_acpt_meta_lock', __( 'Die ACPT-Lite-Sperre wurde vor dem Löschen verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$provider = wpagent_acpt_meta_provider();
		if ( is_wp_error( $provider ) ) return $provider;
		$started = true; $provider['repository']::deleteMetaGroup( $current['id'] );
		do_action( 'wpagent_acpt_meta_after_definition_delete', $name, $current['id'] );
		if ( ! wpagent_acpt_meta_refresh_lock( $scope, $token ) ) throw new RuntimeException( 'Lock lost.' );
		$cache = wpagent_acpt_flush_provider_cache( $provider );
		if ( is_wp_error( $cache ) ) throw new RuntimeException( $cache->get_error_message() );
		$after = wpagent_acpt_meta_group_state( $name );
		if ( ! is_wp_error( $after ) || ! in_array( 'wpagent_acpt_meta_not_found', $after->get_error_codes(), true ) ) throw new RuntimeException( 'Delete readback mismatch.' );
		if ( ! current_user_can( 'manage_options' ) ) throw new RuntimeException( 'Capability lost after ACPT delete.' );
		if ( ! wpagent_acpt_meta_refresh_lock( $scope, $token ) ) throw new RuntimeException( 'Lock lost after readback.' );
		return array( 'deleted' => true, 'name' => $name, 'retained_values' => true );
	} catch ( Throwable $error ) {
		return $started ? wpagent_acpt_meta_recovery_error( __( 'Das Löschen der ACPT-Lite-Definition wurde begonnen, aber nicht vollständig bestätigt. Gespeicherte Objektwerte werden nicht verändert.', 'wp-agent-companion' ) ) : new WP_Error( 'wpagent_acpt_meta_delete', __( 'Die ACPT-Lite-Metagruppe konnte nicht gelöscht werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	} finally { wpagent_acpt_meta_release_lock( $scope, $token ); }
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_update_acpt_field_values( $input ) {
	$input = is_array( $input ) ? $input : array();
	$name = wpagent_acpt_meta_identifier( $input['group'] ?? '', 'group' );
	if ( is_wp_error( $name ) ) return $name;
	$group = wpagent_acpt_meta_group_state( $name );
	if ( is_wp_error( $group ) ) return $group;
	$object_type = is_string( $input['object_type'] ?? null ) ? $input['object_type'] : '';
	$context = wpagent_acpt_meta_object_context( $object_type, $input['object_id'] ?? null, $group );
	if ( is_wp_error( $context ) ) return $context;
	if ( ! wpagent_acpt_meta_can_edit_object( $context ) ) return new WP_Error( 'wpagent_acpt_meta_forbidden', __( 'Du darfst die ACPT-Lite-Werte dieses Objekts nicht ändern.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	$values = $input['values'] ?? null; $expected_hash = is_string( $input['expected_hash'] ?? null ) ? $input['expected_hash'] : '';
	if ( ! is_array( $values ) || array() === $values || array_is_list( $values ) || count( $values ) > WPAGENT_ACPT_META_MAX_FIELDS ) return new WP_Error( 'wpagent_acpt_meta_values', __( 'values muss 1 bis 20 benannte Feldwerte enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$fields = array();
	foreach ( $group['boxes'] as $box ) foreach ( $box['fields'] as $field ) $fields[ $field['name'] ] = array( 'box' => $box['name'], 'field' => $field );
	$validated = array();
	foreach ( $values as $field_name => $value ) {
		if ( ! is_string( $field_name ) || ! isset( $fields[ $field_name ] ) ) return new WP_Error( 'wpagent_acpt_meta_field_unknown', __( 'values enthält ein nicht registriertes ACPT-Lite-Feld.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		$valid = wpagent_acpt_meta_validate_value( $fields[ $field_name ]['field'], $value );
		if ( is_wp_error( $valid ) ) return $valid;
		$validated[ $field_name ] = $valid;
	}
	ksort( $validated, SORT_STRING );
	$current = wpagent_acpt_meta_values_state( $group, $context );
	if ( is_wp_error( $current ) ) return $current;
	if ( ! preg_match( '/^[a-f0-9]{64}$/D', $expected_hash ) || ! hash_equals( $current['values_hash'], $expected_hash ) ) return new WP_Error( 'wpagent_acpt_meta_conflict', __( 'Die ACPT-Lite-Feldwerte wurden seit dem Lesen geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$changed = array_filter( $validated, static fn( string $value, string $field_name ): bool => $current['values'][ $field_name ] !== $value, ARRAY_FILTER_USE_BOTH );
	if ( array() === $changed ) return $current + array( 'verified' => true, 'changed' => false );
	$scope = 'values:' . $context['type'] . ':' . $context['id'] . ':' . $name; $token = wpagent_acpt_meta_acquire_lock( $scope );
	if ( false === $token ) return new WP_Error( 'wpagent_acpt_meta_locked', __( 'Eine andere ACPT-Lite-Wertänderung läuft bereits.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$started = false;
	try {
		$locked_group = wpagent_acpt_meta_group_state( $name );
		if ( is_wp_error( $locked_group ) || ! hash_equals( $group['definition_hash'], $locked_group['definition_hash'] ) ) return new WP_Error( 'wpagent_acpt_meta_conflict', __( 'Die ACPT-Lite-Metagruppe wurde vor dem Schreiben geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$locked_context = wpagent_acpt_meta_object_context( $object_type, $context['id'], $locked_group );
		if ( is_wp_error( $locked_context ) || ! wpagent_acpt_meta_can_edit_object( $locked_context ) ) return new WP_Error( 'wpagent_acpt_meta_forbidden', __( 'Das Ziel oder seine Berechtigung wurde vor dem Schreiben geändert.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		$locked_state = wpagent_acpt_meta_values_state( $locked_group, $locked_context );
		if ( is_wp_error( $locked_state ) ) return $locked_state;
		if ( ! hash_equals( $current['values_hash'], $locked_state['values_hash'] ) ) return new WP_Error( 'wpagent_acpt_meta_conflict', __( 'Die ACPT-Lite-Feldwerte wurden vor dem Schreiben geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$expected_values = $locked_state['values'];
		foreach ( $changed as $field_name => $value ) {
			if ( ! wpagent_acpt_meta_refresh_lock( $scope, $token ) ) throw new RuntimeException( 'Lock lost before value write.' );
			$before = wpagent_acpt_meta_values_state( $locked_group, $locked_context );
			if ( is_wp_error( $before ) ) throw new RuntimeException( 'Invalid ACPT value state before write.' );
			if ( $before['values'][ $field_name ] === $value ) continue;
			$entry = $fields[ $field_name ];
			$args = array( $locked_context['provider_key'] => $locked_context['id'], 'box_name' => $entry['box'], 'field_name' => $field_name, 'value' => $value );
			$started = true;
			if ( true !== save_acpt_meta_field_value( $args ) ) throw new RuntimeException( 'Native ACPT value write failed.' );
			do_action( 'wpagent_acpt_meta_after_value_write', $name, $locked_context, $field_name, $value );
			if ( ! wpagent_acpt_meta_refresh_lock( $scope, $token ) ) throw new RuntimeException( 'Lock lost after value write.' );
			$after = wpagent_acpt_meta_values_state( $locked_group, $locked_context );
			if ( is_wp_error( $after ) ) throw new RuntimeException( 'Invalid ACPT value state after write.' );
			$expected_values[ $field_name ] = $value;
			ksort( $expected_values, SORT_STRING );
			$key = $entry['box'] . '_' . $field_name;
			$provider_type = ucfirst( $entry['field']['type'] );
			if ( $after['values'] !== $expected_values || get_metadata( $locked_context['type'], $locked_context['id'], $key . '_type', true ) !== $provider_type ) throw new RuntimeException( 'Native ACPT value readback failed.' );
		}
		$final = wpagent_acpt_meta_values_state( $locked_group, $locked_context );
		if ( is_wp_error( $final ) ) throw new RuntimeException( 'Invalid final ACPT value state.' );
		if ( $final['values'] !== $expected_values ) throw new RuntimeException( 'Final ACPT value readback failed.' );
		if ( ! wpagent_acpt_meta_refresh_lock( $scope, $token ) ) throw new RuntimeException( 'Lock lost after final readback.' );
		$final_group = wpagent_acpt_meta_group_state( $name );
		if ( is_wp_error( $final_group ) || ! hash_equals( $locked_group['definition_hash'], $final_group['definition_hash'] ) ) throw new RuntimeException( 'Definition drift after value write.' );
		$final_context = wpagent_acpt_meta_object_context( $object_type, $locked_context['id'], $final_group );
		if ( is_wp_error( $final_context ) || ! wpagent_acpt_meta_can_edit_object( $final_context ) ) throw new RuntimeException( 'Object or capability drift after value write.' );
		if ( ! wpagent_acpt_meta_refresh_lock( $scope, $token ) ) throw new RuntimeException( 'Lock lost before success.' );
		return $final + array( 'verified' => true, 'changed' => true );
	} catch ( Throwable $error ) {
		$current_state = wpagent_acpt_meta_values_state( $group, $context );
		$current_state = is_wp_error( $current_state ) ? array() : $current_state;
		return $started ? wpagent_acpt_meta_recovery_error( __( 'Ein ACPT-Lite-Wertwrite wurde begonnen, aber nicht vollständig bestätigt. Es erfolgt keine automatische Folgemutation.', 'wp-agent-companion' ), $current_state ) : new WP_Error( 'wpagent_acpt_meta_write', __( 'Die ACPT-Lite-Feldwerte konnten nicht geschrieben werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	} finally { wpagent_acpt_meta_release_lock( $scope, $token ); }
}
