<?php
/**
 * REST Controller for Webhooks.
 *
 * @package  LifterLMS_REST/Classes
 *
 * @since 1.0.0-beta.3
 * @version 1.0.0-beta.27
 */

defined( 'ABSPATH' ) || exit;

/**
 * LLMS_REST_Webhooks_Controller class.
 *
 * @since 1.0.0-beta.3
 */
class LLMS_REST_Webhooks_Controller extends LLMS_REST_Controller {

	/**
	 * Route base.
	 *
	 * @var string
	 */
	protected $rest_base = 'webhooks';

	/**
	 * Schema properties available for ordering the collection.
	 *
	 * @var string[]
	 */
	protected $orderby_properties = array(
		'id',
		'name',
		'created',
		'updated',
	);

	/**
	 * Check if the authenticated user can perform the request action.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @return boolean
	 */
	protected function check_permissions() {
		return current_user_can( 'manage_lifterlms_webhooks' ) ? true : llms_rest_authorization_required_error();
	}

	/**
	 * Check if a given request has access to create an item.
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */
	public function create_item_permissions_check( $request ) {
		return $this->check_permissions();
	}

	/**
	 * Insert the prepared data into the database.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @param array           $prepared Prepared item data.
	 * @param WP_REST_Request $request  Request object.
	 * @return obj Object Instance of object from $this->get_object().
	 */
	protected function create_object( $prepared, $request ) {

		return LLMS_REST_API()->webhooks()->create( $prepared );

	}

	/**
	 * Check if a given request has access to delete an item.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return bool|WP_Error
	 */
	public function delete_item_permissions_check( $request ) {
		return $this->check_permissions();
	}

	/**
	 * Delete the object.
	 *
	 * Note: we do not return 404s when the resource to delete cannot be found. We assume it's already been deleted and respond with 204.
	 * Errors returned by this method should be any error other than a 404!
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @param obj             $object  Instance of the object from $this->get_object().
	 * @param WP_REST_Request $request Request object.
	 * @return true|WP_Error true when the object is removed, WP_Error on failure.
	 */
	protected function delete_object( $object, $request ) {

		return $object->delete();

	}

	/**
	 * Retrieves the query params for the objects collection.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @return array Collection parameters.
	 */
	public function get_collection_params() {

		$params = parent::get_collection_params();

		$params['status'] = array(
			'description' => __( 'Include only webhooks matching a specific status.', 'lifterlms' ),
			'type'        => 'string',
			'enum'        => array_keys( LLMS_REST_API()->webhooks()->get_statuses() ),
		);

		return $params;

	}

	/**
	 * Get the Webhook's schema, conforming to JSON Schema.
	 *
	 * @since 1.0.0-beta.27
	 *
	 * @return array
	 */
	protected function get_item_schema_base() {

		return array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'webhook',
			'type'       => 'object',
			'properties' => array(
				'id'           => array(
					'description' => __( 'Webhook ID.', 'lifterlms' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'name'         => array(
					'description' => __( 'Friendly, human-readable name or description.', 'lifterlms' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'arg_options' => array(
						'sanitize_callback' => 'sanitize_text_field',
					),
				),
				'status'       => array(
					'description' => __( 'The status of the webhook.', 'lifterlms' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'enum'        => array_keys( LLMS_REST_API()->webhooks()->get_statuses() ),
					'default'     => 'disabled',
				),
				'topic'        => array(
					'description' => __( 'The webhook topic', 'lifterlms' ),
					'type'        => 'string',
					'required'    => true,
					'context'     => array( 'view', 'edit' ),
					'arg_options' => array(
						'validate_callback' => array( LLMS_REST_API()->webhooks(), 'is_topic_valid' ),
					),
				),
				'delivery_url' => array(
					'description' => __( 'The webhook payload delivery URL.', 'lifterlms' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'required'    => true,
				),
				'secret'       => array(
					'description' => __( 'An optional secret key used to generate the delivery signature.', 'lifterlms' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
				),
				'created'      => array(
					'description' => __( 'Creation date. Format: Y-m-d H:i:s', 'lifterlms' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'updated'      => array(
					'description' => __( 'Date last modified. Format: Y-m-d H:i:s', 'lifterlms' ),
					'type'        => 'string',
					'format'      => 'date-time',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'resource'     => array(
					'description' => __( 'The parsed resource from the `topic`.', 'lifterlms' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'event'        => array(
					'description' => __( 'The parsed event from the `topic`.', 'lifterlms' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'hooks'        => array(
					'description' => __( 'List of WordPress action hook associated with the webhook.', 'lifterlms' ),
					'type'        => 'array',
					'items'       => array(
						'type' => 'string',
					),
					'context'     => array( 'view', 'edit' ),
				),
			),
		);

	}

	/**
	 * Check if a given request has access to read an item.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */
	public function get_item_permissions_check( $request ) {
		return $this->check_permissions();
	}

	/**
	 * Check if a given request has access to read items.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */
	public function get_items_permissions_check( $request ) {
		return $this->check_permissions();
	}

	/**
	 * Retrieve pagination information from an objects query.
	 *
	 * @since 1.0.0-beta.3
	 * @since 1.0.0-beta-24 Fixed access of protected LLMS_Abstract_Query properties.
	 *
	 * @param LLMS_REST_Webhooks_Query $query    Objects query result returned by {@see LLMS_REST_Webhooks_Controller::get_objects_query()}.
	 * @param array                    $prepared Array of collection arguments.
	 * @param WP_REST_Request          $request  Request object.
	 * @return array {
	 *     Array of pagination information.
	 *
	 *     @type int $current_page  Current page number.
	 *     @type int $total_results Total number of results.
	 *     @type int $total_pages   Total number of results pages.
	 * }
	 */
	protected function get_pagination_data_from_query( $query, $prepared, $request ) {

		return array(
			'current_page'  => $query->get( 'page' ),
			'total_results' => $query->get_found_results(),
			'total_pages'   => $query->get_max_pages(),
		);

	}

	/**
	 * Retrieve An Webhook object by ID.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @param int  $id      Webhook ID.
	 * @param bool $hydrate If true, pulls all key data from the database on instantiation.
	 * @return WP_Error|LLMS_REST_API_Key
	 */
	protected function get_object( $id, $hydrate = true ) {

		if ( ! is_numeric( $id ) ) {
			$id = $this->get_object_id( $id );
		}

		$key = LLMS_REST_API()->webhooks()->get( $id, $hydrate );
		return $key ? $key : llms_rest_not_found_error();

	}

	/**
	 * Retrieve a query object based on arguments from a `get_items()` (collection) request.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @param array           $prepared Array of collection arguments.
	 * @param WP_REST_Request $request  Request object.
	 * @return LLMS_REST_Webhooks_Query
	 */
	protected function get_objects_query( $prepared, $request ) {

		return new LLMS_REST_Webhooks_Query( $prepared );

	}

	/**
	 * Retrieve an array of objects from the result of $this->get_objects_query().
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @param obj $query Objects query result.
	 * @return obj[]
	 */
	protected function get_objects_from_query( $query ) {

		return $query->get_results();

	}

	/**
	 * Map request keys to database keys for insertion.
	 *
	 * Array keys are the request fields (as defined in the schema) and
	 * array values are the database fields.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @return array
	 */
	protected function map_schema_to_database() {

		$map = parent::map_schema_to_database();

		// Not inserted/read via database calls.
		unset( $map['resource'], $map['event'], $map['hooks'] );

		return $map;

	}

	/**
	 * Prepare an object for response.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @param LLMS_Abstract_User_Data $object  User object.
	 * @param WP_REST_Request         $request Request object.
	 * @return array
	 */
	protected function prepare_object_for_response( $object, $request ) {

		$prepared = parent::prepare_object_for_response( $object, $request );

		$prepared['id']       = absint( $prepared['id'] );
		$prepared['resource'] = $object->get_resource();
		$prepared['event']    = $object->get_event();
		$prepared['hooks']    = $object->get_hooks();
		$prepared['created']  = mysql_to_rfc3339( $prepared['created'] );
		$prepared['updated']  = mysql_to_rfc3339( $prepared['updated'] );

		return $prepared;

	}

	/**
	 * Update a Webhook.
	 *
	 * @since 1.0.0-beta.3
	 * @since 1.0.0-beta.27 Handle custom rest fields registered via `register_rest_field()`.
	 *
	 * @param WP_REST_Request $request Request object.
	 * @return WP_Error|WP_REST_Response
	 */
	public function update_item( $request ) {

		$prepared = $this->prepare_item_for_database( $request );
		// Exclude additional fields registered via `register_rest_field()`.
		$prepared = array_diff_key( $prepared, $this->get_additional_fields() );
		$webhook  = LLMS_REST_API()->webhooks()->update( $prepared );
		if ( is_wp_error( $request ) ) {
			$request->add_data( array( 'status' => 400 ) );
			return $request;
		}

		// Fields registered via `register_rest_field()`.
		$fields_update = $this->update_additional_fields_for_object( $webhook, $request );
		if ( is_wp_error( $fields_update ) ) {
			return $fields_update;
		}

		$request->set_param( 'context', 'edit' );

		$response = $this->prepare_item_for_response( $webhook, $request );

		return $response;

	}

	/**
	 * Check if a given request has access to update an item.
	 *
	 * @since 1.0.0-beta.3
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */
	public function update_item_permissions_check( $request ) {
		return $this->check_permissions();
	}

}
