<?php

/**
 * Controller which provides REST endpoint for registered icons.
 *
 * @package gutenberg
 */

if ( ! class_exists( 'WP_REST_Icons_Controller' ) ) {
	class WP_REST_Icons_Controller extends WP_REST_Controller {
		/**
		 * Constructs the controller.
		 */
		public function __construct() {
			$this->namespace = 'wp/v2';
			$this->rest_base = 'icons';
		}

		/**
		 * Registers the routes for the objects of the controller.
		 */
		public function register_routes() {
			register_rest_route(
				$this->namespace,
				'/' . $this->rest_base,
				array(
					array(
						'methods'             => WP_REST_Server::READABLE,
						'callback'            => array( $this, 'get_items' ),
						'permission_callback' => array( $this, 'get_items_permissions_check' ),
						'args'                => $this->get_collection_params(),
					),
					'schema' => array( $this, 'get_public_item_schema' ),
				)
			);

			register_rest_route(
				$this->namespace,
				'/' . $this->rest_base . '/(?P<name>[a-z][a-z0-9-]*/[a-z][a-z0-9-]*)',
				array(
					'args'   => array(
						'name' => array(
							'description' => __( 'Icon name.', 'gutenberg' ),
							'type'        => 'string',
						),
					),
					array(
						'methods'             => WP_REST_Server::READABLE,
						'callback'            => array( $this, 'get_item' ),
						'permission_callback' => array( $this, 'get_item_permissions_check' ),
						'args'                => array(
							'context' => $this->get_context_param( array( 'default' => 'view' ) ),
						),
					),
					'schema' => array( $this, 'get_public_item_schema' ),
				)
			);
		}

		/**
		 * Checks whether a given request has permission to read icons.
		 *
		 * @param WP_REST_Request $_request Full details about the request.
		 * @return true|WP_Error True if the request has read access, WP_Error object otherwise.
		 */
		public function get_items_permissions_check(
			// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
			$request
		) {
			if ( current_user_can( 'edit_posts' ) ) {
				return true;
			}

			foreach ( get_post_types( array( 'show_in_rest' => true ), 'objects' ) as $post_type ) {
				if ( current_user_can( $post_type->cap->edit_posts ) ) {
					return true;
				}
			}

			return new WP_Error(
				'rest_cannot_view',
				__( 'Sorry, you are not allowed to view the registered icons.', 'gutenberg' ),
				array( 'status' => rest_authorization_required_code() )
			);
		}

		/**
		 * Checks if a given request has access to read a specific icon.
		 *
		 * @param WP_REST_Request $request Full details about the request.
		 * @return true|WP_Error True if the request has read access for the item, WP_Error object otherwise.
		 */
		public function get_item_permissions_check( $request ) {
			$check = $this->get_items_permissions_check( $request );
			if ( is_wp_error( $check ) ) {
				return $check;
			}

			return true;
		}

		/**
		 * Retrieves all icons.
		 *
		 * @param WP_REST_Request $request Full details about the request.
		 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
		 */
		public function get_items( $request ) {
			$response = array();
			$search   = $request->get_param( 'search' );
			$icons    = WP_Icons_Registry::get_instance()->get_registered_icons( $search );
			foreach ( $icons as $icon ) {
				$prepared_icon = $this->prepare_item_for_response( $icon, $request );
				$response[]    = $this->prepare_response_for_collection( $prepared_icon );
			}
			return rest_ensure_response( $response );
		}

		/**
		 * Retrieves a specific icon.
		 *
		 * @param WP_REST_Request $request Full details about the request.
		 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
		 */
		public function get_item( $request ) {
			$icon = $this->get_icon( $request['name'] );
			if ( is_wp_error( $icon ) ) {
				return $icon;
			}

			$data = $this->prepare_item_for_response( $icon, $request );
			return rest_ensure_response( $data );
		}

		/**
		 * Retrieves a specific icon from the registry.
		 *
		 * @param string $name Icon name.
		 * @return array|WP_Error Icon data on success, or WP_Error object on failure.
		 */
		public function get_icon( $name ) {
			$registry = WP_Icons_Registry::get_instance();
			$icon     = $registry->get_registered_icon( $name );

			if ( null === $icon ) {
				return new WP_Error(
					'rest_icon_not_found',
					sprintf(
						// translators: %s is the name of any user-provided name
						__( 'Icon not found: "%s".', 'gutenberg' ),
						$name
					),
					array( 'status' => 404 )
				);
			}

			return $icon;
		}

		/**
		 * Prepare a raw icon before it gets output in a REST API response.
		 *
		 * @param array           $item    Raw icon as registered, before any changes.
		 * @param WP_REST_Request $request Request object.
		 * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
		 */
		public function prepare_item_for_response( $item, $request ) {
			$fields = $this->get_fields_for_response( $request );
			$keys   = array(
				'name'    => 'name',
				'label'   => 'label',
				'content' => 'content',
			);
			$data   = array();
			foreach ( $keys as $item_key => $rest_key ) {
				if ( isset( $item[ $item_key ] ) && rest_is_field_included( $rest_key, $fields ) ) {
					$data[ $rest_key ] = $item[ $item_key ];
				}
			}

			$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
			$data    = $this->add_additional_fields_to_object( $data, $request );
			$data    = $this->filter_response_by_context( $data, $context );
			return rest_ensure_response( $data );
		}

		/**
		 * Retrieves the icon schema, conforming to JSON Schema.
		 *
		 * @return array Item schema data.
		 */
		public function get_item_schema() {
			if ( $this->schema ) {
				return $this->add_additional_fields_schema( $this->schema );
			}

			$schema = array(
				'$schema'    => 'http://json-schema.org/draft-04/schema#',
				'title'      => 'icon',
				'type'       => 'object',
				'properties' => array(
					'name'    => array(
						'description' => __( 'The icon name.', 'gutenberg' ),
						'type'        => 'string',
						'readonly'    => true,
						'context'     => array( 'view', 'edit', 'embed' ),
					),
					'label'   => array(
						'description' => __( 'The icon label.', 'gutenberg' ),
						'type'        => 'string',
						'readonly'    => true,
						'context'     => array( 'view', 'edit', 'embed' ),
					),
					'content' => array(
						'description' => __( 'The icon content (SVG markup).', 'gutenberg' ),
						'type'        => 'string',
						'readonly'    => true,
						'context'     => array( 'view', 'edit', 'embed' ),
					),
				),
			);

			$this->schema = $schema;

			return $this->add_additional_fields_schema( $this->schema );
		}

		/**
		 * Retrieves the query params for the icons collection.
		 *
		 * @return array Collection parameters.
		 */
		public function get_collection_params() {
			$query_params                       = parent::get_collection_params();
			$query_params['context']['default'] = 'view';
			return $query_params;
		}
	}
}
