<?php
/**
 * Content picker REST route.
 *
 * @package MenuVerse
 */

namespace WPXero\MenuVerse\Rest;

defined( 'ABSPATH' ) || exit;

/**
 * Serves the dashboard's page and popup pickers.
 *
 * Core's own `/wp/v2/pages` would cover pages, but not Popup Maker's `popup`
 * post type, which does not reliably opt into REST. One route that returns the
 * same small shape for both keeps the picker component simple and means the
 * capability gate is MenuVerse's own.
 */
final class Content_Controller extends Rest_Controller {

	/**
	 * Largest number of entries returned for one post type.
	 *
	 * A picker is not a place to load ten thousand posts. Beyond this the
	 * response says it was truncated so the control can tell the user to
	 * search rather than silently offering a partial list.
	 *
	 * @var int
	 */
	const LIMIT = 200;

	/**
	 * Post types this route will serve, keyed by request value.
	 *
	 * @return array<string, string>
	 */
	private function post_types() {
		return array(
			'page'  => 'page',
			'popup' => 'popup',
		);
	}

	/**
	 * Registers routes.
	 *
	 * @return void
	 */
	public function register_routes() {
		register_rest_route(
			self::REST_NAMESPACE,
			'/content',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_content' ),
					'permission_callback' => array( $this, 'permission_check' ),
					'args'                => array(
						'type'   => array(
							'type'    => 'string',
							'enum'    => array_keys( $this->post_types() ),
							'default' => 'page',
						),
						'search' => array(
							'type'              => 'string',
							'default'           => '',
							'sanitize_callback' => 'sanitize_text_field',
						),
					),
				),
			)
		);
	}

	/**
	 * Returns entries for one post type.
	 *
	 * @param \WP_REST_Request $request Request.
	 * @return \WP_REST_Response
	 */
	public function get_content( $request ) {
		$types = $this->post_types();
		$key   = $request->get_param( 'type' );
		$type  = isset( $types[ $key ] ) ? $types[ $key ] : 'page';

		if ( ! post_type_exists( $type ) ) {
			return rest_ensure_response(
				array(
					'items'     => array(),
					'truncated' => false,
					// Lets the picker explain itself rather than showing an
					// empty dropdown with no reason given.
					'reason'    => 'inactive',
				)
			);
		}

		$query = array(
			'post_type'              => $type,
			'post_status'            => 'publish',
			'posts_per_page'         => self::LIMIT + 1,
			'orderby'                => 'title',
			'order'                  => 'ASC',
			'suppress_filters'       => false,
			'ignore_sticky_posts'    => true,
			'no_found_rows'          => true,
			'update_post_meta_cache' => false,
			'update_post_term_cache' => false,
		);

		$search = (string) $request->get_param( 'search' );

		if ( '' !== $search ) {
			$query['s'] = $search;
		}

		$posts     = get_posts( $query );
		$truncated = count( $posts ) > self::LIMIT;
		$posts     = array_slice( $posts, 0, self::LIMIT );
		$items     = array();

		foreach ( $posts as $post ) {
			$title = get_the_title( $post );

			$items[] = array(
				'id'    => (int) $post->ID,
				/* translators: %d: post id, used when a post has no title. */
				'title' => '' !== $title ? $title : sprintf( __( '#%d (no title)', 'menuverse' ), $post->ID ),
			);
		}

		return rest_ensure_response(
			array(
				'items'     => $items,
				'truncated' => $truncated,
				'reason'    => '',
			)
		);
	}
}
