<?php
/**
 * WordPress and WooCommerce REST schema hints.
 *
 * @package ScheduledChanges
 */

namespace ScheduledChangesWooCommerce\CoreCompat\Discovery;

use Throwable;

/**
 * Reads public REST schemas as typing hints, never as write authorization.
 */
final class RestSchemaInspector {
	/**
	 * @var array<string,string>
	 */
	private const CONTROLLERS = array(
		'WC_REST_Products_Controller'           => 'wc/v3/products',
		'WC_REST_Product_Variations_Controller' => 'wc/v3/product-variations',
	);

	/**
	 * @return array<string,array<string,mixed>>
	 */
	public function productHints(): array {
		$hints = array();

		foreach ( self::CONTROLLERS as $class_name => $source ) {
			try {
				$controller = new $class_name();
				$this->appendSchema( $hints, $source, $controller->get_item_schema() );
			} catch ( Throwable $throwable ) {
				unset( $throwable );
			}
		}

		$post_type = get_post_type_object( 'product' );

		if ( $post_type instanceof \WP_Post_Type && $post_type->show_in_rest ) {
			try {
				$controller = $post_type->get_rest_controller();

				if ( null !== $controller ) {
					$this->appendSchema( $hints, 'wp/v2/product', $controller->get_item_schema() );
				}
			} catch ( Throwable $throwable ) {
				unset( $throwable );
			}
		}

		global $wp_rest_additional_fields;

		foreach ( array( 'product', 'product_variation' ) as $object_type ) {
			$additional = is_array( $wp_rest_additional_fields[ $object_type ] ?? null )
				? $wp_rest_additional_fields[ $object_type ]
				: array();

			foreach ( $additional as $name => $arguments ) {
				$property = is_array( $arguments['schema'] ?? null )
					? $arguments['schema']
					: array();

				if ( array() === $property ) {
					continue;
				}

				$source = 'wp-rest-field/' . $object_type;
				$this->appendProperty( $hints, $source, (string) $name, $property );
			}
		}

		ksort( $hints );

		return $hints;
	}

	/**
	 * @param array<string,array<string,mixed>> $hints Aggregated hints.
	 * @param array<string,mixed>               $schema REST item schema.
	 */
	private function appendSchema( array &$hints, string $source, array $schema ): void {
		$properties = is_array( $schema['properties'] ?? null ) ? $schema['properties'] : array();

		foreach ( $properties as $name => $property ) {
			if ( is_array( $property ) ) {
				$this->appendProperty( $hints, $source, (string) $name, $property );
			}
		}
	}

	/**
	 * @param array<string,array<string,mixed>> $hints Aggregated hints.
	 * @param array<string,mixed>               $property REST property schema.
	 */
	private function appendProperty(
		array &$hints,
		string $source,
		string $name,
		array $property
	): void {
		$key           = $source . '.' . $name;
		$hints[ $key ] = array(
			'name'        => $name,
			'source'      => $source,
			'type'        => $property['type'] ?? 'mixed',
			'description' => wp_strip_all_tags( (string) ( $property['description'] ?? '' ) ),
			'read_only'   => (bool) ( $property['readonly'] ?? false ),
			'enum'        => is_array( $property['enum'] ?? null ) ? array_values( $property['enum'] ) : array(),
			'contexts'    => is_array( $property['context'] ?? null ) ? array_values( $property['context'] ) : array(),
		);
	}
}
