<?php
/** Guarded update ability for ACPT Lite WooCommerce product-data configuration. */

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

/** @return array<string,mixed> */
function wpagent_ability_create_acpt_woocommerce_product_data(): array {
	return array(
		'label'       => __( 'ACPT-Lite-WooCommerce-Product-Data anlegen', 'wp-agent-companion' ),
		'description' => __( 'Legt ausschließlich eine neue, verifizierte ACPT-Lite-2.0.11-Product-Data-Gruppe mit Text-, Number- und Select-Feldern an. Bestehende Gruppen, Felder, Optionen und Providerinhalte werden nie überschrieben.', 'wp-agent-companion' ),
		'category'    => 'wp-agent',
		'input_schema' => wpagent_acpt_product_data_create_input_schema(),
		'output_schema' => wpagent_acpt_product_data_create_output_schema(),
		'execute_callback' => 'wpagent_exec_create_acpt_woocommerce_product_data',
		'permission_callback' => 'wpagent_perm_acpt_product_data',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

/** @return array<string,mixed> */
function wpagent_ability_delete_acpt_woocommerce_product_data(): array {
	return array(
		'label'       => __( 'ACPT-Lite-WooCommerce-Product-Data löschen', 'wp-agent-companion' ),
		'description' => __( 'Löscht eine bestehende sichere ACPT-Lite-2.0.11-Product-Data-Gruppe einschließlich ihrer Feld- und Optionsdefinitionen erst nach ausdrücklicher Datenverlustbestätigung und vollständiger Zustandsprüfung.', 'wp-agent-companion' ),
		'category'    => 'wp-agent',
		'input_schema' => array(
			'type' => 'object', 'properties' => array(
				'id' => array( 'type' => 'string', 'pattern' => '^[a-f0-9-]{36}$' ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'confirm' => array( 'type' => 'boolean' ),
			), 'required' => array( 'id', 'expected_hash', 'confirm' ), 'additionalProperties' => false,
		),
		'output_schema' => array(
			'type' => 'object', 'properties' => array( 'verified' => array( 'type' => 'boolean' ), 'deleted' => array( 'type' => 'boolean' ) ),
			'required' => array( 'verified', 'deleted' ), 'additionalProperties' => false,
		),
		'execute_callback' => 'wpagent_exec_delete_acpt_woocommerce_product_data',
		'permission_callback' => 'wpagent_perm_acpt_product_data',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => true, 'idempotent' => false ) ),
	);
}

/** @return array<string,mixed> */
function wpagent_acpt_product_data_create_input_schema(): array {
	$field = array(
		'type' => 'object', 'properties' => array(
			'id' => array( 'type' => 'string', 'pattern' => '^[a-f0-9-]{36}$' ), 'name' => array( 'type' => 'string', 'minLength' => 2, 'maxLength' => 50 ),
			'type' => array( 'type' => 'string', 'enum' => array( 'Text', 'Number', 'Select' ) ), 'default_value' => array( 'type' => 'string', 'maxLength' => 50 ),
			'description' => array( 'type' => 'string', 'maxLength' => 500 ), 'required' => array( 'type' => 'boolean' ),
			'options' => array( 'type' => 'array', 'maxItems' => WPAGENT_ACPT_PRODUCT_DATA_MAX_OPTIONS, 'items' => array(
				'type' => 'object', 'properties' => array( 'id' => array( 'type' => 'string', 'pattern' => '^[a-f0-9-]{36}$' ), 'label' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 50 ), 'value' => array( 'type' => 'string', 'minLength' => 1, 'maxLength' => 50 ) ),
				'required' => array( 'id', 'label', 'value' ), 'additionalProperties' => false,
			) ),
		), 'required' => array( 'id', 'name', 'type', 'default_value', 'description', 'required', 'options' ), 'additionalProperties' => false,
	);
	return array(
		'type' => 'object', 'properties' => array(
			'id' => array( 'type' => 'string', 'pattern' => '^[a-f0-9-]{36}$' ), 'expected_absent_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
			'idempotency_key' => array( 'type' => 'string', 'minLength' => 8, 'maxLength' => 64, 'pattern' => '^[A-Za-z0-9._:-]+$' ), 'name' => array( 'type' => 'string', 'minLength' => 2, 'maxLength' => 32 ),
			'visibility' => array( 'type' => 'array', 'maxItems' => 6, 'uniqueItems' => true, 'items' => array( 'type' => 'string', 'enum' => array( 'show_if_simple', 'show_if_variable', 'show_if_grouped', 'show_if_external', 'hide_if_virtual', 'hide_if_external' ) ) ),
			'show_in_ui' => array( 'type' => 'boolean' ), 'fields' => array( 'type' => 'array', 'minItems' => 1, 'maxItems' => WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS, 'items' => $field ),
		), 'required' => array( 'id', 'expected_absent_hash', 'idempotency_key', 'name', 'visibility', 'show_in_ui', 'fields' ), 'additionalProperties' => false,
	);
}

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

/** @return array<string,mixed> */
function wpagent_ability_update_acpt_woocommerce_product_data(): array {
	return array(
		'label'       => __( 'ACPT-Lite-WooCommerce-Product-Data aktualisieren', 'wp-agent-companion' ),
		'description' => __( 'Aktualisiert nur Standardwerte, Beschreibungen, Pflichtstatus und die UI-Sichtbarkeit einer bestehenden sicheren ACPT-Lite-2.0.11-Product-Data-Gruppe. Inhalt, Gruppen, Optionen und Feldstruktur bleiben unverändert.', 'wp-agent-companion' ),
		'category'    => 'wp-agent',
		'input_schema' => array(
			'type' => 'object',
			'properties' => array(
				'id' => array( 'type' => 'string', 'pattern' => '^[a-f0-9-]{36}$' ),
				'expected_hash' => array( 'type' => 'string', 'pattern' => '^[a-f0-9]{64}$' ),
				'show_in_ui' => array( 'type' => 'boolean' ),
				'fields' => array(
					'type' => 'array', 'maxItems' => WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS,
					'items' => array(
						'type' => 'object',
						'properties' => array( 'id' => array( 'type' => 'string', 'pattern' => '^[a-f0-9-]{36}$' ), 'default_value' => array( 'type' => 'string', 'maxLength' => 200 ), 'description' => array( 'type' => 'string', 'maxLength' => 500 ), 'required' => array( 'type' => 'boolean' ) ),
						'required' => array( 'id' ), 'minProperties' => 2, 'additionalProperties' => false,
					),
				),
			),
			'required' => array( 'id', 'expected_hash', 'fields' ), 'minProperties' => 3, 'additionalProperties' => false,
		),
		'output_schema' => array(
			'type' => 'object', 'properties' => array( 'verified' => array( 'type' => 'boolean' ), 'changed' => array( 'type' => 'boolean' ), 'group' => wpagent_acpt_product_data_group_schema() ),
			'required' => array( 'verified', 'changed', 'group' ), 'additionalProperties' => false,
		),
		'execute_callback' => 'wpagent_exec_update_acpt_woocommerce_product_data',
		'permission_callback' => 'wpagent_perm_acpt_product_data',
		'meta' => array( 'show_in_rest' => true, 'annotations' => array( 'readonly' => false, 'destructive' => false, 'idempotent' => false ) ),
	);
}

/** @return true|WP_Error */
function wpagent_acpt_product_data_keys( array $input, array $allowed, array $required, string $label ) {
	/* translators: %s: input object label. */
	if ( array() !== array_diff( array_keys( $input ), $allowed ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', sprintf( __( 'Unbekannte Eingabefelder in %s.', 'wp-agent-companion' ), $label ), array( 'status' => 400 ) );
	foreach ( $required as $key ) if ( ! array_key_exists( $key, $input ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', sprintf( __( 'Das Feld %s ist erforderlich.', 'wp-agent-companion' ), $key ), array( 'status' => 400 ) );
	return true;
}

/** @return string|WP_Error */
function wpagent_acpt_product_data_create_name( $value, string $label, int $max ): string|WP_Error {
	$value = wpagent_acpt_product_data_safe_text( $value, $label, $max, false );
	if ( is_wp_error( $value ) || ! preg_match( '/^[A-Za-z0-9][A-Za-z0-9 _-]{1,' . ( $max - 1 ) . '}$/D', $value ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Namen dürfen nur sichere ASCII-Buchstaben, Zahlen, Leerzeichen, Unterstriche und Bindestriche enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	return $value;
}

/** @return string|WP_Error */
function wpagent_acpt_product_data_create_option_text( $value, string $label ): string|WP_Error {
	$value = wpagent_acpt_product_data_safe_text( $value, $label, 50, false );
	if ( is_wp_error( $value ) || ! preg_match( '/^[A-Za-z0-9][A-Za-z0-9 ._-]{0,49}$/D', $value ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Optionswerte dürfen nur sichere ASCII-Buchstaben, Zahlen, Leerzeichen, Punkte, Unterstriche und Bindestriche enthalten.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	return $value;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_product_data_validate_create( $input ) {
	$input = is_array( $input ) && ! array_is_list( $input ) ? $input : array();
	$keys = wpagent_acpt_product_data_keys( $input, array( 'id', 'expected_absent_hash', 'idempotency_key', 'name', 'visibility', 'show_in_ui', 'fields' ), array( 'id', 'expected_absent_hash', 'idempotency_key', 'name', 'visibility', 'show_in_ui', 'fields' ), 'Product-Data-Create' );
	if ( is_wp_error( $keys ) || ! wpagent_acpt_product_data_uuid( $input['id'] ?? null ) || ! is_string( $input['expected_absent_hash'] ?? null ) || ! preg_match( '/^[a-f0-9]{64}$/D', $input['expected_absent_hash'] ) || ! is_string( $input['idempotency_key'] ?? null ) || ! preg_match( '/^[A-Za-z0-9._:-]{8,64}$/D', $input['idempotency_key'] ) || ! is_bool( $input['show_in_ui'] ?? null ) || ! is_array( $input['visibility'] ?? null ) || ! array_is_list( $input['visibility'] ) || ! is_array( $input['fields'] ?? null ) || count( $input['fields'] ) < 1 || count( $input['fields'] ) > WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Der ACPT-Lite-Product-Data-Create ist nicht gültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$name = wpagent_acpt_product_data_create_name( $input['name'] ?? null, 'Gruppenname', 32 );
	if ( is_wp_error( $name ) ) return $name;
	$allowed_visibility = array( 'show_if_simple', 'show_if_variable', 'show_if_grouped', 'show_if_external', 'hide_if_virtual', 'hide_if_external' );
	if ( count( $input['visibility'] ) !== count( array_unique( $input['visibility'] ) ) || array_diff( $input['visibility'], $allowed_visibility ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Die WooCommerce-Sichtbarkeit ist nicht Teil des sicheren ACPT-Lite-Vertrags.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$fields = array(); $field_ids = array(); $field_names = array(); $option_ids = array();
	foreach ( $input['fields'] as $index => $field ) {
		$valid_field = is_array( $field )
			&& ! array_is_list( $field )
			&& ! is_wp_error( wpagent_acpt_product_data_keys( $field, array( 'id', 'name', 'type', 'default_value', 'description', 'required', 'options' ), array( 'id', 'name', 'type', 'default_value', 'description', 'required', 'options' ), 'Create-Feld' ) )
			&& wpagent_acpt_product_data_uuid( $field['id'] ?? null )
			&& ! isset( $field_ids[ $field['id'] ] )
			&& in_array( $field['type'] ?? null, array( 'Text', 'Number', 'Select' ), true )
			&& is_bool( $field['required'] ?? null )
			&& is_array( $field['options'] ?? null )
			&& array_is_list( $field['options'] )
			&& count( $field['options'] ) <= WPAGENT_ACPT_PRODUCT_DATA_MAX_OPTIONS;
		if ( ! $valid_field ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Ein ACPT-Lite-Product-Data-Create-Feld ist nicht gültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		$field_name = wpagent_acpt_product_data_create_name( $field['name'] ?? null, 'Feldname', 50 );
		$default = wpagent_acpt_product_data_safe_text( $field['default_value'] ?? null, 'Standardwert', 50 );
		$description = wpagent_acpt_product_data_safe_text( $field['description'] ?? null, 'Beschreibung', 500 );
		if ( is_wp_error( $field_name ) || is_wp_error( $default ) || is_wp_error( $description ) || isset( $field_names[ wpagent_acpt_product_data_name_identity( $field_name ) ] ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Ein ACPT-Lite-Product-Data-Create-Feld ist nicht eindeutig oder nicht sicher.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		$options = array(); $values = array();
		foreach ( $field['options'] as $option_index => $option ) {
			if ( ! is_array( $option ) || array_is_list( $option ) || is_wp_error( wpagent_acpt_product_data_keys( $option, array( 'id', 'label', 'value' ), array( 'id', 'label', 'value' ), 'Create-Option' ) ) || ! wpagent_acpt_product_data_uuid( $option['id'] ?? null ) || isset( $option_ids[ $option['id'] ] ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Eine ACPT-Lite-Product-Data-Create-Option ist nicht gültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			$label = wpagent_acpt_product_data_create_option_text( $option['label'] ?? null, 'Optionsbeschriftung' );
			$value = wpagent_acpt_product_data_create_option_text( $option['value'] ?? null, 'Optionswert' );
			if ( is_wp_error( $label ) || is_wp_error( $value ) || isset( $values[ $value ] ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Eine ACPT-Lite-Product-Data-Create-Option ist nicht eindeutig oder nicht sicher.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			$option_ids[ $option['id'] ] = true; $values[ $value ] = true; $options[] = array( 'id' => $option['id'], 'label' => $label, 'value' => $value, 'sort' => $option_index + 1 );
		}
		if ( ( 'Select' === $field['type'] && count( $options ) < 1 ) || ( 'Select' !== $field['type'] && array() !== $options ) || ( 'Number' === $field['type'] && '' !== $default && ( ! preg_match( '/^-?(?:\d+|\d*\.\d+)$/D', $default ) || ! is_finite( (float) $default ) ) ) || ( 'Select' === $field['type'] && '' !== $default && ! isset( $values[ $default ] ) ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Der Feldtyp, Standardwert oder die Optionen liegen außerhalb des sicheren ACPT-Lite-Vertrags.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		$field_ids[ $field['id'] ] = true; $field_names[ wpagent_acpt_product_data_name_identity( $field_name ) ] = true;
		$fields[] = array( 'id' => $field['id'], 'name' => $field_name, 'type' => $field['type'], 'default_value' => $default, 'description' => $description, 'required' => $field['required'], 'options' => $options, 'sort' => $index + 1 );
	}
	return array( 'id' => $input['id'], 'expected_absent_hash' => $input['expected_absent_hash'], 'idempotency_key' => $input['idempotency_key'], 'name' => $name, 'visibility' => array_values( $input['visibility'] ), 'show_in_ui' => $input['show_in_ui'], 'fields' => $fields );
}

function wpagent_acpt_product_data_create_input_hash( array $definition ): string {
	return hash( 'sha256', (string) wp_json_encode( wpagent_acpt_product_data_canonicalize( $definition ) ) );
}

/** @return array<string,string>|WP_Error */
function wpagent_acpt_product_data_acquire_locks( array $names ) {
	$names = array_values( array_unique( array_filter( $names, 'is_string' ) ) );
	sort( $names, SORT_STRING );
	$tokens = array();
	foreach ( $names as $name ) {
		$token = wpagent_companion_acquire_option_lock( $name, WPAGENT_ACPT_PRODUCT_DATA_LOCK_TTL );
		if ( ! is_string( $token ) ) {
			foreach ( $tokens as $owned_name => $owned_token ) wpagent_companion_release_option_lock( $owned_name, $owned_token );
			return new WP_Error( 'wpagent_acpt_product_data_locked', __( 'Eine andere ACPT-Lite-Product-Data-Änderung läuft bereits.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		}
		$tokens[ $name ] = $token;
	}
	return $tokens;
}

function wpagent_acpt_product_data_release_locks( array $tokens ): void {
	foreach ( $tokens as $name => $token ) if ( is_string( $name ) && is_string( $token ) ) wpagent_companion_release_option_lock( $name, $token );
}

function wpagent_acpt_product_data_refresh_locks( array $tokens ): bool {
	foreach ( $tokens as $name => $token ) if ( ! is_string( $name ) || ! is_string( $token ) || ! wpagent_companion_refresh_option_lock( $name, $token, WPAGENT_ACPT_PRODUCT_DATA_LOCK_TTL ) ) return false;
	return true;
}

/** @return object */
function wpagent_acpt_product_data_create_native_model( array $definition, array $provider ) {
	$model_class = $provider['model']; $field_class = $provider['field_model']; $option_class = $provider['option_model'];
	$model = new $model_class( $definition['id'], $definition['name'], array( 'value' => '\\e001' ), $definition['visibility'], $definition['show_in_ui'] );
	$model->setContent( '' );
	foreach ( $definition['fields'] as $field_definition ) {
		$field = new $field_class( $field_definition['id'], $model, $field_definition['name'], $field_definition['type'], $field_definition['required'], $field_definition['sort'], $field_definition['default_value'], $field_definition['description'] );
		foreach ( $field_definition['options'] as $option_definition ) $field->addOption( new $option_class( $option_definition['id'], $field, $option_definition['label'], $option_definition['value'], $option_definition['sort'] ) );
		$model->addField( $field );
	}
	return $model;
}

/** @return true|WP_Error */
function wpagent_acpt_product_data_create_preflight_locked( array $definition, array $provider ) {
	$namespace = wpagent_acpt_product_data_lock_create_namespace( $definition, $provider );
	if ( is_wp_error( $namespace ) ) return $namespace;
	foreach ( $definition['fields'] as $field ) {
		$available = wpagent_acpt_product_data_lock_child_id( 'field', $field['id'], $provider );
		if ( is_wp_error( $available ) ) return $available;
		foreach ( $field['options'] as $option ) {
			$available = wpagent_acpt_product_data_lock_child_id( 'option', $option['id'], $provider );
			if ( is_wp_error( $available ) ) return $available;
		}
	}
	return true;
}

function wpagent_acpt_product_data_create_matches( array $state, array $definition ): bool {
	$group = $state['public'] ?? null;
	if ( ! is_array( $group ) || $group['id'] !== $definition['id'] || $group['name'] !== $definition['name'] || $group['show_in_ui'] !== $definition['show_in_ui'] || $group['visibility'] !== $definition['visibility'] || count( $group['fields'] ?? array() ) !== count( $definition['fields'] ) ) return false;
	foreach ( $definition['fields'] as $index => $expected ) {
		$actual = $group['fields'][ $index ] ?? null;
		if ( ! is_array( $actual ) || $actual['id'] !== $expected['id'] || $actual['name'] !== $expected['name'] || $actual['type'] !== $expected['type'] || $actual['default_value'] !== $expected['default_value'] || $actual['description'] !== $expected['description'] || $actual['required'] !== $expected['required'] || $actual['sort'] !== $expected['sort'] || wpagent_acpt_product_data_canonicalize( $actual['options'] ) !== wpagent_acpt_product_data_canonicalize( $expected['options'] ) ) return false;
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_acpt_product_data_flush_cache( array $provider ) {
	try {
		$result = $provider['database']::flushCache();
		if ( false === $result ) throw new RuntimeException( 'ACPT cache flush failed.' );
	} catch ( Throwable ) {
		return new WP_Error( 'wpagent_acpt_product_data_cache', __( 'Der ACPT-Lite-Providercache konnte nicht sicher geleert werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return true;
}

/** @return WP_Error */
function wpagent_acpt_product_data_lifecycle_recovery_error( string $message, bool $rollback_verified, ?array $state = null ): WP_Error {
	$data = array( 'status' => 409, 'recovery_required' => true, 'rollback_verified' => $rollback_verified );
	if ( is_array( $state ) ) $data['current'] = array_merge( $state['public'], array( 'state_hash' => $state['state_hash'] ) );
	return new WP_Error( 'wpagent_acpt_product_data_recovery_required', $message, $data );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_product_data_validate_changes( array $input, array $state ) {
	$keys = wpagent_acpt_product_data_keys( $input, array( 'id', 'expected_hash', 'show_in_ui', 'fields' ), array( 'id', 'expected_hash', 'fields' ), 'Product Data' );
	if ( is_wp_error( $keys ) || ! wpagent_acpt_product_data_uuid( $input['id'] ?? null ) || ! is_string( $input['expected_hash'] ?? null ) || ! preg_match( '/^[a-f0-9]{64}$/D', $input['expected_hash'] ) || ! is_array( $input['fields'] ) || count( $input['fields'] ) > WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Die Product-Data-Änderung ist nicht gültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	if ( array_key_exists( 'show_in_ui', $input ) && ! is_bool( $input['show_in_ui'] ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'show_in_ui muss ein boolescher Wert sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$known = array();
	foreach ( $state['public']['fields'] as $field ) $known[ $field['id'] ] = $field;
	$changes = array();
	foreach ( $input['fields'] as $change ) {
		if ( ! is_array( $change ) || is_wp_error( wpagent_acpt_product_data_keys( $change, array( 'id', 'default_value', 'description', 'required' ), array( 'id' ), 'Feldänderung' ) ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Eine Feldänderung ist nicht gültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		$id = $change['id'] ?? null;
		if ( ! wpagent_acpt_product_data_uuid( $id ) || ! isset( $known[ $id ] ) || isset( $changes[ $id ] ) || count( $change ) < 2 ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Eine Feldänderung enthält eine doppelte oder unbekannte ID.', 'wp-agent-companion' ), array( 'status' => 400 ) );
		$current = $known[ $id ];
		$next = array();
		if ( array_key_exists( 'default_value', $change ) ) {
			$value = wpagent_acpt_product_data_safe_text( $change['default_value'], 'Standardwert', 'Number' === $current['type'] ? 30 : 200 );
			if ( is_wp_error( $value ) || ( 'Number' === $current['type'] && ( ! preg_match( '/^-?(?:\d+|\d*\.\d+)$/D', $value ) || ! is_finite( (float) $value ) ) ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Der Standardwert passt nicht zum ACPT-Lite-Feldtyp.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			if ( 'Select' === $current['type'] && '' !== $value && ! in_array( $value, array_column( $current['options'], 'value' ), true ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Der Select-Standardwert ist keine vorhandene Provideroption.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			$next['default_value'] = $value;
		}
		if ( array_key_exists( 'description', $change ) ) {
			$description = wpagent_acpt_product_data_safe_text( $change['description'], 'Beschreibung', 500 );
			if ( is_wp_error( $description ) ) return $description;
			$next['description'] = $description;
		}
		if ( array_key_exists( 'required', $change ) ) {
			if ( ! is_bool( $change['required'] ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'required muss ein boolescher Wert sein.', 'wp-agent-companion' ), array( 'status' => 400 ) );
			$next['required'] = $change['required'];
		}
		$changes[ $id ] = $next;
	}
	if ( array() === $changes && ! array_key_exists( 'show_in_ui', $input ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Die Product-Data-Änderung enthält keine erlaubte Aktualisierung.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	return array( 'show_in_ui' => $input['show_in_ui'] ?? $state['public']['show_in_ui'], 'fields' => $changes );
}

function wpagent_acpt_product_data_changes_state( array $state, array $changes ): bool {
	if ( $state['public']['show_in_ui'] !== $changes['show_in_ui'] ) return true;
	foreach ( $state['public']['fields'] as $field ) {
		foreach ( $changes['fields'][ $field['id'] ] ?? array() as $key => $value ) if ( $field[ $key ] !== $value ) return true;
	}
	return false;
}

/** @return object */
function wpagent_acpt_product_data_native_model( array $state, array $changes, array $provider ) {
	$raw = $state['raw'];
	$group_row = $raw['group'];
	$model_class = $provider['model'];
	$field_class = $provider['field_model'];
	$option_class = $provider['option_model'];
	$model = new $model_class( $group_row['id'], $group_row['product_data_name'], json_decode( $group_row['icon'], true ), json_decode( $group_row['visibility'], true ), $changes['show_in_ui'] );
	$model->setContent( $group_row['content'] );
	$options = array();
	foreach ( $raw['options'] as $option ) $options[ $option['product_data_field_id'] ][] = $option;
	foreach ( $raw['fields'] as $field_row ) {
		$change = $changes['fields'][ $field_row['id'] ] ?? array();
		$field = new $field_class( $field_row['id'], $model, $field_row['field_name'], $field_row['field_type'], array_key_exists( 'required', $change ) ? $change['required'] : ( '1' === (string) $field_row['required'] ), (int) $field_row['sort'], $change['default_value'] ?? $field_row['field_default_value'], $change['description'] ?? $field_row['field_description'] );
		foreach ( $options[ $field_row['id'] ] ?? array() as $option_row ) $field->addOption( new $option_class( $option_row['id'], $field, $option_row['option_label'], $option_row['option_value'], (int) $option_row['sort'] ) );
		$model->addField( $field );
	}
	return $model;
}

function wpagent_acpt_product_data_preserved( array $before, array $after, array $changes ): bool {
	if ( ! is_array( $after['group'] ?? null ) || ! is_array( $after['fields'] ?? null ) || ! is_array( $after['options'] ?? null ) || count( $before['fields'] ) !== count( $after['fields'] ) || count( $before['options'] ) !== count( $after['options'] ) ) return false;
	foreach ( $before['group'] as $key => $value ) if ( 'show_in_ui' !== $key && ( ! array_key_exists( $key, $after['group'] ) || $after['group'][ $key ] !== $value ) ) return false;
	$after_fields = array();
	foreach ( $after['fields'] as $field ) if ( is_array( $field ) && isset( $field['id'] ) ) $after_fields[ $field['id'] ] = $field;
	foreach ( $before['fields'] as $field ) {
		$id = $field['id'] ?? '';
		if ( ! isset( $after_fields[ $id ] ) ) return false;
		foreach ( $field as $key => $value ) {
			$change = $changes['fields'][ $id ] ?? array();
			$allowed = ( 'field_default_value' === $key && array_key_exists( 'default_value', $change ) )
				|| ( 'field_description' === $key && array_key_exists( 'description', $change ) )
				|| ( 'required' === $key && array_key_exists( 'required', $change ) );
			if ( $allowed ) continue;
			if ( ! array_key_exists( $key, $after_fields[ $id ] ) || $after_fields[ $id ][ $key ] !== $value ) return false;
		}
	}
	return wpagent_acpt_product_data_canonicalize( $before['options'] ) === wpagent_acpt_product_data_canonicalize( $after['options'] );
}

/** @return true|WP_Error */
function wpagent_acpt_product_data_verify_tab( object $model, array $provider ) {
	try {
		$generator = new $provider['generator']( array( $model ) );
		$tabs = $generator->generateWooCommerceProductDataTabs( array() );
		$slug = $model->getSluggedName();
		$expected = array( 'label' => __( $model->getName(), 'woocommerce' ), 'target' => $slug . '_options', 'priority' => 45, 'class' => $model->getVisibility() );
		if ( ! is_array( $tabs ) || ! isset( $tabs[ $slug ] ) || $tabs[ $slug ] !== $expected ) throw new UnexpectedValueException( 'ACPT tab mismatch.' );
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_acpt_product_data_tab', __( 'Der native ACPT-Lite-WooCommerce-Tab konnte nicht exakt bestätigt werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return true;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_product_data_create_replay( array $definition, string $input_hash, array $provider ) {
	$record = wpagent_acpt_product_data_idempotency_record( $definition['idempotency_key'], $input_hash );
	if ( null === $record ) return new WP_Error( 'wpagent_acpt_product_data_replay_missing', __( 'Der ACPT-Lite-Create-Idempotenzbeleg fehlt.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	if ( ! empty( $record['invalid'] ) ) return new WP_Error( 'wpagent_acpt_product_data_idempotency_conflict', __( 'Der ACPT-Lite-Create-Idempotenzschlüssel ist bereits mit einem anderen oder beschädigten Vorgang verbunden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$state = wpagent_acpt_product_data_state( $record['id'], $provider );
	if ( is_wp_error( $state ) || ! hash_equals( $record['state_hash'], $state['state_hash'] ) || ! wpagent_acpt_product_data_create_matches( $state, $definition ) ) return new WP_Error( 'wpagent_acpt_product_data_replay_conflict', __( 'Die zuvor angelegte ACPT-Lite-Product-Data-Gruppe wurde verändert oder entfernt und wird nicht erneut angelegt.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	return array( 'verified' => true, 'created' => false, 'idempotent_replay' => true, 'group' => array_merge( $state['public'], array( 'provider_version' => $provider['version'], 'state_hash' => $state['state_hash'] ) ) );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_create_acpt_woocommerce_product_data( $input ) {
	$definition = wpagent_acpt_product_data_validate_create( $input );
	if ( is_wp_error( $definition ) ) return $definition;
	$capability = wpagent_acpt_product_data_require_capability();
	if ( is_wp_error( $capability ) ) return $capability;
	$context = wpagent_acpt_product_data_execution_context();
	$provider = wpagent_acpt_product_data_provider();
	if ( is_wp_error( $provider ) ) return $provider;
	if ( ! hash_equals( wpagent_acpt_product_data_absent_hash( $definition['id'] ), $definition['expected_absent_hash'] ) ) return new WP_Error( 'wpagent_acpt_product_data_conflict', __( 'Die ACPT-Lite-Product-Data-Create-Abwesenheitsvorprüfung gehört nicht zu dieser Website und ID.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$transaction_support = wpagent_acpt_product_data_require_transaction_support( $provider );
	if ( is_wp_error( $transaction_support ) ) return $transaction_support;
	$input_hash = wpagent_acpt_product_data_create_input_hash( $definition );
	$locks = wpagent_acpt_product_data_acquire_locks( array(
		wpagent_acpt_product_data_lock_name( $definition['id'] ),
		wpagent_acpt_product_data_lifecycle_lock_name( 'name', wpagent_acpt_product_data_name_identity( $definition['name'] ) ),
		wpagent_acpt_product_data_lifecycle_lock_name( 'create', $definition['idempotency_key'] ),
	) );
	if ( is_wp_error( $locks ) ) return $locks;
	$transaction_started = false;
	try {
		if ( ! wpagent_acpt_product_data_context_current( $context ) ) return new WP_Error( 'wpagent_acpt_product_data_forbidden', __( 'Der ACPT-Lite-Product-Data-Create-Ausführungskontext hat sich geändert.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		$provider = wpagent_acpt_product_data_provider();
		if ( is_wp_error( $provider ) ) return $provider;
		if ( ! wpagent_acpt_product_data_refresh_locks( $locks ) ) return new WP_Error( 'wpagent_acpt_product_data_lock', __( 'Die ACPT-Lite-Product-Data-Sperre ging verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$record = wpagent_acpt_product_data_idempotency_record( $definition['idempotency_key'], $input_hash );
		if ( is_array( $record ) ) return wpagent_acpt_product_data_create_replay( $definition, $input_hash, $provider );
		$started = wpagent_acpt_product_data_start_transaction( $provider );
		if ( is_wp_error( $started ) ) return $started;
		$transaction_started = true;
		$preflight = wpagent_acpt_product_data_create_preflight_locked( $definition, $provider );
		if ( is_wp_error( $preflight ) ) { $provider['database']::rollbackTransaction(); $transaction_started = false; return $preflight; }
		if ( ! wpagent_acpt_product_data_refresh_locks( $locks ) || ! wpagent_acpt_product_data_context_current( $context ) ) throw new RuntimeException( 'ACPT Product Data lock, actor, blog, or capability lost before create.' );
		$model = wpagent_acpt_product_data_create_native_model( $definition, $provider );
		$tab = wpagent_acpt_product_data_verify_tab( $model, $provider );
		if ( is_wp_error( $tab ) ) { $provider['database']::rollbackTransaction(); $transaction_started = false; return $tab; }
		$provider['repository']::save( $model );
		do_action( 'wpagent_acpt_product_data_after_provider_create', $definition['id'], wpagent_acpt_product_data_lock_name( $definition['id'] ), $locks[ wpagent_acpt_product_data_lock_name( $definition['id'] ) ] );
		if ( ! wpagent_acpt_product_data_context_current( $context ) || ! wpagent_acpt_product_data_refresh_locks( $locks ) ) throw new RuntimeException( 'ACPT Product Data lock, actor, blog, or capability lost after create.' );
		$post_provider = wpagent_acpt_product_data_provider();
		if ( is_wp_error( $post_provider ) ) throw new RuntimeException( 'ACPT Product Data runtime changed after create.' );
		$provider = $post_provider;
		$cache = wpagent_acpt_product_data_flush_cache( $provider );
		if ( is_wp_error( $cache ) ) throw new RuntimeException( 'ACPT Product Data cache flush failed after create.' );
		$state = wpagent_acpt_product_data_state( $definition['id'], $provider );
		if ( is_wp_error( $state ) || ! wpagent_acpt_product_data_create_matches( $state, $definition ) || ! wpagent_acpt_product_data_context_current( $context ) || ! wpagent_acpt_product_data_refresh_locks( $locks ) || is_wp_error( wpagent_acpt_product_data_verify_tab( is_array( $state ) ? $state['model'] : $model, $provider ) ) ) throw new RuntimeException( 'ACPT Product Data create readback mismatch.' );
		$record = array( 'version' => WPAGENT_ACPT_PRODUCT_DATA_LIFECYCLE_RECORD_VERSION, 'blog_id' => $context['blog_id'], 'user_id' => $context['user_id'], 'input_hash' => $input_hash, 'id' => $definition['id'], 'state_hash' => $state['state_hash'] );
		if ( ! wpagent_companion_add_option_create_only( wpagent_acpt_product_data_idempotency_record_name( $definition['idempotency_key'] ), $record, '', false ) ) throw new RuntimeException( 'ACPT Product Data idempotency record failed.' );
		$stored = wpagent_acpt_product_data_idempotency_record( $definition['idempotency_key'], $input_hash );
		if ( ! is_array( $stored ) || ! empty( $stored['invalid'] ) || wpagent_acpt_product_data_canonicalize( $stored ) !== wpagent_acpt_product_data_canonicalize( $record ) || ! wpagent_acpt_product_data_refresh_locks( $locks ) || ! wpagent_acpt_product_data_context_current( $context ) ) throw new RuntimeException( 'ACPT Product Data idempotency record mismatch.' );
		$provider['database']::commitTransaction();
		$transaction_started = false;
		$cache = wpagent_acpt_product_data_flush_cache( $provider );
		$after = wpagent_acpt_product_data_state( $definition['id'], $provider );
		$stored = wpagent_acpt_product_data_idempotency_record( $definition['idempotency_key'], $input_hash );
		if ( is_wp_error( $cache ) || is_wp_error( $after ) || ! wpagent_acpt_product_data_create_matches( $after, $definition ) || ! hash_equals( $state['state_hash'], $after['state_hash'] ) || ! is_array( $stored ) || ! empty( $stored['invalid'] ) || wpagent_acpt_product_data_canonicalize( $stored ) !== wpagent_acpt_product_data_canonicalize( $record ) || ! wpagent_acpt_product_data_context_current( $context ) || ! wpagent_acpt_product_data_refresh_locks( $locks ) ) return wpagent_acpt_product_data_lifecycle_recovery_error( __( 'Die neue ACPT-Lite-Product-Data-Gruppe wurde gespeichert, konnte nach dem Commit jedoch nicht vollständig bestätigt werden.', 'wp-agent-companion' ), false, is_array( $after ) ? $after : null );
		return array( 'verified' => true, 'created' => true, 'idempotent_replay' => false, 'group' => array_merge( $after['public'], array( 'provider_version' => $provider['version'], 'state_hash' => $after['state_hash'] ) ) );
	} catch ( Throwable $error ) {
		if ( $transaction_started ) {
			try { $provider['database']::rollbackTransaction(); } catch ( Throwable ) {}
			$transaction_started = false;
		}
		$absent = wpagent_acpt_product_data_absent( $definition['id'], is_array( $provider ) ? $provider : array() );
		return wpagent_acpt_product_data_lifecycle_recovery_error( __( 'Der ACPT-Lite-Product-Data-Create wurde nicht vollständig bestätigt. Die Transaktion wurde zurückgenommen, soweit ihre Abwesenheit nachweisbar ist.', 'wp-agent-companion' ), true === $absent );
	} finally {
		if ( $transaction_started ) try { $provider['database']::rollbackTransaction(); } catch ( Throwable ) {}
		wpagent_acpt_product_data_release_locks( $locks );
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_product_data_validate_delete( $input ) {
	$input = is_array( $input ) && ! array_is_list( $input ) ? $input : array();
	$keys = wpagent_acpt_product_data_keys( $input, array( 'id', 'expected_hash', 'confirm' ), array( 'id', 'expected_hash', 'confirm' ), 'Product-Data-Delete' );
	if ( is_wp_error( $keys ) || ! wpagent_acpt_product_data_uuid( $input['id'] ?? null ) || ! is_string( $input['expected_hash'] ?? null ) || ! preg_match( '/^[a-f0-9]{64}$/D', $input['expected_hash'] ) || true !== ( $input['confirm'] ?? false ) ) return new WP_Error( 'wpagent_acpt_product_data_confirm', __( 'Das Löschen der ACPT-Lite-Product-Data-Gruppe erfordert eine gültige ID, ihren vollständigen Zustands-Hash und confirm=true.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	return array( 'id' => $input['id'], 'expected_hash' => $input['expected_hash'], 'confirm' => true );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_delete_acpt_woocommerce_product_data( $input ) {
	$input = wpagent_acpt_product_data_validate_delete( $input );
	if ( is_wp_error( $input ) ) return $input;
	$capability = wpagent_acpt_product_data_require_capability();
	if ( is_wp_error( $capability ) ) return $capability;
	$context = wpagent_acpt_product_data_execution_context();
	$provider = wpagent_acpt_product_data_provider();
	if ( is_wp_error( $provider ) ) return $provider;
	$transaction_support = wpagent_acpt_product_data_require_transaction_support( $provider );
	if ( is_wp_error( $transaction_support ) ) return $transaction_support;
	$before = wpagent_acpt_product_data_state( $input['id'], $provider );
	if ( is_wp_error( $before ) ) return $before;
	if ( ! hash_equals( $before['state_hash'], $input['expected_hash'] ) ) return new WP_Error( 'wpagent_acpt_product_data_conflict', __( 'Die ACPT-Lite-Product-Data-Gruppe wurde seit dem Lesen geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$lock_name = wpagent_acpt_product_data_lock_name( $input['id'] );
	$locks = wpagent_acpt_product_data_acquire_locks( array( $lock_name ) );
	if ( is_wp_error( $locks ) ) return $locks;
	$transaction_started = false;
	try {
		if ( ! wpagent_acpt_product_data_context_current( $context ) ) return new WP_Error( 'wpagent_acpt_product_data_forbidden', __( 'Der ACPT-Lite-Product-Data-Delete-Ausführungskontext hat sich geändert.', 'wp-agent-companion' ), array( 'status' => 403 ) );
		$provider = wpagent_acpt_product_data_provider();
		if ( is_wp_error( $provider ) ) return $provider;
		if ( ! wpagent_acpt_product_data_refresh_locks( $locks ) ) return new WP_Error( 'wpagent_acpt_product_data_lock', __( 'Die ACPT-Lite-Product-Data-Sperre ging verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$started = wpagent_acpt_product_data_start_transaction( $provider );
		if ( is_wp_error( $started ) ) return $started;
		$transaction_started = true;
		$locked = wpagent_acpt_product_data_lock_rows( $input['id'], $provider );
		if ( is_wp_error( $locked ) || 1 !== count( $locked['group'] ) ) { $provider['database']::rollbackTransaction(); $transaction_started = false; return is_wp_error( $locked ) ? $locked : new WP_Error( 'wpagent_acpt_product_data_conflict', __( 'Die ACPT-Lite-Product-Data-Gruppe wurde vor dem Delete verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) ); }
		$cache = wpagent_acpt_product_data_flush_cache( $provider );
		if ( is_wp_error( $cache ) ) throw new RuntimeException( 'ACPT Product Data cache flush failed before delete.' );
		$current = wpagent_acpt_product_data_state( $input['id'], $provider );
		if ( is_wp_error( $current ) || ! hash_equals( $current['state_hash'], $input['expected_hash'] ) ) { $provider['database']::rollbackTransaction(); $transaction_started = false; return new WP_Error( 'wpagent_acpt_product_data_conflict', __( 'Die ACPT-Lite-Product-Data-Gruppe wurde vor dem Delete verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) ); }
		if ( ! wpagent_acpt_product_data_context_current( $context ) || ! wpagent_acpt_product_data_refresh_locks( $locks ) ) throw new RuntimeException( 'ACPT Product Data lock, actor, blog, or capability lost before delete.' );
		$deleted = $provider['repository']::delete( $input['id'] );
		if ( true !== $deleted ) throw new RuntimeException( 'ACPT Product Data repository delete failed.' );
		do_action( 'wpagent_acpt_product_data_after_provider_delete', $input['id'], $lock_name, $locks[ $lock_name ] );
		if ( ! wpagent_acpt_product_data_context_current( $context ) || ! wpagent_acpt_product_data_refresh_locks( $locks ) ) throw new RuntimeException( 'ACPT Product Data lock, actor, blog, or capability lost after delete.' );
		$post_provider = wpagent_acpt_product_data_provider();
		if ( is_wp_error( $post_provider ) ) throw new RuntimeException( 'ACPT Product Data runtime changed after delete.' );
		$provider = $post_provider;
		$cache = wpagent_acpt_product_data_flush_cache( $provider );
		if ( is_wp_error( $cache ) ) throw new RuntimeException( 'ACPT Product Data cache flush failed after delete.' );
		$absent = wpagent_acpt_product_data_absent( $input['id'], $provider );
		if ( true !== $absent || $provider['repository']::exists( $input['id'] ) || ! wpagent_acpt_product_data_context_current( $context ) || ! wpagent_acpt_product_data_refresh_locks( $locks ) ) throw new RuntimeException( 'ACPT Product Data delete readback mismatch.' );
		$provider['database']::commitTransaction();
		$transaction_started = false;
		$cache = wpagent_acpt_product_data_flush_cache( $provider );
		$absent = wpagent_acpt_product_data_absent( $input['id'], $provider );
		if ( is_wp_error( $cache ) || true !== $absent || $provider['repository']::exists( $input['id'] ) || ! wpagent_acpt_product_data_context_current( $context ) || ! wpagent_acpt_product_data_refresh_locks( $locks ) ) return wpagent_acpt_product_data_lifecycle_recovery_error( __( 'Die ACPT-Lite-Product-Data-Gruppe wurde gelöscht, konnte nach dem Commit jedoch nicht vollständig als abwesend bestätigt werden.', 'wp-agent-companion' ), false );
		return array( 'verified' => true, 'deleted' => true );
	} catch ( Throwable $error ) {
		if ( $transaction_started ) {
			try { $provider['database']::rollbackTransaction(); } catch ( Throwable ) {}
			$transaction_started = false;
		}
		$restored = wpagent_acpt_product_data_state( $input['id'], is_array( $provider ) ? $provider : array() );
		$rollback_verified = is_array( $restored ) && hash_equals( $input['expected_hash'], $restored['state_hash'] );
		return wpagent_acpt_product_data_lifecycle_recovery_error( __( 'Der ACPT-Lite-Product-Data-Delete wurde nicht vollständig bestätigt. Die Transaktion wurde zurückgenommen, soweit der Ausgangszustand nachweisbar ist.', 'wp-agent-companion' ), $rollback_verified, is_array( $restored ) ? $restored : null );
	} finally {
		if ( $transaction_started ) try { $provider['database']::rollbackTransaction(); } catch ( Throwable ) {}
		wpagent_acpt_product_data_release_locks( $locks );
	}
}

/** @return array<string,mixed>|WP_Error */
function wpagent_exec_update_acpt_woocommerce_product_data( $input ) {
	$input = is_array( $input ) ? $input : array();
	$id = $input['id'] ?? '';
	if ( ! wpagent_acpt_product_data_uuid( $id ) ) return new WP_Error( 'wpagent_acpt_product_data_invalid', __( 'Die Product-Data-ID ist nicht gültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	$capability = wpagent_acpt_product_data_require_capability();
	if ( is_wp_error( $capability ) ) return $capability;
	$provider = wpagent_acpt_product_data_provider();
	if ( is_wp_error( $provider ) ) return $provider;
	$before = wpagent_acpt_product_data_state( $id, $provider );
	if ( is_wp_error( $before ) ) return $before;
	if ( ! hash_equals( $before['state_hash'], (string) ( $input['expected_hash'] ?? '' ) ) ) return new WP_Error( 'wpagent_acpt_product_data_conflict', __( 'Die ACPT-Lite-Product-Data-Gruppe wurde seit dem Lesen geändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$changes = wpagent_acpt_product_data_validate_changes( $input, $before );
	if ( is_wp_error( $changes ) ) return $changes;
	if ( ! wpagent_acpt_product_data_changes_state( $before, $changes ) ) return array( 'verified' => true, 'changed' => false, 'group' => array_merge( $before['public'], array( 'provider_version' => $provider['version'], 'state_hash' => $before['state_hash'] ) ) );
	$lock = wpagent_acpt_product_data_lock_name( $id );
	$token = wpagent_companion_acquire_option_lock( $lock, WPAGENT_ACPT_PRODUCT_DATA_LOCK_TTL );
	if ( ! is_string( $token ) ) return new WP_Error( 'wpagent_acpt_product_data_locked', __( 'Eine andere ACPT-Lite-Product-Data-Aktualisierung läuft bereits.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$started = false;
	try {
		$capability = wpagent_acpt_product_data_require_capability();
		if ( is_wp_error( $capability ) ) return $capability;
		$provider = wpagent_acpt_product_data_provider();
		if ( is_wp_error( $provider ) ) return $provider;
		$current = wpagent_acpt_product_data_state( $id, $provider );
		if ( is_wp_error( $current ) ) return $current;
		if ( ! hash_equals( $current['state_hash'], (string) $input['expected_hash'] ) ) return new WP_Error( 'wpagent_acpt_product_data_conflict', __( 'Die ACPT-Lite-Product-Data-Gruppe wurde vor dem Write verändert.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		if ( ! wpagent_companion_refresh_option_lock( $lock, $token, WPAGENT_ACPT_PRODUCT_DATA_LOCK_TTL ) ) return new WP_Error( 'wpagent_acpt_product_data_lock', __( 'Die ACPT-Lite-Product-Data-Sperre wurde vor dem Write verloren.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$model = wpagent_acpt_product_data_native_model( $current, $changes, $provider );
		$tab = wpagent_acpt_product_data_verify_tab( $model, $provider );
		if ( is_wp_error( $tab ) ) return $tab;
		$started = true;
		$provider['repository']::save( $model );
		do_action( 'wpagent_acpt_product_data_after_provider_write', $id, $lock, $token );
		if ( ! wpagent_acpt_product_data_can_manage() ) throw new RuntimeException( 'Capability lost after ACPT Product Data write.' );
		$post_provider = wpagent_acpt_product_data_provider();
		if ( is_wp_error( $post_provider ) ) throw new RuntimeException( 'ACPT Product Data runtime changed after write.' );
		$provider = $post_provider;
		if ( ! wpagent_companion_refresh_option_lock( $lock, $token, WPAGENT_ACPT_PRODUCT_DATA_LOCK_TTL ) ) throw new RuntimeException( 'ACPT Product Data lock lost after write.' );
		$cache = $provider['database']::flushCache();
		if ( false === $cache ) throw new RuntimeException( 'ACPT Product Data cache flush failed.' );
		$after = wpagent_acpt_product_data_state( $id, $provider );
		if ( is_wp_error( $after ) || ! wpagent_acpt_product_data_preserved( $current['raw'], $after['raw'], $changes ) || $after['public']['show_in_ui'] !== $changes['show_in_ui'] || ! wpagent_acpt_product_data_can_manage() || ! wpagent_companion_refresh_option_lock( $lock, $token, WPAGENT_ACPT_PRODUCT_DATA_LOCK_TTL ) ) throw new RuntimeException( 'ACPT Product Data readback mismatch.' );
		foreach ( $changes['fields'] as $field_id => $change ) {
			$after_fields = array_column( $after['public']['fields'], null, 'id' );
			if ( ! isset( $after_fields[ $field_id ] ) ) throw new RuntimeException( 'ACPT Product Data field drift.' );
			foreach ( $change as $key => $value ) if ( $after_fields[ $field_id ][ $key ] !== $value ) throw new RuntimeException( 'ACPT Product Data field readback mismatch.' );
		}
		$tab = wpagent_acpt_product_data_verify_tab( $after['model'], $provider );
		if ( is_wp_error( $tab ) ) throw new RuntimeException( 'ACPT Product Data tab drift.' );
		return array( 'verified' => true, 'changed' => true, 'group' => array_merge( $after['public'], array( 'provider_version' => $provider['version'], 'state_hash' => $after['state_hash'] ) ) );
	} catch ( Throwable $error ) {
		$after = wpagent_acpt_product_data_state( $id, is_array( $provider ) ? $provider : array() );
		return $started ? wpagent_acpt_product_data_recovery_error( __( 'Der ACPT-Lite-Product-Data-Write wurde begonnen, aber nicht vollständig bestätigt. Es erfolgt keine automatische Folgemutation oder Rücknahme.', 'wp-agent-companion' ), is_array( $after ) ? $after : null ) : new WP_Error( 'wpagent_acpt_product_data_write', __( 'Die ACPT-Lite-Product-Data-Gruppe konnte nicht sicher aktualisiert werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	} finally {
		wpagent_companion_release_option_lock( $lock, $token );
	}
}
