<?php
/**
 * Support block editor (Gutenberg).
 *
 * @package WP_Revisions_Control
 */

namespace WP_Revisions_Control;

use WP_REST_Response;
use WP_REST_Request;
use WP_Revisions_Control;

/**
 * Class Block_Editor.
 */
class Block_Editor {
	use Singleton;

	/**
	 * Name of action used to clean up post's revisions via cron.
	 *
	 * @var string
	 */
	private $cron_action = 'wp_revisions_control_cron_purge';

	/**
	 * Prepare class.
	 *
	 * This is called at `init`, so cannot use that hook unless priority is
	 * greater than 10.
	 */
	private function setup() {
		add_action( 'rest_api_init', array( $this, 'action_rest_api_init' ) );
		add_filter( 'is_protected_meta', array( $this, 'filter_is_protected_meta' ), 10, 2 );
		add_action( $this->cron_action, array( WP_Revisions_Control::get_instance(), 'do_purge_excess' ), 10, 2 );
		add_action( 'enqueue_block_editor_assets', array( $this, 'action_enqueue_block_editor_assets' ) );
	}

	/**
	 * Register REST API components for Gutenberg UI.
	 */
	public function action_rest_api_init() {
		if ( ! function_exists( 'register_rest_route' ) ) {
			return;
		}

		foreach ( array_keys( WP_Revisions_Control::get_instance()->get_post_types() ) as $post_type ) {
			register_meta(
				'post',
				WP_REVISIONS_CONTROL_LIMIT_META_KEY,
				array(
					'object_subtype' => $post_type,
					'type'           => 'string', // Can be empty, so must be string.
					'default'        => '',
					'single'         => true,
					'show_in_rest'   => true,
					'description'    => __(
						'Number of revisions to retain.',
						'wp-revisions-control'
					),
				)
			);
		}

		register_rest_route(
			'wp-revisions-control/v1',
			'schedule/(?P<id>[\d]+)/(?P<limit_override>[\d]+)',
			array(
				'methods'             => 'PUT',
				'callback'            => array( $this, 'rest_api_schedule_purge' ),
				'permission_callback' => array( $this, 'rest_api_permission_callback' ),
				'args'                => array(
					'id'             => array(
						'required'          => true,
						'type'              => 'integer',
						'validate_callback' => array( $this, 'rest_api_validate_id' ),
					),
					'limit_override' => array(
						'required'          => false,
						'type'              => 'integer',
						'default'           => null,
						'validate_callback' => array( $this, 'rest_api_validate_id' ),
					),
				),
				'show_in_index'       => false,
			)
		);
	}

	/**
	 * Permissions callback for REST endpoint.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return bool
	 */
	public function rest_api_permission_callback( $request ) {
		return current_user_can(
			'edit_post',
			$request->get_param( 'id' )
		);
	}

	/**
	 * Validate post ID.
	 *
	 * @param int $input Post ID.
	 * @return bool
	 */
	public function rest_api_validate_id( $input ) {
		return is_numeric( $input );
	}

	/**
	 * Schedule cleanup of post's excess revisions.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_REST_Response
	 */
	public function rest_api_schedule_purge( $request ) {
		$result = wp_schedule_single_event(
			time() + 3,
			$this->cron_action,
			array(
				$request->get_param( 'id' ),
				$request->get_param( 'limit_override' ),
			)
		);

		return rest_ensure_response( $result );
	}

	/**
	 * Allow our meta to be edited from Gutenberg.
	 *
	 * @param bool   $protected If meta is protected.
	 * @param string $meta_key  Meta key being checked.
	 * @return false
	 */
	public function filter_is_protected_meta( $protected, $meta_key ) {
		if ( WP_REVISIONS_CONTROL_LIMIT_META_KEY === $meta_key ) {
			return false;
		}

		return $protected;
	}

	/**
	 * Register Gutenberg script.
	 */
	public function action_enqueue_block_editor_assets() {
		global $pagenow;

		if ( 'widgets.php' === $pagenow ) {
			return;
		}

		$handle     = 'wp-revisions-control-block-editor';
		$asset_data = require_once dirname( __DIR__ ) . '/assets/build/gutenberg.asset.php';

		wp_enqueue_script(
			$handle,
			plugins_url(
				'assets/build/gutenberg.js',
				__DIR__
			),
			$asset_data['dependencies'],
			$asset_data['version']
		);

		wp_localize_script(
			$handle,
			'wpRevisionsControlBlockEditorSettings',
			array(
				'metaKey' => WP_REVISIONS_CONTROL_LIMIT_META_KEY,
			)
		);
	}
}