<?php
/**
 * Menu source REST routes.
 *
 * @package MenuVerse
 */

namespace WPXero\MenuVerse\Rest;

use WPXero\MenuVerse\Menus\Item_Extras;
use WPXero\MenuVerse\Menus\Menu_Tree;

defined( 'ABSPATH' ) || exit;

/**
 * Exposes normalized menus from every registered source.
 */
final class Menus_Controller extends Rest_Controller {

	/**
	 * Registers routes.
	 *
	 * @return void
	 */
	public function register_routes() {
		register_rest_route(
			self::REST_NAMESPACE,
			'/menus',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_menus' ),
					'permission_callback' => array( $this, 'permission_check' ),
				),
			)
		);

		register_rest_route(
			self::REST_NAMESPACE,
			'/menus/(?P<source>[a-z][a-z0-9_]*)/(?P<id>\d+)',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_menu' ),
					'permission_callback' => array( $this, 'permission_check' ),
				),
			)
		);

		register_rest_route(
			self::REST_NAMESPACE,
			'/menus/(?P<source>[a-z][a-z0-9_]*)/(?P<id>\d+)/items/(?P<item>\d+)/extras',
			array(
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'update_extras' ),
					'permission_callback' => array( $this, 'permission_check' ),
					'args'                => array(
						'extras' => array(
							'type'     => 'object',
							'required' => true,
						),
					),
				),
			)
		);
	}

	/**
	 * Lists every available menu.
	 *
	 * @return \WP_REST_Response
	 */
	public function get_menus() {
		$registry = $this->plugin()->menu_sources();
		$sources  = array();

		foreach ( $registry->ids() as $source_id ) {
			$source = $registry->get( $source_id );

			if ( $source ) {
				$sources[] = array(
					'id'    => $source->id(),
					'label' => $source->label(),
				);
			}
		}

		return rest_ensure_response(
			array(
				'sources' => $sources,
				'menus'   => $registry->list_menus(),
			)
		);
	}

	/**
	 * Returns the normalized tree for one menu.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function get_menu( \WP_REST_Request $request ) {
		$tree = $this->plugin()->menu_sources()->get_tree(
			(string) $request->get_param( 'source' ),
			(int) $request->get_param( 'id' )
		);

		if ( ! $tree instanceof Menu_Tree ) {
			return new \WP_Error(
				'menuverse_menu_not_found',
				__( 'That menu could not be found.', 'menuverse' ),
				array( 'status' => 404 )
			);
		}

		return rest_ensure_response( $tree->to_array() );
	}

	/**
	 * Writes MenuVerse extras onto a classic menu item.
	 *
	 * @param \WP_REST_Request $request Request object.
	 * @return \WP_REST_Response|\WP_Error
	 */
	public function update_extras( \WP_REST_Request $request ) {
		$source_id = (string) $request->get_param( 'source' );
		$item_id   = (int) $request->get_param( 'item' );

		if ( 'classic' !== $source_id ) {
			return new \WP_Error(
				'menuverse_unsupported_source',
				__( 'Per item data for this menu source is stored in the block itself.', 'menuverse' ),
				array( 'status' => 400 )
			);
		}

		$item = get_post( $item_id );

		if ( ! $item instanceof \WP_Post || 'nav_menu_item' !== $item->post_type ) {
			return new \WP_Error(
				'menuverse_item_not_found',
				__( 'That menu item could not be found.', 'menuverse' ),
				array( 'status' => 404 )
			);
		}

		$extras = Item_Extras::update( $item_id, (array) $request->get_param( 'extras' ) );

		$this->plugin()->menu_sources()->invalidate( (int) $request->get_param( 'id' ) );

		return rest_ensure_response(
			array(
				'item'   => $item_id,
				'extras' => $extras,
			)
		);
	}
}
