<?php
/** Bounded ACPT Lite 2.0.11 WooCommerce product-data configuration support. */

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

define( 'WPAGENT_ACPT_PRODUCT_DATA_MAX_GROUPS', 50 );
define( 'WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS', 20 );
define( 'WPAGENT_ACPT_PRODUCT_DATA_MAX_OPTIONS', 20 );
define( 'WPAGENT_ACPT_PRODUCT_DATA_MAX_SNAPSHOT_BYTES', 131072 );
define( 'WPAGENT_ACPT_PRODUCT_DATA_MAX_CONTENT_BYTES', 65536 );
define( 'WPAGENT_ACPT_PRODUCT_DATA_LOCK_TTL', 300 );
define( 'WPAGENT_ACPT_PRODUCT_DATA_LIFECYCLE_RECORD_VERSION', 1 );

/** @return array<string,string>|WP_Error */
function wpagent_acpt_product_data_provider() {
	global $wpdb;
	$engine = defined( 'DB_ENGINE' ) ? strtolower( (string) DB_ENGINE ) : ( false !== stripos( get_class( $wpdb ), 'sqlite' ) ? 'sqlite' : 'mysql' );
	if ( 'sqlite' === $engine || ! isset( $wpdb->dbh ) || ! $wpdb->dbh instanceof mysqli ) {
		return new WP_Error( 'wpagent_acpt_product_data_database', __( 'ACPT Lite Product Data wird nur auf MySQL oder MariaDB unterstützt.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$provider = array(
		'version'       => defined( 'ACPT_LITE_PLUGIN_VERSION' ) ? (string) ACPT_LITE_PLUGIN_VERSION : '',
		'woo_version'   => defined( 'WC_VERSION' ) ? (string) WC_VERSION : '',
		'repository'    => 'ACPT_Lite\\Core\\Repository\\WooCommerceProductDataRepository',
		'model'         => 'ACPT_Lite\\Core\\Models\\WooCommerce\\WooCommerceProductDataModel',
		'field_model'   => 'ACPT_Lite\\Core\\Models\\WooCommerce\\WooCommerceProductDataFieldModel',
		'option_model'  => 'ACPT_Lite\\Core\\Models\\WooCommerce\\WooCommerceProductDataFieldOptionModel',
		'generator'     => 'ACPT_Lite\\Core\\Generators\\Meta\\WooCommerceProductDataGenerator',
		'database'      => 'ACPT_Lite\\Includes\\ACPT_Lite_DB',
		'woo_runtime'   => 'ACPT_Lite\\Integrations\\WooCommerce\\ACPT_Lite_WooCommerce',
	);
	if ( '2.0.11' !== $provider['version'] || '10.9.4' !== $provider['woo_version'] || ! function_exists( 'is_plugin_active' ) || ! is_plugin_active( 'acpt-lite/acpt-lite.php' ) || ! is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
		return new WP_Error( 'wpagent_acpt_product_data_missing', __( 'Dieser sichere Product-Data-Pfad erfordert genau ACPT Lite 2.0.11 und WooCommerce 10.9.4.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	foreach ( array( 'repository', 'model', 'field_model', 'option_model', 'generator', 'database', 'woo_runtime' ) as $class ) {
		if ( ! class_exists( $provider[ $class ] ) ) return new WP_Error( 'wpagent_acpt_product_data_api', __( 'Die erforderliche native ACPT-Lite-Product-Data-API ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	foreach ( array( 'get', 'exists', 'save', 'delete' ) as $method ) {
		if ( ! is_callable( array( $provider['repository'], $method ) ) ) return new WP_Error( 'wpagent_acpt_product_data_api', __( 'Die erforderliche native ACPT-Lite-Product-Data-API ist nicht verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	if ( ! is_callable( array( $provider['database'], 'flushCache' ) ) || ! is_callable( array( $provider['woo_runtime'], 'active' ) ) || ! $provider['woo_runtime']::active() ) {
		return new WP_Error( 'wpagent_acpt_product_data_api', __( 'Die native ACPT-Lite-WooCommerce-Product-Data-Integration ist nicht aktiv.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return $provider;
}

/** @return true|WP_Error */
function wpagent_acpt_product_data_require_transaction_support( array $provider ) {
	global $wpdb;
	$tables = wpagent_acpt_product_data_tables( $provider );
	if ( is_wp_error( $tables ) || ! is_callable( array( $provider['database'], 'startTransaction' ) ) || ! is_callable( array( $provider['database'], 'commitTransaction' ) ) || ! is_callable( array( $provider['database'], 'rollbackTransaction' ) ) ) {
		return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Die native ACPT-Lite-Transaktionsschnittstelle ist nicht vollständig verfügbar.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$options_table = isset( $wpdb->options ) && is_string( $wpdb->options ) ? $wpdb->options : '';
	if ( ! preg_match( '/^[A-Za-z0-9_]+$/D', $options_table ) ) return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Die WordPress-Options-Tabelle ist nicht kanonisch.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$all_tables = array_merge( array_values( $tables ), array( $options_table ) );
	foreach ( $all_tables as $table ) {
		$wpdb->last_error = '';
		$engine = $wpdb->get_var( $wpdb->prepare( 'SELECT ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s LIMIT 1', $table ) );
		if ( '' !== $wpdb->last_error || ! is_string( $engine ) || 'innodb' !== strtolower( $engine ) ) return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Der sichere ACPT-Lite-Create/Delete-Pfad erfordert InnoDB für alle beteiligten Tabellen.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$wpdb->last_error = '';
	$in_transaction = $wpdb->get_var( 'SELECT @@session.in_transaction' );
	if ( '' !== $wpdb->last_error || ! in_array( (string) $in_transaction, array( '0', 'OFF' ), true ) ) return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Der sichere ACPT-Lite-Create/Delete-Pfad kann nicht innerhalb einer fremden Datenbanktransaktion starten.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	return true;
}

/** @return true|WP_Error */
function wpagent_acpt_product_data_start_transaction( array $provider ) {
	global $wpdb;
	$support = wpagent_acpt_product_data_require_transaction_support( $provider );
	if ( is_wp_error( $support ) ) return $support;
	$wpdb->last_error = '';
	if ( false === $wpdb->query( 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE' ) || '' !== $wpdb->last_error ) return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Die ACPT-Lite-Transaktion kann nicht seriell gestartet werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	try {
		$provider['database']::startTransaction();
		$wpdb->last_error = '';
		$in_transaction = $wpdb->get_var( 'SELECT @@session.in_transaction' );
		if ( '' !== $wpdb->last_error || ! in_array( (string) $in_transaction, array( '1', 'ON' ), true ) ) throw new UnexpectedValueException( 'ACPT transaction did not start.' );
	} catch ( Throwable $error ) {
		try { $provider['database']::rollbackTransaction(); } catch ( Throwable ) {}
		return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Die ACPT-Lite-Transaktion konnte nicht sicher gestartet werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return true;
}

function wpagent_acpt_product_data_name_identity( string $name ): string {
	return strtolower( str_replace( ' ', '_', $name ) );
}

function wpagent_acpt_product_data_absent_hash( string $id ): string {
	return hash( 'sha256', (string) wp_json_encode( array( 'blog_id' => get_current_blog_id(), 'id' => $id, 'state' => 'absent-v1' ) ) );
}

function wpagent_acpt_product_data_lifecycle_lock_name( string $scope, string $value ): string {
	return 'wp_agent_acpt_product_data_' . $scope . '_' . substr( hash( 'sha256', get_current_blog_id() . "\0" . $value ), 0, 32 );
}

function wpagent_acpt_product_data_idempotency_record_name( string $key ): string {
	return 'wp_agent_acpt_product_data_receipt_' . substr( hash( 'sha256', get_current_blog_id() . "\0" . $key ), 0, 32 );
}

/** @return array<string,mixed>|null */
function wpagent_acpt_product_data_idempotency_record( string $key, string $input_hash ): ?array {
	$record = wpagent_companion_read_option_record_direct( wpagent_acpt_product_data_idempotency_record_name( $key ) );
	if ( ! is_array( $record ) ) return null;
	if ( WPAGENT_ACPT_PRODUCT_DATA_LIFECYCLE_RECORD_VERSION !== (int) ( $record['version'] ?? 0 ) || get_current_blog_id() !== (int) ( $record['blog_id'] ?? 0 ) || get_current_user_id() !== (int) ( $record['user_id'] ?? 0 ) || ! is_string( $record['input_hash'] ?? null ) || ! hash_equals( $record['input_hash'], $input_hash ) || ! wpagent_acpt_product_data_uuid( $record['id'] ?? null ) || ! is_string( $record['state_hash'] ?? null ) || ! preg_match( '/^[a-f0-9]{64}$/D', $record['state_hash'] ) ) return array( 'invalid' => true );
	return $record;
}

function wpagent_acpt_product_data_can_manage(): bool {
	return current_user_can( 'manage_options' ) && current_user_can( 'manage_woocommerce' );
}

/** @return array{blog_id:int,user_id:int} */
function wpagent_acpt_product_data_execution_context(): array {
	return array( 'blog_id' => (int) get_current_blog_id(), 'user_id' => (int) get_current_user_id() );
}

function wpagent_acpt_product_data_context_current( array $context ): bool {
	return isset( $context['blog_id'], $context['user_id'] )
		&& (int) $context['blog_id'] === (int) get_current_blog_id()
		&& (int) $context['user_id'] === (int) get_current_user_id()
		&& wpagent_acpt_product_data_can_manage();
}

function wpagent_perm_acpt_product_data( $input = null ): bool {
	unset( $input );
	return wpagent_acpt_product_data_can_manage();
}

/** @return true|WP_Error */
function wpagent_acpt_product_data_require_capability() {
	if ( ! wpagent_acpt_product_data_can_manage() ) return new WP_Error( 'wpagent_acpt_product_data_forbidden', __( 'Du darfst ACPT-Lite-WooCommerce-Product-Data nicht verwalten.', 'wp-agent-companion' ), array( 'status' => 403 ) );
	return true;
}

function wpagent_acpt_product_data_uuid( $value ): bool {
	return is_string( $value ) && 1 === preg_match( '/^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/Di', $value );
}

function wpagent_acpt_product_data_bytes( string $value ): int {
	return strlen( $value );
}

function wpagent_acpt_product_data_canonicalize( $value ) {
	if ( ! is_array( $value ) ) return $value;
	foreach ( $value as $key => $item ) $value[ $key ] = wpagent_acpt_product_data_canonicalize( $item );
	ksort( $value, SORT_STRING );
	return $value;
}

function wpagent_acpt_product_data_hash( array $raw ): string {
	$payload = array( 'blog_id' => get_current_blog_id(), 'state' => wpagent_acpt_product_data_canonicalize( $raw ) );
	return hash( 'sha256', (string) wp_json_encode( $payload ) );
}

/** @return array<string,string>|WP_Error */
function wpagent_acpt_product_data_tables( array $provider ) {
	try {
		$database = $provider['database'];
		$tables = array(
			'group'   => $database::prefixedTableName( $database::TABLE_WOOCOMMERCE_PRODUCT_DATA ),
			'field'   => $database::prefixedTableName( $database::TABLE_WOOCOMMERCE_PRODUCT_DATA_FIELD ),
			'option'  => $database::prefixedTableName( $database::TABLE_WOOCOMMERCE_PRODUCT_DATA_OPTION ),
		);
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_acpt_product_data_tables', __( 'Die nativen ACPT-Lite-Product-Data-Tabellen konnten nicht sicher aufgelöst werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	}
	foreach ( $tables as $table ) if ( ! is_string( $table ) || ! preg_match( '/^[A-Za-z0-9_]+$/D', $table ) ) return new WP_Error( 'wpagent_acpt_product_data_tables', __( 'Die nativen ACPT-Lite-Product-Data-Tabellen sind nicht kanonisch.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	return $tables;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_product_data_raw( string $id, array $provider ) {
	global $wpdb;
	$tables = wpagent_acpt_product_data_tables( $provider );
	if ( is_wp_error( $tables ) ) return $tables;
	$wpdb->last_error = '';
	$group = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM `{$tables['group']}` WHERE id = %s LIMIT 1", $id ), ARRAY_A );
	if ( '' !== $wpdb->last_error ) return new WP_Error( 'wpagent_acpt_product_data_tables', __( 'Die ACPT-Lite-Product-Data-Gruppe konnte nicht sicher gelesen werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
	if ( ! is_array( $group ) ) return new WP_Error( 'wpagent_acpt_product_data_not_found', __( 'Die ACPT-Lite-Product-Data-Gruppe wurde nicht gefunden.', 'wp-agent-companion' ), array( 'status' => 404 ) );
	$wpdb->last_error = '';
	$fields = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$tables['field']}` WHERE product_data_id = %s ORDER BY sort ASC, id ASC LIMIT 21", $id ), ARRAY_A );
	$field_error = $wpdb->last_error;
	$wpdb->last_error = '';
	$options = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$tables['option']}` WHERE product_data_id = %s ORDER BY sort ASC, id ASC LIMIT 401", $id ), ARRAY_A );
	$option_error = $wpdb->last_error;
	if ( '' !== $field_error || '' !== $option_error || ! is_array( $fields ) || ! is_array( $options ) || count( $fields ) > WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS || count( $options ) > WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS * WPAGENT_ACPT_PRODUCT_DATA_MAX_OPTIONS ) {
		return new WP_Error( 'wpagent_acpt_product_data_limit', __( 'Der vollständige ACPT-Lite-Product-Data-Zustand überschreitet die sichere Grenze.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	$raw = array( 'group' => $group, 'fields' => $fields, 'options' => $options );
	$encoded = wp_json_encode( $raw );
	if ( ! is_string( $encoded ) || wpagent_acpt_product_data_bytes( $encoded ) > WPAGENT_ACPT_PRODUCT_DATA_MAX_SNAPSHOT_BYTES ) return new WP_Error( 'wpagent_acpt_product_data_limit', __( 'Der vollständige ACPT-Lite-Product-Data-Zustand ist zu groß.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	return $raw;
}

/** @return true|WP_Error */
function wpagent_acpt_product_data_absent( string $id, array $provider ) {
	global $wpdb;
	$tables = wpagent_acpt_product_data_tables( $provider );
	if ( is_wp_error( $tables ) ) return $tables;
	foreach ( array( array( $tables['group'], 'id' ), array( $tables['field'], 'product_data_id' ), array( $tables['option'], 'product_data_id' ) ) as $query ) {
		$wpdb->last_error = '';
		$count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM `{$query[0]}` WHERE `{$query[1]}` = %s", $id ) );
		if ( '' !== $wpdb->last_error || ! is_numeric( $count ) ) return new WP_Error( 'wpagent_acpt_product_data_tables', __( 'Der ACPT-Lite-Product-Data-Zustand konnte nicht sicher auf Abwesenheit geprüft werden.', 'wp-agent-companion' ), array( 'status' => 500 ) );
		if ( 0 !== (int) $count ) return false;
	}
	return true;
}

/**
 * Locks exactly the target group and its direct children inside the caller's
 * SERIALIZABLE transaction. This path is only used for an already-known ID.
 *
 * @return array{group:array<int,array<string,mixed>>,fields:array<int,array<string,mixed>>,options:array<int,array<string,mixed>>}|WP_Error
 */
function wpagent_acpt_product_data_lock_rows( string $id, array $provider ) {
	global $wpdb;
	$tables = wpagent_acpt_product_data_tables( $provider );
	if ( is_wp_error( $tables ) ) return $tables;
	$wpdb->last_error = '';
	$groups = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$tables['group']}` WHERE id = %s FOR UPDATE", $id ), ARRAY_A );
	$group_error = $wpdb->last_error;
	$wpdb->last_error = '';
	$fields = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$tables['field']}` WHERE product_data_id = %s ORDER BY sort, id LIMIT %d FOR UPDATE", $id, WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS + 1 ), ARRAY_A );
	$field_error = $wpdb->last_error;
	$wpdb->last_error = '';
	$options = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$tables['option']}` WHERE product_data_id = %s ORDER BY sort, id LIMIT %d FOR UPDATE", $id, WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS * WPAGENT_ACPT_PRODUCT_DATA_MAX_OPTIONS + 1 ), ARRAY_A );
	$option_error = $wpdb->last_error;
	if ( '' !== $group_error || '' !== $field_error || '' !== $option_error || ! is_array( $groups ) || ! is_array( $fields ) || ! is_array( $options ) || count( $fields ) > WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS || count( $options ) > WPAGENT_ACPT_PRODUCT_DATA_MAX_FIELDS * WPAGENT_ACPT_PRODUCT_DATA_MAX_OPTIONS ) return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Die zu schützenden ACPT-Lite-Product-Data-Zeilen konnten nicht vollständig gesperrt werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	return array( 'group' => $groups, 'fields' => $fields, 'options' => $options );
}

/**
 * Locks the complete native Product Data primary-key range without loading any
 * provider rows. The explicit range predicate and PRIMARY hint make InnoDB
 * acquire shared next-key locks for all current and phantom group IDs while
 * this explicit SERIALIZABLE transaction is open. ACPT has no name index or
 * unique name constraint, so its native upsert cannot bypass this boundary.
 *
 * @return true|WP_Error
 */
function wpagent_acpt_product_data_lock_create_namespace( array $definition, array $provider ) {
	global $wpdb;
	$tables = wpagent_acpt_product_data_tables( $provider );
	if ( is_wp_error( $tables ) ) return $tables;
	$wpdb->last_error = '';
	$namespace_count = $wpdb->get_var( "SELECT COUNT(id) FROM `{$tables['group']}` FORCE INDEX (PRIMARY) WHERE id >= '' LOCK IN SHARE MODE" );
	if ( '' !== $wpdb->last_error || ! is_numeric( $namespace_count ) || (int) $namespace_count > WPAGENT_ACPT_PRODUCT_DATA_MAX_GROUPS ) return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Die ACPT-Lite-Product-Data-Namensdomäne konnte nicht sicher und begrenzt gesperrt werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$wpdb->last_error = '';
	$collision = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM `{$tables['group']}` FORCE INDEX (PRIMARY) WHERE id = %s OR LOWER(REPLACE(product_data_name, ' ', '_')) = %s LIMIT 1 FOR UPDATE", $definition['id'], wpagent_acpt_product_data_name_identity( $definition['name'] ) ) );
	if ( '' !== $wpdb->last_error ) return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Die ACPT-Lite-Product-Data-ID und ihr WooCommerce-Tabname konnten nicht sicher geprüft werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	return null === $collision ? true : new WP_Error( 'wpagent_acpt_product_data_conflict', __( 'Die ACPT-Lite-Product-Data-ID oder ihr WooCommerce-Tabname ist bereits belegt.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

/** @return true|WP_Error */
function wpagent_acpt_product_data_lock_child_id( string $table, string $id, array $provider ) {
	global $wpdb;
	$tables = wpagent_acpt_product_data_tables( $provider );
	if ( is_wp_error( $tables ) || ! in_array( $table, array( 'field', 'option' ), true ) ) return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Die ACPT-Lite-Child-ID konnte nicht sicher gesperrt werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$wpdb->last_error = '';
	$row = $wpdb->get_var( $wpdb->prepare( "SELECT id FROM `{$tables[ $table ]}` WHERE id = %s FOR UPDATE", $id ) );
	if ( '' !== $wpdb->last_error ) return new WP_Error( 'wpagent_acpt_product_data_transaction', __( 'Die ACPT-Lite-Child-ID konnte nicht sicher gesperrt werden.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	return null === $row ? true : new WP_Error( 'wpagent_acpt_product_data_conflict', __( 'Eine ACPT-Lite-Child-ID ist bereits belegt.', 'wp-agent-companion' ), array( 'status' => 409 ) );
}

/** @return string|WP_Error */
function wpagent_acpt_product_data_safe_text( $value, string $label, int $max, bool $allow_empty = true ) {
	if ( ! is_string( $value ) || wpagent_acpt_product_data_bytes( $value ) > $max || sanitize_text_field( $value ) !== $value || ( ! $allow_empty && '' === $value ) ) {
		/* translators: %s: field label. */
		return new WP_Error( 'wpagent_acpt_product_data_invalid', sprintf( __( '%s ist nicht sicher oder überschreitet die erlaubte Länge.', 'wp-agent-companion' ), $label ), array( 'status' => 400 ) );
	}
	return $value;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_product_data_public( array $raw ) {
	$group = $raw['group'] ?? null;
	$fields = $raw['fields'] ?? null;
	$options = $raw['options'] ?? null;
	if ( ! is_array( $group ) || ! is_array( $fields ) || ! is_array( $options ) || ! wpagent_acpt_product_data_uuid( $group['id'] ?? null ) || ! isset( $group['product_data_name'], $group['icon'], $group['visibility'], $group['content'], $group['show_in_ui'] ) ) return new WP_Error( 'wpagent_acpt_product_data_unsupported', __( 'Die ACPT-Lite-Product-Data-Gruppe liegt außerhalb des sicheren Vertrags.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$name = wpagent_acpt_product_data_safe_text( $group['product_data_name'], 'Gruppenname', 100, false );
	$content = $group['content'];
	if ( is_wp_error( $name ) || ! is_string( $content ) || wpagent_acpt_product_data_bytes( $content ) > WPAGENT_ACPT_PRODUCT_DATA_MAX_CONTENT_BYTES || ! in_array( (string) $group['show_in_ui'], array( '0', '1' ), true ) ) return new WP_Error( 'wpagent_acpt_product_data_unsupported', __( 'Die ACPT-Lite-Product-Data-Gruppe liegt außerhalb des sicheren Vertrags.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$icon = json_decode( (string) $group['icon'], true );
	$visibility = json_decode( (string) $group['visibility'], true );
	$visibility_values = array( 'show_if_simple', 'show_if_variable', 'show_if_grouped', 'show_if_external', 'hide_if_virtual', 'hide_if_external' );
	$visibility_strings = is_array( $visibility ) && count( $visibility ) === count( array_filter( $visibility, 'is_string' ) );
	if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $icon ) || ! is_array( $visibility ) || array_values( $visibility ) !== $visibility || count( $visibility ) > 6 || ! $visibility_strings || count( $visibility ) !== count( array_unique( $visibility ) ) || array_diff( $visibility, $visibility_values ) || wp_json_encode( $icon ) !== $group['icon'] || wp_json_encode( $visibility ) !== $group['visibility'] || count( $fields ) < 1 ) return new WP_Error( 'wpagent_acpt_product_data_unsupported', __( 'Die ACPT-Lite-Product-Data-Gruppe liegt außerhalb des sicheren Vertrags.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	$options_by_field = array();
	foreach ( $options as $option ) {
		if ( ! is_array( $option ) || ! wpagent_acpt_product_data_uuid( $option['id'] ?? null ) || ! wpagent_acpt_product_data_uuid( $option['product_data_field_id'] ?? null ) || ( $option['product_data_id'] ?? null ) !== $group['id'] ) return new WP_Error( 'wpagent_acpt_product_data_unsupported', __( 'Die ACPT-Lite-Product-Data-Gruppe liegt außerhalb des sicheren Vertrags.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$label = wpagent_acpt_product_data_safe_text( $option['option_label'] ?? null, 'Optionsbeschriftung', 100, false );
		$value = wpagent_acpt_product_data_safe_text( $option['option_value'] ?? null, 'Optionswert', 100, false );
		if ( is_wp_error( $label ) || is_wp_error( $value ) || ! is_numeric( $option['sort'] ?? null ) ) return new WP_Error( 'wpagent_acpt_product_data_unsupported', __( 'Die ACPT-Lite-Product-Data-Gruppe liegt außerhalb des sicheren Vertrags.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$options_by_field[ $option['product_data_field_id'] ][] = array( 'id' => $option['id'], 'label' => $label, 'value' => $value, 'sort' => (int) $option['sort'] );
	}
	$public_fields = array();
	$field_ids = array();
	foreach ( $fields as $field ) {
		if ( ! is_array( $field ) || ! wpagent_acpt_product_data_uuid( $field['id'] ?? null ) || ( $field['product_data_id'] ?? null ) !== $group['id'] || ! in_array( $field['field_type'] ?? null, array( 'Text', 'Number', 'Select' ), true ) || ! in_array( (string) ( $field['required'] ?? '' ), array( '0', '1' ), true ) || ! is_numeric( $field['sort'] ?? null ) ) return new WP_Error( 'wpagent_acpt_product_data_unsupported', __( 'Die ACPT-Lite-Product-Data-Gruppe liegt außerhalb des sicheren Vertrags.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$field_name = wpagent_acpt_product_data_safe_text( $field['field_name'] ?? null, 'Feldname', 100, false );
		$default = wpagent_acpt_product_data_safe_text( $field['field_default_value'] ?? null, 'Standardwert', 200 );
		$description = wpagent_acpt_product_data_safe_text( $field['field_description'] ?? null, 'Beschreibung', 500 );
		$field_options = $options_by_field[ $field['id'] ] ?? array();
		$invalid_select = 'Select' === $field['field_type'] && ( count( $field_options ) < 1 || count( $field_options ) !== count( array_unique( array_column( $field_options, 'value' ) ) ) );
		$invalid_number_default = 'Number' === $field['field_type'] && ( '' !== $default && ( ! preg_match( '/^-?(?:\d+|\d*\.\d+)$/D', $default ) || ! is_finite( (float) $default ) ) );
		$invalid_select_default = 'Select' === $field['field_type'] && '' !== $default && ! in_array( $default, array_column( $field_options, 'value' ), true );
		if ( is_wp_error( $field_name ) || is_wp_error( $default ) || is_wp_error( $description ) || count( $field_options ) > WPAGENT_ACPT_PRODUCT_DATA_MAX_OPTIONS || $invalid_select || $invalid_number_default || $invalid_select_default ) return new WP_Error( 'wpagent_acpt_product_data_unsupported', __( 'Die ACPT-Lite-Product-Data-Gruppe liegt außerhalb des sicheren Vertrags.', 'wp-agent-companion' ), array( 'status' => 409 ) );
		$field_ids[] = $field['id'];
		$public_fields[] = array( 'id' => $field['id'], 'name' => $field_name, 'type' => $field['field_type'], 'default_value' => $default, 'description' => $description, 'required' => '1' === (string) $field['required'], 'sort' => (int) $field['sort'], 'options' => $field_options );
	}
	foreach ( array_keys( $options_by_field ) as $field_id ) if ( ! in_array( $field_id, $field_ids, true ) ) return new WP_Error( 'wpagent_acpt_product_data_unsupported', __( 'Die ACPT-Lite-Product-Data-Gruppe liegt außerhalb des sicheren Vertrags.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	return array( 'id' => $group['id'], 'name' => $name, 'show_in_ui' => '1' === (string) $group['show_in_ui'], 'visibility' => $visibility, 'fields' => $public_fields );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_product_data_state( string $id, array $provider ) {
	$raw = wpagent_acpt_product_data_raw( $id, $provider );
	if ( is_wp_error( $raw ) ) return $raw;
	$public = wpagent_acpt_product_data_public( $raw );
	if ( is_wp_error( $public ) ) return $public;
	try {
		$models = $provider['repository']::get( array( 'id' => $id ) );
		$model = is_array( $models ) && 1 === count( $models ) ? $models[0] : null;
		$model_field_ids = array_map( static fn( $field ): string => $field->getId(), $model ? $model->getFields() : array() );
		$public_field_ids = array_column( $public['fields'], 'id' );
		sort( $model_field_ids, SORT_STRING );
		sort( $public_field_ids, SORT_STRING );
		$model_visibility = $model && is_object( $model->getVisibility() ) ? (array) $model->getVisibility() : ( $model ? $model->getVisibility() : null );
		if ( ! $model || ! is_a( $model, $provider['model'] ) || $model->getId() !== $id || $model->getName() !== $public['name'] || $model->isShowInUI() !== $public['show_in_ui'] || $model_visibility !== $public['visibility'] || $model_field_ids !== $public_field_ids ) throw new UnexpectedValueException( 'Native ACPT product data mismatch.' );
		$public_fields = array_column( $public['fields'], null, 'id' );
		foreach ( $model->getFields() as $field ) {
			$field_id = $field->getId();
			$expected = $public_fields[ $field_id ] ?? null;
			if ( ! is_array( $expected ) || $field->getName() !== $expected['name'] || $field->getType() !== $expected['type'] || (string) $field->getDefaultValue() !== $expected['default_value'] || (string) $field->getDescription() !== $expected['description'] || $field->isRequired() !== $expected['required'] || (int) $field->getSort() !== $expected['sort'] ) throw new UnexpectedValueException( 'Native ACPT field mismatch.' );
			$options = array();
			foreach ( $field->getOptions() as $option ) $options[] = array( 'id' => $option->getId(), 'label' => $option->getLabel(), 'value' => $option->getValue(), 'sort' => (int) $option->getSort() );
			if ( wpagent_acpt_product_data_canonicalize( $options ) !== wpagent_acpt_product_data_canonicalize( $expected['options'] ) ) throw new UnexpectedValueException( 'Native ACPT option mismatch.' );
		}
	} catch ( Throwable $error ) {
		return new WP_Error( 'wpagent_acpt_product_data_api', __( 'Der native ACPT-Lite-Product-Data-Readback ist nicht vollständig.', 'wp-agent-companion' ), array( 'status' => 409 ) );
	}
	return array( 'raw' => $raw, 'public' => $public, 'state_hash' => wpagent_acpt_product_data_hash( $raw ), 'model' => $model );
}

function wpagent_acpt_product_data_lock_name( string $id ): string {
	return 'wp_agent_acpt_product_data_lock_' . $id;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_acpt_product_data_recovery_error( string $message, ?array $state = null ) {
	$data = array( 'status' => 409, 'recovery_required' => true );
	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 );
}
