<?php
/**
 * Geschlossene, begrenzte Leseverträge für WooCommerce-Produkte und Varianten.
 *
 * Die nativen WooCommerce-Getter bleiben die einzige Datenquelle. Diese
 * Schicht prüft deren Werte vor der REST-Ausgabe, damit ein fehlerhafter oder
 * unerwartet großer Providerwert weder offene Felder noch unbegrenzte Daten an
 * einen Agenten übergibt.
 *
 * @package WPAgentCompanion
 */

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

define( 'WPAGENT_WC_READ_MAX_CATALOG_ITEMS', 25 );
define( 'WPAGENT_WC_READ_MAX_TOTAL', 1000000 );
define( 'WPAGENT_WC_READ_MAX_PAGE', 1000000 );
define( 'WPAGENT_WC_READ_MAX_SEARCH_CANDIDATES', 1000 );

/** @return array<string,mixed> */
function wpagent_wc_read_text_schema( int $maximum, int $minimum = 0 ): array {
	$schema = array( 'type' => 'string', 'maxLength' => $maximum );
	if ( $minimum > 0 ) {
		$schema['minLength'] = $minimum;
	}
	return $schema;
}

/** @return array<string,mixed> */
function wpagent_wc_read_id_schema( int $minimum = 1 ): array {
	return array( 'type' => 'integer', 'minimum' => $minimum, 'maximum' => 2147483647 );
}

/** @return array<string,mixed> */
function wpagent_wc_product_attribute_read_schema(): array {
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'id'        => wpagent_wc_read_id_schema( 0 ),
			'name'      => wpagent_wc_read_text_schema( 200, 1 ),
			'taxonomy'  => array( 'type' => 'boolean' ),
			'options'   => array( 'type' => 'array', 'maxItems' => 100, 'items' => wpagent_wc_read_text_schema( 200, 1 ) ),
			'visible'   => array( 'type' => 'boolean' ),
			'variation' => array( 'type' => 'boolean' ),
			'position'  => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => 10000 ),
		),
		'required'             => array( 'id', 'name', 'taxonomy', 'options', 'visible', 'variation', 'position' ),
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_wc_variation_attributes_read_schema(): array {
	return array(
		// WooCommerce represents an empty attribute map as an empty PHP array.
		// A composition keeps that exceptional list form finite without allowing
		// arbitrary array values beside the bounded provider attribute map.
		'oneOf' => array(
			array(
				'type'                 => 'object',
				'maxProperties'        => 50,
				'additionalProperties' => wpagent_wc_read_text_schema( 200 ),
			),
			array(
				'type'     => 'array',
				'maxItems' => 0,
				'items'    => wpagent_wc_read_text_schema( 200 ),
			),
		),
	);
}

/**
 * @param list<string> $fields
 * @return array<string,mixed>
 */
function wpagent_wc_written_fields_schema( array $fields ): array {
	$properties = array();
	foreach ( $fields as $field ) {
		$properties[ $field ] = array( 'type' => 'boolean' );
	}
	return array(
		'type'                 => 'object',
		'properties'           => $properties,
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_wc_product_written_schema(): array {
	return wpagent_wc_written_fields_schema(
		array(
			'name', 'status', 'sku', 'regular_price', 'sale_price',
			'weight', 'length', 'width', 'height', 'short_description',
			'description', 'manage_stock', 'virtual', 'downloadable', 'featured',
			'stock_quantity', 'stock_status', 'catalog_visibility', 'image_id',
			'gallery_image_ids', 'category_ids', 'tag_ids', 'shipping_class_id',
			'attributes',
		)
	);
}

/** @return array<string,mixed> */
function wpagent_wc_variation_written_schema(): array {
	return wpagent_wc_written_fields_schema(
		array(
			'parent_id', 'attributes', 'sku', 'regular_price', 'sale_price',
			'manage_stock', 'stock_quantity', 'stock_status', 'image_id', 'virtual',
			'downloadable', 'weight', 'length', 'width', 'height', 'description', 'status',
		)
	);
}

/** @return array<string,mixed> */
function wpagent_wc_product_read_schema(): array {
	$properties = array(
		'product_id'         => wpagent_wc_read_id_schema(),
		'type'               => array( 'type' => 'string', 'enum' => array( 'simple', 'variable', 'grouped', 'external' ) ),
		'status'             => array( 'type' => 'string', 'enum' => array( 'draft', 'publish', 'pending', 'private', 'future', 'trash' ) ),
		'name'               => wpagent_wc_read_text_schema( 255, 1 ),
		'slug'               => wpagent_wc_read_text_schema( 200 ),
		'sku'                => wpagent_wc_read_text_schema( 191 ),
		'regular_price'      => wpagent_wc_read_text_schema( 32 ),
		'sale_price'         => wpagent_wc_read_text_schema( 32 ),
		'price'              => wpagent_wc_read_text_schema( 32 ),
		'stock_quantity'     => array( 'type' => array( 'integer', 'null' ), 'minimum' => -1000000000, 'maximum' => 1000000000 ),
		'manage_stock'       => array( 'type' => 'boolean' ),
		'stock_status'       => array( 'type' => 'string', 'enum' => array( 'instock', 'outofstock', 'onbackorder' ) ),
		'short_description'  => wpagent_wc_read_text_schema( 10000 ),
		'description'        => wpagent_wc_read_text_schema( 200000 ),
		'virtual'            => array( 'type' => 'boolean' ),
		'downloadable'       => array( 'type' => 'boolean' ),
		'featured'           => array( 'type' => 'boolean' ),
		'catalog_visibility' => array( 'type' => 'string', 'enum' => array( 'visible', 'catalog', 'search', 'hidden' ) ),
		'weight'             => wpagent_wc_read_text_schema( 32 ),
		'dimensions'         => array(
			'type'                 => 'object',
			'properties'           => array(
				'length' => wpagent_wc_read_text_schema( 32 ),
				'width'  => wpagent_wc_read_text_schema( 32 ),
				'height' => wpagent_wc_read_text_schema( 32 ),
			),
			'required'             => array( 'length', 'width', 'height' ),
			'additionalProperties' => false,
		),
		'image_id'           => wpagent_wc_read_id_schema( 0 ),
		'gallery_image_ids'  => array( 'type' => 'array', 'maxItems' => 100, 'items' => wpagent_wc_read_id_schema() ),
		'category_ids'       => array( 'type' => 'array', 'maxItems' => 100, 'items' => wpagent_wc_read_id_schema() ),
		'tag_ids'            => array( 'type' => 'array', 'maxItems' => 100, 'items' => wpagent_wc_read_id_schema() ),
		'shipping_class_id'  => wpagent_wc_read_id_schema( 0 ),
		'attributes'         => array( 'type' => 'array', 'maxItems' => 50, 'items' => wpagent_wc_product_attribute_read_schema() ),
		'state_hash'         => array( 'type' => 'string', 'minLength' => 64, 'maxLength' => 64, 'pattern' => '^[a-f0-9]{64}$' ),
	);
	return array( 'type' => 'object', 'properties' => $properties, 'required' => array_keys( $properties ), 'additionalProperties' => false );
}

/** @return array<string,mixed> */
function wpagent_wc_variation_read_schema(): array {
	$properties = array(
		'variation_id'   => wpagent_wc_read_id_schema(),
		'parent_id'      => wpagent_wc_read_id_schema(),
		'status'         => array( 'type' => 'string', 'enum' => array( 'draft', 'publish', 'pending', 'private', 'future', 'trash' ) ),
		'sku'            => wpagent_wc_read_text_schema( 191 ),
		'regular_price'  => wpagent_wc_read_text_schema( 32 ),
		'sale_price'     => wpagent_wc_read_text_schema( 32 ),
		'price'          => wpagent_wc_read_text_schema( 32 ),
		'manage_stock'   => array( 'type' => 'boolean' ),
		'stock_quantity' => array( 'type' => array( 'integer', 'null' ), 'minimum' => -1000000000, 'maximum' => 1000000000 ),
		'stock_status'   => array( 'type' => 'string', 'enum' => array( 'instock', 'outofstock', 'onbackorder' ) ),
		'virtual'        => array( 'type' => 'boolean' ),
		'downloadable'   => array( 'type' => 'boolean' ),
		'weight'         => wpagent_wc_read_text_schema( 32 ),
		'dimensions'     => array(
			'type'                 => 'object',
			'properties'           => array(
				'length' => wpagent_wc_read_text_schema( 32 ),
				'width'  => wpagent_wc_read_text_schema( 32 ),
				'height' => wpagent_wc_read_text_schema( 32 ),
			),
			'required'             => array( 'length', 'width', 'height' ),
			'additionalProperties' => false,
		),
		'image_id'       => wpagent_wc_read_id_schema( 0 ),
		'description'    => wpagent_wc_read_text_schema( 10000 ),
		'attributes'     => wpagent_wc_variation_attributes_read_schema(),
		'state_hash'     => array( 'type' => 'string', 'minLength' => 64, 'maxLength' => 64, 'pattern' => '^[a-f0-9]{64}$' ),
	);
	return array( 'type' => 'object', 'properties' => $properties, 'required' => array_keys( $properties ), 'additionalProperties' => false );
}

/** @return array<string,mixed> */
function wpagent_wc_catalog_read_schema(): array {
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'products' => array( 'type' => 'array', 'maxItems' => WPAGENT_WC_READ_MAX_CATALOG_ITEMS, 'items' => wpagent_wc_product_read_schema() ),
			'total'    => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => WPAGENT_WC_READ_MAX_TOTAL ),
			'pages'    => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => WPAGENT_WC_READ_MAX_TOTAL ),
		),
		'required'             => array( 'products', 'total', 'pages' ),
		'additionalProperties' => false,
	);
}

/** @return array<string,mixed> */
function wpagent_wc_variation_catalog_read_schema(): array {
	return array(
		'type'                 => 'object',
		'properties'           => array(
			'variations' => array( 'type' => 'array', 'maxItems' => WPAGENT_WC_READ_MAX_CATALOG_ITEMS, 'items' => wpagent_wc_variation_read_schema() ),
			'total'      => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => WPAGENT_WC_READ_MAX_TOTAL ),
			'pages'      => array( 'type' => 'integer', 'minimum' => 0, 'maximum' => WPAGENT_WC_READ_MAX_TOTAL ),
		),
		'required'             => array( 'variations', 'total', 'pages' ),
		'additionalProperties' => false,
	);
}

/** @return WP_Error */
function wpagent_wc_read_contract_error( string $kind, string $field ): WP_Error {
	$code    = 'overflow' === $kind ? 'wpagent_wc_read_output_overflow' : 'wpagent_wc_read_output_mismatch';
	$message = 'overflow' === $kind
		? __( 'WooCommerce hat mehr Daten geliefert als dieser sichere Leseweg ausgeben darf.', 'wp-agent-companion' )
		: __( 'Die WooCommerce-Produktdaten entsprechen nicht dem sicheren Lesevertrag.', 'wp-agent-companion' );
	return new WP_Error( $code, $message, array( 'status' => 500, 'field' => $field ) );
}

/** @return true|WP_Error */
function wpagent_wc_read_exact_keys( array $value, array $keys, string $field ) {
	if ( count( $value ) !== count( $keys ) || array_diff( array_keys( $value ), $keys ) || array_diff( $keys, array_keys( $value ) ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', $field );
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_wc_read_bounded_string( $value, int $maximum, string $field, bool $nonempty = false ) {
	if ( ! is_string( $value ) || false === preg_match( '//u', $value ) || str_contains( $value, "\0" ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', $field );
	}
	if ( strlen( $value ) > $maximum ) {
		return wpagent_wc_read_contract_error( 'overflow', $field );
	}
	if ( $nonempty && '' === $value ) {
		return wpagent_wc_read_contract_error( 'mismatch', $field );
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_wc_read_integer( $value, int $minimum, int $maximum, string $field, bool $nullable = false ) {
	if ( $nullable && null === $value ) {
		return true;
	}
	if ( ! is_int( $value ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', $field );
	}
	if ( $value < $minimum || $value > $maximum ) {
		return wpagent_wc_read_contract_error( 'overflow', $field );
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_wc_read_enum( $value, array $allowed, string $field ) {
	if ( ! is_string( $value ) || ! in_array( $value, $allowed, true ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', $field );
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_wc_read_id_list( $value, int $maximum, string $field ) {
	if ( ! is_array( $value ) || ! array_is_list( $value ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', $field );
	}
	if ( count( $value ) > $maximum ) {
		return wpagent_wc_read_contract_error( 'overflow', $field );
	}
	foreach ( $value as $index => $item ) {
		$valid = wpagent_wc_read_integer( $item, 1, 2147483647, $field . '[' . $index . ']' );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
	}
	return true;
}

/** @return list<int>|WP_Error */
function wpagent_wc_read_search_ids( $ids ) {
	if ( ! is_array( $ids ) || ! array_is_list( $ids ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', 'search_ids' );
	}
	// WC_Product_Data_Store_CPT appends one zero when the textual query has no
	// parent product. It is a documented provider sentinel, never a product ID.
	if ( count( $ids ) > WPAGENT_WC_READ_MAX_SEARCH_CANDIDATES + 1 ) {
		return wpagent_wc_read_contract_error( 'overflow', 'search_ids' );
	}
	$normalized             = array();
	$zero_parent_candidates = 0;
	foreach ( $ids as $index => $id ) {
		if ( 0 === $id ) {
			++$zero_parent_candidates;
			if ( $zero_parent_candidates > 1 ) {
				return wpagent_wc_read_contract_error( 'mismatch', 'search_ids' );
			}
			continue;
		}
		$valid = wpagent_wc_read_integer( $id, 1, 2147483647, 'search_ids[' . $index . ']' );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
		$normalized[] = $id;
		if ( count( $normalized ) > WPAGENT_WC_READ_MAX_SEARCH_CANDIDATES ) {
			return wpagent_wc_read_contract_error( 'overflow', 'search_ids' );
		}
	}
	return $normalized;
}

/** @return true|WP_Error */
function wpagent_wc_read_dimensions( $value, string $field ) {
	if ( ! is_array( $value ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', $field );
	}
	$keys = array( 'length', 'width', 'height' );
	$valid = wpagent_wc_read_exact_keys( $value, $keys, $field );
	if ( is_wp_error( $valid ) ) {
		return $valid;
	}
	foreach ( $keys as $key ) {
		$valid = wpagent_wc_read_bounded_string( $value[ $key ], 32, $field . '.' . $key );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_wc_read_product_attributes( $value ) {
	if ( ! is_array( $value ) || ! array_is_list( $value ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', 'attributes' );
	}
	if ( count( $value ) > 50 ) {
		return wpagent_wc_read_contract_error( 'overflow', 'attributes' );
	}
	$keys = array( 'id', 'name', 'taxonomy', 'options', 'visible', 'variation', 'position' );
	foreach ( $value as $index => $attribute ) {
		if ( ! is_array( $attribute ) ) {
			return wpagent_wc_read_contract_error( 'mismatch', 'attributes[' . $index . ']' );
		}
		$valid = wpagent_wc_read_exact_keys( $attribute, $keys, 'attributes[' . $index . ']' );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
		foreach ( array(
			wpagent_wc_read_integer( $attribute['id'], 0, 2147483647, 'attributes[' . $index . '].id' ),
			wpagent_wc_read_bounded_string( $attribute['name'], 200, 'attributes[' . $index . '].name', true ),
			wpagent_wc_read_integer( $attribute['position'], 0, 10000, 'attributes[' . $index . '].position' ),
		) as $check ) {
			if ( is_wp_error( $check ) ) {
				return $check;
			}
		}
		if ( ! is_bool( $attribute['taxonomy'] ) || ! is_bool( $attribute['visible'] ) || ! is_bool( $attribute['variation'] ) ) {
			return wpagent_wc_read_contract_error( 'mismatch', 'attributes[' . $index . ']' );
		}
		if ( ! is_array( $attribute['options'] ) || ! array_is_list( $attribute['options'] ) ) {
			return wpagent_wc_read_contract_error( 'mismatch', 'attributes[' . $index . '].options' );
		}
		if ( count( $attribute['options'] ) > 100 ) {
			return wpagent_wc_read_contract_error( 'overflow', 'attributes[' . $index . '].options' );
		}
		foreach ( $attribute['options'] as $option_index => $option ) {
			$valid = wpagent_wc_read_bounded_string( $option, 200, 'attributes[' . $index . '].options[' . $option_index . ']', true );
			if ( is_wp_error( $valid ) ) {
				return $valid;
			}
		}
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_wc_read_variation_attributes( $value ) {
	if ( ! is_array( $value ) || ( array() !== $value && array_is_list( $value ) ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', 'attributes' );
	}
	if ( count( $value ) > 50 ) {
		return wpagent_wc_read_contract_error( 'overflow', 'attributes' );
	}
	foreach ( $value as $key => $item ) {
		if ( ! is_string( $key ) || '' === $key ) {
			return wpagent_wc_read_contract_error( 'mismatch', 'attributes' );
		}
		$valid = wpagent_wc_read_bounded_string( $key, 200, 'attributes.key', true );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
		$valid = wpagent_wc_read_bounded_string( $item, 200, 'attributes.value' );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_wc_read_state_hash( array $payload, string $field ) {
	$state_hash = $payload['state_hash'] ?? null;
	if ( ! is_string( $state_hash ) || ! preg_match( '/^[a-f0-9]{64}$/', $state_hash ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', $field );
	}
	$hash_input = $payload;
	unset( $hash_input['state_hash'] );
	if ( ! hash_equals( wpagent_wc_state_hash( $hash_input ), $state_hash ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', $field );
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_wc_validate_product_read_payload( array $payload ) {
	$keys = array_keys( wpagent_wc_product_read_schema()['properties'] );
	$valid = wpagent_wc_read_exact_keys( $payload, $keys, 'product' );
	if ( is_wp_error( $valid ) ) {
		return $valid;
	}
	foreach ( array(
		wpagent_wc_read_integer( $payload['product_id'], 1, 2147483647, 'product_id' ),
		wpagent_wc_read_enum( $payload['type'], array( 'simple', 'variable', 'grouped', 'external' ), 'type' ),
		wpagent_wc_read_enum( $payload['status'], array( 'draft', 'publish', 'pending', 'private', 'future', 'trash' ), 'status' ),
		wpagent_wc_read_bounded_string( $payload['name'], 255, 'name', true ),
		wpagent_wc_read_bounded_string( $payload['slug'], 200, 'slug' ),
		wpagent_wc_read_bounded_string( $payload['sku'], 191, 'sku' ),
		wpagent_wc_read_bounded_string( $payload['regular_price'], 32, 'regular_price' ),
		wpagent_wc_read_bounded_string( $payload['sale_price'], 32, 'sale_price' ),
		wpagent_wc_read_bounded_string( $payload['price'], 32, 'price' ),
		wpagent_wc_read_integer( $payload['stock_quantity'], -1000000000, 1000000000, 'stock_quantity', true ),
		wpagent_wc_read_enum( $payload['stock_status'], array( 'instock', 'outofstock', 'onbackorder' ), 'stock_status' ),
		wpagent_wc_read_bounded_string( $payload['short_description'], 10000, 'short_description' ),
		wpagent_wc_read_bounded_string( $payload['description'], 200000, 'description' ),
		wpagent_wc_read_enum( $payload['catalog_visibility'], array( 'visible', 'catalog', 'search', 'hidden' ), 'catalog_visibility' ),
		wpagent_wc_read_bounded_string( $payload['weight'], 32, 'weight' ),
		wpagent_wc_read_integer( $payload['image_id'], 0, 2147483647, 'image_id' ),
		wpagent_wc_read_integer( $payload['shipping_class_id'], 0, 2147483647, 'shipping_class_id' ),
	) as $check ) {
		if ( is_wp_error( $check ) ) {
			return $check;
		}
	}
	foreach ( array( 'manage_stock', 'virtual', 'downloadable', 'featured' ) as $field ) {
		if ( ! is_bool( $payload[ $field ] ) ) {
			return wpagent_wc_read_contract_error( 'mismatch', $field );
		}
	}
	foreach ( array( 'gallery_image_ids', 'category_ids', 'tag_ids' ) as $field ) {
		$valid = wpagent_wc_read_id_list( $payload[ $field ], 100, $field );
		if ( is_wp_error( $valid ) ) {
			return $valid;
		}
	}
	foreach ( array(
		wpagent_wc_read_dimensions( $payload['dimensions'], 'dimensions' ),
		wpagent_wc_read_product_attributes( $payload['attributes'] ),
		wpagent_wc_read_state_hash( $payload, 'state_hash' ),
	) as $check ) {
		if ( is_wp_error( $check ) ) {
			return $check;
		}
	}
	return true;
}

/** @return true|WP_Error */
function wpagent_wc_validate_variation_read_payload( array $payload ) {
	$keys = array_keys( wpagent_wc_variation_read_schema()['properties'] );
	$valid = wpagent_wc_read_exact_keys( $payload, $keys, 'variation' );
	if ( is_wp_error( $valid ) ) {
		return $valid;
	}
	foreach ( array(
		wpagent_wc_read_integer( $payload['variation_id'], 1, 2147483647, 'variation_id' ),
		wpagent_wc_read_integer( $payload['parent_id'], 1, 2147483647, 'parent_id' ),
		wpagent_wc_read_enum( $payload['status'], array( 'draft', 'publish', 'pending', 'private', 'future', 'trash' ), 'status' ),
		wpagent_wc_read_bounded_string( $payload['sku'], 191, 'sku' ),
		wpagent_wc_read_bounded_string( $payload['regular_price'], 32, 'regular_price' ),
		wpagent_wc_read_bounded_string( $payload['sale_price'], 32, 'sale_price' ),
		wpagent_wc_read_bounded_string( $payload['price'], 32, 'price' ),
		wpagent_wc_read_integer( $payload['stock_quantity'], -1000000000, 1000000000, 'stock_quantity', true ),
		wpagent_wc_read_enum( $payload['stock_status'], array( 'instock', 'outofstock', 'onbackorder' ), 'stock_status' ),
		wpagent_wc_read_bounded_string( $payload['weight'], 32, 'weight' ),
		wpagent_wc_read_integer( $payload['image_id'], 0, 2147483647, 'image_id' ),
		wpagent_wc_read_bounded_string( $payload['description'], 10000, 'description' ),
	) as $check ) {
		if ( is_wp_error( $check ) ) {
			return $check;
		}
	}
	foreach ( array( 'manage_stock', 'virtual', 'downloadable' ) as $field ) {
		if ( ! is_bool( $payload[ $field ] ) ) {
			return wpagent_wc_read_contract_error( 'mismatch', $field );
		}
	}
	foreach ( array(
		wpagent_wc_read_dimensions( $payload['dimensions'], 'dimensions' ),
		wpagent_wc_read_variation_attributes( $payload['attributes'] ),
		wpagent_wc_read_state_hash( $payload, 'state_hash' ),
	) as $check ) {
		if ( is_wp_error( $check ) ) {
			return $check;
		}
	}
	return true;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_wc_read_product_payload( WC_Product $product ) {
	try {
		$payload = wpagent_wc_product_payload( $product, true );
	} catch ( Throwable $error ) {
		return wpagent_wc_read_contract_error( 'mismatch', 'product' );
	}
	$valid = wpagent_wc_validate_product_read_payload( $payload );
	return is_wp_error( $valid ) ? $valid : $payload;
}

/** @return array<string,mixed>|WP_Error */
function wpagent_wc_read_variation_payload( WC_Product_Variation $variation ) {
	try {
		$payload = wpagent_wc_variation_payload( $variation );
	} catch ( Throwable $error ) {
		return wpagent_wc_read_contract_error( 'mismatch', 'variation' );
	}
	$valid = wpagent_wc_validate_variation_read_payload( $payload );
	return is_wp_error( $valid ) ? $valid : $payload;
}

/** @return array{page:int,per_page:int}|WP_Error */
function wpagent_wc_read_pagination_input( array $input ) {
	$page     = $input['page'] ?? 1;
	$per_page = $input['per_page'] ?? 20;
	if ( ! is_int( $page ) || ! is_int( $per_page ) || $page < 1 || $page > WPAGENT_WC_READ_MAX_PAGE || $per_page < 1 || $per_page > WPAGENT_WC_READ_MAX_CATALOG_ITEMS ) {
		return new WP_Error( 'wpagent_wc_invalid_read_input', __( 'Die Seitennummer oder Seitengröße ist ungültig.', 'wp-agent-companion' ), array( 'status' => 400 ) );
	}
	return array( 'page' => $page, 'per_page' => $per_page );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_wc_read_catalog_payload( $items, $total, $pages, int $per_page ) {
	if ( ! is_array( $items ) || ! array_is_list( $items ) || ! is_int( $total ) || ! is_int( $pages ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', 'catalog' );
	}
	if ( count( $items ) > WPAGENT_WC_READ_MAX_CATALOG_ITEMS || count( $items ) > $per_page || $total < 0 || $total > WPAGENT_WC_READ_MAX_TOTAL || $pages < 0 || $pages > WPAGENT_WC_READ_MAX_TOTAL ) {
		return wpagent_wc_read_contract_error( 'overflow', 'catalog' );
	}
	$expected_pages = 0 === $total ? 0 : (int) ceil( $total / $per_page );
	if ( $total < count( $items ) || $pages !== $expected_pages ) {
		return wpagent_wc_read_contract_error( 'mismatch', 'catalog' );
	}
	$products = array();
	foreach ( $items as $item ) {
		if ( ! $item instanceof WC_Product || $item->is_type( 'variation' ) ) {
			return wpagent_wc_read_contract_error( 'mismatch', 'products' );
		}
		$payload = wpagent_wc_read_product_payload( $item );
		if ( is_wp_error( $payload ) ) {
			return $payload;
		}
		$products[] = $payload;
	}
	return array( 'products' => $products, 'total' => $total, 'pages' => $pages );
}

/** @return array<string,mixed>|WP_Error */
function wpagent_wc_read_variation_catalog_payload( $items, $total, $pages, int $per_page, int $product_id ) {
	if ( ! is_array( $items ) || ! array_is_list( $items ) || ! is_int( $total ) || ! is_int( $pages ) ) {
		return wpagent_wc_read_contract_error( 'mismatch', 'variations' );
	}
	if ( count( $items ) > WPAGENT_WC_READ_MAX_CATALOG_ITEMS || count( $items ) > $per_page || $total < 0 || $total > WPAGENT_WC_READ_MAX_TOTAL || $pages < 0 || $pages > WPAGENT_WC_READ_MAX_TOTAL ) {
		return wpagent_wc_read_contract_error( 'overflow', 'variations' );
	}
	$expected_pages = 0 === $total ? 0 : (int) ceil( $total / $per_page );
	if ( $total < count( $items ) || $pages !== $expected_pages ) {
		return wpagent_wc_read_contract_error( 'mismatch', 'variations' );
	}
	$variations = array();
	foreach ( $items as $item ) {
		if ( ! $item instanceof WC_Product_Variation || (int) $item->get_parent_id() !== $product_id ) {
			return wpagent_wc_read_contract_error( 'mismatch', 'variations' );
		}
		$payload = wpagent_wc_read_variation_payload( $item );
		if ( is_wp_error( $payload ) ) {
			return $payload;
		}
		$variations[] = $payload;
	}
	return array( 'variations' => $variations, 'total' => $total, 'pages' => $pages );
}
