<?php
/**
 * Reactions endpoints.
 *
 * @package Nisje
 */

namespace Dekode\Nisje\Components\Rest;

defined( 'ABSPATH' ) || die( 'Shame on you' );

// phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery

/**
 * Reactions Rest Class.
 */
class Reactions_Controller extends \WP_REST_Controller {

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->namespace = nisje_get_rest_namespace();
		$this->rest_base = 'reactions';
		$this->hook_base = strtolower( $this->rest_base );
	}

	/**
	 * Register the plugin routes.
	 */
	public function register_routes() {
		register_rest_route( $this->namespace, '/' . $this->rest_base, [
			[
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => [ $this, 'get_items' ],
				'permission_callback' => [ $this, 'get_items_permissions_check' ],
				'args'                => $this->get_collection_params(),
			],
			'schema' => [ $this, 'get_public_item_schema' ],
		] );
	}

	/**
	 * Check if a given request has access to reactions.
	 *
	 * @param WP_REST_Request $request Full data about the request.
	 * @return WP_Error|bool
	 */
	public function get_items_permissions_check( $request ) {
		$auth = nisje_validate_rest_authentication( $this->hook_base, 'get_items' );
		if ( is_wp_error( $auth ) ) {
			return $auth;
		}

		if ( 'edit' === $request['context'] ) {
			return new \WP_Error( 'nisje_rest_forbidden_context', esc_html__( 'Sorry, you cannot view this resource with edit context.', 'nisje' ), [
				'status' => rest_authorization_required_code(),
			] );
		}

		return true;
	}

	/**
	 * Get a collection of reactors.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return WP_Error|WP_REST_Response
	 */
	public function get_items( $request ) {
		global $wpdb;

		// Retrieve the list of registered collection query parameters.
		$registered = $this->get_collection_params();

		$parameter_mappings = [
			'page'     => 'page',
			'per_page' => 'per_page',
		];

		$per_page         = $request['per_page'] ?? 10;
		$page             = $request['page'] ?? 1;
		$object_id        = $request['object_id'];
		$object_type      = $request['object_type'];
		$reaction_type_id = $request['reaction_type_id'];

		$retval = [];
		$offset = ( (int) $page - 1 ) * $per_page;

		if ( $reaction_type_id ) {
			$total_reactions = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(user_id) FROM {$wpdb->nisje_reactions} WHERE object_id = %d AND object_type = %s AND reaction_type_id = %s", $object_id, $object_type, $reaction_type_id ) );
			$reactors        = $wpdb->get_results( $wpdb->prepare( "SELECT user_id FROM {$wpdb->nisje_reactions} WHERE object_id = %d AND object_type = %s AND reaction_type_id = %s ORDER BY date_created DESC LIMIT %d, %d", $object_id, $object_type, $reaction_type_id, $offset, $per_page ) );
		} else {
			$total_reactions = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(user_id) FROM {$wpdb->nisje_reactions} WHERE object_id = %d AND object_type = %s", $object_id, $object_type ) );
			$reactors        = $wpdb->get_results( $wpdb->prepare( "SELECT user_id FROM {$wpdb->nisje_reactions} WHERE object_id = %d AND object_type = %s ORDER BY date_created DESC LIMIT %d, %d", $object_id, $object_type, $offset, $per_page ) );
		}

		foreach ( (array) $reactors as $reactor ) {
			$data     = $this->prepare_item_for_response( $reactor->user_id, $request );
			$retval[] = $this->prepare_response_for_collection( $data );
		}

		$page      = (int) $page;
		$max_pages = ceil( (int) $total_reactions / (int) $per_page );

		$response = rest_ensure_response( $retval );
		$response->header( 'X-WP-Total', (int) $total_reactions );
		$response->header( 'X-WP-TotalPages', (int) $max_pages );

		$request_params = $request->get_query_params();

		$base = add_query_arg( $request_params, rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );

		if ( $page > 1 ) {
			$prev_page = $page - 1;
			if ( $prev_page > $max_pages ) {
				$prev_page = $max_pages;
			}
			$prev_link = add_query_arg( 'page', $prev_page, $base );
			$response->link_header( 'prev', $prev_link );
		}
		if ( $max_pages > $page ) {
			$next_page = $page + 1;
			$next_link = add_query_arg( 'page', $next_page, $base );
			$response->link_header( 'next', $next_link );
		}

		return $response;
	}

	/**
	 * Prepares a single member output for response.
	 *
	 * @param int             $user_id User id.
	 * @param WP_REST_Request $request Rest Request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function prepare_item_for_response( $user_id, $request ) {
		$schema = $this->get_item_schema();

		$user = get_userdata( $user_id );
		$data = [];

		if ( ! empty( $schema['properties']['name'] ) ) {
			$data['name'] = $user->display_name;
		}

		if ( ! empty( $schema['properties']['id'] ) ) {
			$data['id'] = (int) $user_id;
		}

		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';

		$data = $this->add_additional_fields_to_object( $data, $request );
		$data = $this->filter_response_by_context( $data, $context );

		$response = rest_ensure_response( $data );

		/**
		 * Filter an user value returned from the API.
		 */
		return apply_filters( "nisje_rest_prepare_{$this->hook_base}_value", $response, $user_id, $request );
	}

	/**
	 * Get the plugin schema, conforming to JSON Schema.
	 *
	 * @return array
	 */
	public function get_item_schema() {
		$schema = [
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => $this->hook_base,
			'type'       => 'object',
			'properties' => [
				'name' => [
					'context'     => [ 'view' ],
					'description' => esc_html__( 'User display name.', 'nisje' ),
					'type'        => 'string',
				],
				'id'   => [
					'context'     => [ 'view' ],
					'description' => esc_html__( 'User id.', 'nisje' ),
					'type'        => 'int',
				],
			],
		];

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

	/**
	 * Get the query params for collections of plugins.
	 *
	 * @return array
	 */
	public function get_collection_params() {
		$params = parent::get_collection_params();

		$params['context']['default'] = 'view';

		$params['object_id'] = [
			'description' => esc_html__( 'Reaction object id.', 'nisje' ),
			'type'        => 'integer',
			'default'     => 0,
			'required'    => true,
		];

		$params['object_type'] = [
			'description' => esc_html__( 'Reaction object type.', 'nisje' ),
			'type'        => 'string',
			'default'     => 'post',
			'enum'        => [ 'post', 'comment', 'activity' ],
		];

		$params['reaction_type_id'] = [
			'description' => esc_html__( 'Reaction type.', 'nisje' ),
			'type'        => 'integer',
			'default'     => 0,
		];

		return apply_filters( "nisje_rest_{$this->hook_base}_collection_params", $params );
	}
}
