<?php
/**
 * REST API Endpoints
 *
 * @category API
 * @package  My Calendar Pro
 * @author   Joe Dolson
 * @license  GPLv3
 * @link     https://www.joedolson.com/my-calendar-pro/
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

require_once __DIR__ . '/classes/class-my-calendar-api-route.php';

// Resources: Events, Categories, Locations.
// URL formats: v1/events/add, v1/events/update/ID, v1/events/delete/ID.
// No getters at this time; retain original getter API.

/**
 * Basic authentication. Default false, filter to add custom authentication methods.
 *
 * Notes for authentication:
 *  - Authentication key sent from originating site
 *  - Authentication key matched at remote site
 *  - Standard User permissions manage whether API is executed at originating site
 *  - Valid auth key must be present to do anything at remote site
 *  - Need to know whether API is executing to filter permissions on event editing.
 *
 * @param object $request REST API request.
 *
 * @return boolean
 */
function mcs_api_authenticate( $request ) {
	$sources = mcs_api_sources();
	// referer can be found in headers > user_agent.
	// should match request > source.
	$authenticated = false;
	$params        = $request->get_params();
	$referer       = explode( ';', $request->get_header( 'user_agent' ) );
	$referer       = trim( $referer[1] );
	$source        = $params['source'];

	// If the referer value doesn't match the sent value, don't authenticate.
	if ( $referer !== $source ) {
		return false;
	}

	$auth_key = ( isset( $params['mc_api_auth_key'] ) ) ? $params['mc_api_auth_key'] : false;
	$salt     = wp_salt();
	$hashed   = crypt( mcs_api_server_authentication_key(), $salt );
	if ( $auth_key && hash_equals( $hashed, crypt( $auth_key, $salt ) ) ) {
		$authenticated = true;
	}

	return apply_filters( 'mcs_api_authenticated', $authenticated, $request );
}

/**
 * Gets local authentication key.
 *
 * @return string
 */
function mcs_api_server_authentication_key() {
	$settings   = ( is_array( get_option( 'mc_api_server_settings' ) ) ) ? get_option( 'mc_api_server_settings' ) : array();
	$settings   = array_merge(
		array(
			'mc_api_key'       => '',
			'mc_api_whitelist' => array(),
		),
		$settings
	);
	$mc_api_key = $settings['mc_api_key'];

	return apply_filters( 'mc_api_authentication_key', $mc_api_key );
}

/**
 * Get list of allowed API sources.
 *
 * These are the sites allowed to submit posts to the current site. Default none, white list specific sources.
 *
 * Usage: return true to generally allow; otherwise, do verification postback to URL using public key
 *
 * @return array
 */
function mcs_api_sources() {
	$settings  = ( is_array( get_option( 'mc_api_server_settings' ) ) ) ? get_option( 'mc_api_server_settings' ) : array();
	$settings  = array_merge(
		array(
			'mc_api_key'       => '',
			'mc_api_whitelist' => array(),
		),
		$settings
	);
	$whitelist = isset( $settings['mc_api_whitelist'] ) ? $settings['mc_api_whitelist'] : array();

	return apply_filters( 'my_calendar_api_sources', $whitelist );
}

/**
 * Check whether current server has an event with this ID. If yes, update it.
 * If not, return false.
 *
 * @param array $params Request submitted parameters.
 *
 * @return string
 */
function mcs_api_check_requested_id( $params ) {
	$event_id  = $params['id'];
	$source    = $params['source'];
	$api_event = md5( $event_id . $source );

	$events = get_posts(
		array(
			'post_type'  => 'mc-events',
			'meta_key'   => '_mc_api_event',
			'meta_value' => $api_event,
		)
	);
	if ( empty( $events ) ) {
		$return = false;
	} else {
		$post_id = $events[0]->ID;
		$return  = get_post_meta( $post_id, '_mc_event_id', true );
	}

	return $return;
}

/**
 * Handle save and generate response
 *
 * @param object $request Request object from REST.
 * @param array  $check Checked data from My Calendar.
 * @param int    $original_id Original event ID.
 * @param string $action What action is being ordered.
 * @param int    $event_id Event ID if exists.
 *
 * @return array server response.
 */
function mcs_api_create_item( $request, $check, $original_id, $action = 'add', $event_id = false ) {
	// Keeping this will result in double posting.
	remove_action( 'mc_save_event', 'mcs_api_post', 10, 4 );
	if ( $check[0] ) {
		// Categories: if category doesn't exist, create new category.
		// Get category data & remove from checkset.
		$categories = $check['category'];
		unset( $check['category'] );
		$cat_submit = array();

		foreach ( $categories as $category ) {
			// see whether current site has matching category.
			$cat_id = mc_category_by_name( $category['category_name'] );
			if ( $cat_id ) {
				$check[2]['event_category'] = $cat_id;
			} else {
				// insert new category (function new in 3.0.0).
				$cat_id                     = mc_create_category( $category );
				$check[2]['event_category'] = $cat_id;
			}
			$cat_submit[] = $cat_id;
		}
		$check[2]['event_categories'] = $cat_submit;

		if ( 'edit' === $action ) {
			$response = my_calendar_save( $action, $check, $event_id );
		} else {
			$response = my_calendar_save( $action, $check );
		}
		$event_id = isset( $response['event_id'] ) ? $response['event_id'] : $event_id;
		$e        = mc_get_first_event( $event_id );
		$params   = $request->get_params();
		$source   = $params['source'];

		if ( is_object( $e ) ) {
			$post_id   = $e->event_post;
			$api_event = md5( $original_id . $source );

			update_post_meta( $post_id, '_mc_api_event', $api_event );
			update_post_meta( $post_id, '_mc_api_source', $source );

			do_action( 'mcs_api_clear_cache' );
		} else {
			if ( true === MC_API_SERVER_DEBUG ) {
				mc_debug( 'Event is not an object', $e );
			}
		}

		$event_id = $response['event_id'];
		if ( isset( $event['event_image'] ) && _mc_is_url( $event['event_image'] ) ) {
			$image = media_sideload_image( $event['event_image'], $post_id, null, 'id' );

			set_post_thumbnail( $post_id, $image );
		}
		if ( isset( $event['UID'] ) ) {
			$uid = apply_filters( 'mcs_set_alternate_guid', $event['UID'], $event );
			update_post_meta( $post_id, '_mc_guid', $uid );
		}
	} else {
		$response = array(
			'event_id' => false,
			'message'  => 'Verification of passed data failed. Cannot process this request.',
		);
	}

	return array_map( 'htmlentities', $response );
}

/**
 * Update a post.
 *
 * @param object $request REST Request.
 * @param object $check Item being updated.
 * @param int    $original_id Original ID.
 * @param string $action Action performed.
 * @param int    $event_id Current event ID.
 *
 * @uses mcs_api_create_item
 *
 * @return array
 */
function mcs_api_update_item( $request, $check, $original_id, $action = 'edit', $event_id = false ) {

	return mcs_api_create_item( $request, $check, $original_id, $action, $event_id );
}

/**
 * Delete an event.
 *
 * @param object $request REST request.
 *
 * @return int Deleted event.
 */
function mcs_api_delete_item( $request ) {
	$delete_id = mc_delete_event( $request );

	return $delete_id;
}

/**
 * Register My Calendar route.
 */
function mcs_api_register_custom_routes() {
	$controller = new My_Calendar_API_Route();
	$controller->register_routes();
}
add_action( 'rest_api_init', 'mcs_api_register_custom_routes' );
