<?php
/**
 * Grades Controller class.
 *
 * @since 2.5.20
 *
 * @package Masteriyo\Addons\Gradebook\Controllers
 */

namespace Masteriyo\Addons\Gradebook\Controllers;

defined( 'ABSPATH' ) || exit;

use Masteriyo\Addons\Gradebook\Enums\GradeResultStatus;
use WP_Query;
use Masteriyo\Helper\Permission;
use Masteriyo\PostType\PostType;
use Masteriyo\Addons\Gradebook\Enums\GradeResultType;
use Masteriyo\Addons\Gradebook\Query\GradeResultQuery;
use Masteriyo\Enums\PostStatus;
use Masteriyo\RestApi\Controllers\Version1\CrudController;
use Masteriyo\RestApi\Controllers\Version1\UsersController;

/**
 * GradeResultsController class.
 */
class GradeResultsController extends CrudController {
	/**
	 * Endpoint namespace.
	 *
	 * @since 2.5.20
	 *
	 * @var string
	 */
	protected $namespace = 'masteriyo/pro/v1';

	/**
	 * Route base.
	 *
	 * @var string
	 */
	protected $rest_base = 'grade-results';

	/**
	 * Object type.
	 *
	 * @var string
	 */
	protected $object_type = 'grade-result';

	/**
	 * If object is hierarchical.
	 *
	 * @var bool
	 */
	protected $hierarchical = false;

	/**
	 * Permission class.
	 *
	 * @since 2.5.20
	 *
	 * @var Masteriyo\Helper\Permission;
	 */
	protected $permission = null;

	/**
	 * Constructor.
	 *
	 * @since 2.5.20
	 *
	 * @param Permission $permission
	 */
	public function __construct( ?Permission $permission = null ) {
		$this->permission = $permission;
	}

	/**
	 * Register routes.
	 *
	 * @since 2.5.20
	 *
	 * @return void
	 */
	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(),
				),
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'create_item' ),
					'permission_callback' => array( $this, 'create_item_permissions_check' ),
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/delete',
			array(
				array(
					'methods'             => \WP_REST_Server::DELETABLE,
					'callback'            => array( $this, 'delete_items' ),
					'permission_callback' => array( $this, 'delete_items_permissions_check' ),
					'args'                => array(
						'ids'      => array(
							'required'    => true,
							'description' => __( 'Grade result IDs.', 'learning-management-system' ),
							'type'        => 'array',
						),
						'force'    => array(
							'default'     => false,
							'description' => __( 'Whether to bypass trash and force deletion.', 'learning-management-system' ),
							'type'        => 'boolean',
						),
						'children' => array(
							'default'     => false,
							'description' => __( 'Whether to delete the children(sections, lessons, quizzes and questions) under the course.', 'learning-management-system' ),
							'type'        => 'boolean',
						),
					),
				),
			)
		);

		register_rest_route(
			$this->namespace,
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
			array(
				'args'   => array(
					'id' => array(
						'description' => __( 'Unique identifier for the resource.', 'learning-management-system' ),
						'type'        => 'integer',
					),
				),
				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',
							)
						),
					),
				),
				array(
					'methods'             => \WP_REST_Server::EDITABLE,
					'callback'            => array( $this, 'update_item' ),
					'permission_callback' => array( $this, 'update_item_permissions_check' ),
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
				),
				array(
					'methods'             => \WP_REST_Server::DELETABLE,
					'callback'            => array( $this, 'delete_item' ),
					'permission_callback' => array( $this, 'delete_item_permissions_check' ),
					'args'                => array(
						'children' => array(
							'default'     => true,
							'description' => __( 'Whether to delete the children.', 'learning-management-system' ),
							'type'        => 'boolean',
						),
					),
				),
				'schema' => array( $this, 'get_public_item_schema' ),
			)
		);

		$users_controllers = new UsersController();
		register_rest_route(
			$users_controllers->namespace,
			'/' . $users_controllers->rest_base . '/me/grade-results',
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_user_grade_results' ),
					'permission_callback' => array( $this, 'get_user_grade_results_permission_check' ),
					'args'                => $this->get_user_grade_results_collection_params(),
				),
			)
		);
	}

	/**
	 * Get the query params for collections of user grade results.
	 *
	 * @since  2.5.20
	 *
	 * @return array
	 */
	public function get_user_grade_results_collection_params() {
		$params = parent::get_collection_params();
		$params = masteriyo_array_only( $params, array( 'page', 'per_page', 'item', 'item_type' ) );

		$params['orderby'] = array(
			'description'       => __( 'Sort collection by object attribute . ', 'learning-management-system' ),
			'type'              => 'string',
			'default'           => 'created_at',
			'enum'              => array(
				'id',
				'created_at',
			),
			'validate_callback' => 'rest_validate_request_arg',
		);

		return $params;
	}


	/**
	 * Return user grade results.
	 *
	 * @since 2.5.20
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_Error|WP_REST_Response
	 */
	public function get_user_grade_results( $request ) {
		$query_args = array(
			'orderby'   => $request['orderby'],
			'page'      => $request['page'],
			'paged'     => $request['page'],
			'limit'     => $request['per_page'],
			'per_page'  => $request['per_page'],
			'user'      => get_current_user_id(),
			'item_type' => isset( $request['item_type'] ) ? $request['item_type'] : GradeResultType::COURSE,
		);

		$query_results = $this->get_objects( $query_args );

		$objects = array();
		foreach ( $query_results['objects'] as $object ) {
			$data      = $this->prepare_object_for_response( $object, $request );
			$objects[] = $this->prepare_response_for_collection( $data );
		}

		/**
		 * Filters objects collection before processing.
		 *
		 * @since 1.0.0
		 *
		 * @param array $objects Objects collection.
		 * @param array $query_vars Query vars.
		 * @param array $query_results Query results.
		 */
		$objects = apply_filters( 'masteriyo_before_process_objects_collection', $objects, $query_args, $query_results );

		if ( is_callable( array( $this, 'process_objects_collection' ) ) ) {
			$objects = $this->process_objects_collection( $objects, $query_args, $query_results );
		}

		/**
		 * Filters objects collection after processing.
		 *
		 * @since 1.0.0
		 *
		 * @param array $objects Objects collection.
		 * @param array $query_vars Query vars.
		 * @param array $query_results Query results.
		 */
		$objects = apply_filters( 'masteriyo_after_process_objects_collection', $objects, $query_args, $query_results );

		$page      = (int) $query_args['paged'];
		$max_pages = $query_results['pages'];

		$response = rest_ensure_response( $objects );
		$response->header( 'X-WP-Total', $query_results['total'] );
		$response->header( 'X-WP-TotalPages', (int) $max_pages );

		$base = $this->rest_base;
		$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $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;
	}

	/**
	 * Check check grade results permission check
	 *
	 * @since 2.5.20
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */
	public function get_user_grade_results_permission_check( $request ) {
		return true;
	}

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

		unset( $params['search'] );

		$params['user'] = array(
			'description'       => __( 'Limit result set to specific user ids.', 'learning-management-system' ),
			'type'              => 'array',
			'items'             => array(
				'type' => 'integer',
			),
			'default'           => array(),
			'sanitize_callback' => 'wp_parse_id_list',
		);

		$params['item'] = array(
			'description'       => __( 'Limit result set to specific item ids.', 'learning-management-system' ),
			'type'              => 'array',
			'items'             => array(
				'type' => 'integer',
			),
			'default'           => array(),
			'sanitize_callback' => 'wp_parse_id_list',
		);

		$params['item_type'] = array(
			'description'       => __( 'Limit result set to specific item types.', 'learning-management-system' ),
			'type'              => 'array',
			'items'             => array(
				'type' => 'string',
			),
			'default'           => GradeResultType::COURSE,
			'enum'              => GradeResultType::all(),
			'sanitize_callback' => 'wp_parse_list',
		);

		$params['orderby'] = array(
			'description'       => __( 'Sort collection by object attribute . ', 'learning-management-system' ),
			'type'              => 'string',
			'default'           => 'created_at',
			'enum'              => array(
				'id',
				'created_at',
			),
			'validate_callback' => 'rest_validate_request_arg',
		);

		return $params;
	}

	/**
	 * Get object.
	 *
	 * @since 2.5.20
	 *
	 * @param  int|Model|WP_Post $object Object ID or Model or WP_Post object.
	 * @return object Model object or WP_Error object.
	 */
	protected function get_object( $object ) {
		try {
			if ( is_int( $object ) ) {
				$id = $object;
			} else {
				$id = $object->get_id();
			}

			$grade_result = masteriyo( 'grade-result' );
			$grade_result->set_id( $id );
			$grade_result_repo = masteriyo( 'grade-result.store' );
			$grade_result_repo->read( $grade_result );
		} catch ( \Exception $e ) {
			return false;
		}

		return $grade_result;
	}

	/**
	 * Get objects.
	 *
	 * @since  1.0.0
	 *
	 * @param  array $query_args Query args.
	 *
	 * @return array
	 */
	protected function get_objects( $query_args ) {
		$query  = new GradeResultQuery( $query_args );
		$result = $query->get_grade_results();

		$total_posts = $query->found_rows;
		if ( $total_posts < 1 ) {
			// Out-of-bounds, run the query again without LIMIT for total count.
			unset( $query_args['paged'] );
			$count_query = new GradeResultQuery( $query_args );
			$count_query->get_grade_results();

			$total_posts = $count_query->found_rows;
		}

		return array(
			'objects' => array_filter( array_map( array( $this, 'get_object' ), $result ) ),
			'total'   => (int) $total_posts,
			'pages'   => (int) ceil( $total_posts / (int) $query_args['per_page'] ),
		);
	}

	/**
	 * Prepares the object for the REST response.
	 *
	 * @since  2.5.20
	 *
	 * @param  Masteriyo\Database\Model $object  Model object.
	 * @param  WP_REST_Request $request Request object.
	 *
	 * @return WP_Error|WP_REST_Response Response object on success, or WP_Error object on failure.
	 */
	protected function prepare_object_for_response( $object, $request ) {
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
		$data    = $this->get_grade_result_data( $object, $context );

		$data     = $this->add_additional_fields_to_object( $data, $request );
		$data     = $this->filter_response_by_context( $data, $context );
		$response = rest_ensure_response( $data );
		$response->add_links( $this->prepare_links( $object, $request ) );

		/**
		 * Filter the data for a response.
		 *
		 * The dynamic portion of the hook name, $this->post_type,
		 * refers to object type being prepared for the response.
		 *
		 * @since 2.5.20
		 *
		 * @param WP_REST_Response $response The response object.
		 * @param Masteriyo\Database\Model $object   Object data.
		 * @param WP_REST_Request  $request  Request object.
		 */
		return apply_filters( "masteriyo_rest_prepare_{$this->object_type}_object", $response, $object, $request );
	}

	/**
	 * Get grade result data.
	 *
	 * @since 2.5.20
	 *
	 * @param \Masteriyo\Addons\Gradebook\Models\GradeResult $grade_result grade instance.
	 * @param string  $context Request context.
	 *                         Options: 'view' and 'edit'.
	 *
	 * @return array
	 */
	protected function get_grade_result_data( $grade_result, $context = 'view' ) {
		$user = masteriyo_get_user( $grade_result->get_user_id( $context ) );
		$user = is_wp_error( $user ) || is_null( $user ) ? null : array(
			'id'           => $user->get_id(),
			'display_name' => $user->get_display_name(),
			'avatar_url'   => $user->profile_image_url(),
			'first_name'   => $user->get_first_name(),
			'last_name'    => $user->get_last_name(),
			'email'        => $user->get_email(),
		);

		$data = array(
			'id'                 => $grade_result->get_id(),
			'user_id'            => $grade_result->get_user_id( $context ),
			'parent_id'          => $grade_result->get_parent_id( $context ),
			'course_id'          => $grade_result->get_course_id( $context ),
			'item_id'            => $grade_result->get_item_id( $context ),
			'item_type'          => $grade_result->get_item_type( $context ),
			'item_name'          => $grade_result->get_item_name( $context ),
			'status'             => $grade_result->get_status( $context ),
			'grade_id'           => $grade_result->get_grade_id( $context ),
			'grade_name'         => $grade_result->get_grade_name( $context ),
			'grade_point'        => masteriyo_trim_zeros( $grade_result->get_grade_point( $context ) ),
			'grade_color'        => $grade_result->get_grade_color( $context ),
			'earned_grade_point' => masteriyo_trim_zeros( $grade_result->get_earned_grade_point( $context ) ),
			'earned_percent'     => masteriyo_trim_zeros( $grade_result->get_earned_percent( $context ) ),
			'created_at'         => masteriyo_rest_prepare_date_response( $grade_result->get_created_at( $context ) ),
			'modified_at'        => masteriyo_rest_prepare_date_response( $grade_result->get_modified_at( $context ) ),
			'children'           => array(),
			'user'               => $user,
			'summary'            => null,
		);

		if ( GradeResultType::COURSE === $grade_result->get_item_type( $context ) ) {
			$children = array_filter(
				$grade_result->get_children(),
				function( $child ) {
					return GradeResultType::COURSE !== $child->get_item_type();
				}
			);

			$data['children'] = array_map(
				function( $child ) use ( $context ) {
					return $this->get_grade_result_children_data( $child, $context );
				},
				$children
			);

			$data['summary'] = $this->get_summary( $grade_result, $context );
		}

		/**
		 * Filter grade rest response data.
		 *
		 * @since 2.5.20
		 *
		 * @param array $data Grade data.
		 * @param \Masteriyo\Addons\Gradebook\Models\GradeResult $grade_result Grade object.
		 * @param string $context What the value is for. Valid values are view and edit.
		 * @param Masteriyo\RestApi\Controllers\Version1\GradeResultsController $controller REST Grades controller object.
		 */
		return apply_filters( "masteriyo_rest_response_{$this->object_type}_data", $data, $grade_result, $context, $this );
	}

	/**
	 * Get grade result data.
	 *
	 * @since 2.5.20
	 *
	 * @param \Masteriyo\Addons\Gradebook\Models\GradeResult $grade_result grade instance.
	 * @param string  $context Request context.
	 *                         Options: 'view' and 'edit'.
	 *
	 * @return array
	 */
	protected function get_grade_result_children_data( $grade_result, $context = 'view' ) {
		$data = array(
			'id'                 => $grade_result->get_id(),
			'user_id'            => $grade_result->get_user_id( $context ),
			'parent_id'          => $grade_result->get_parent_id( $context ),
			'course_id'          => $grade_result->get_course_id( $context ),
			'item_id'            => $grade_result->get_item_id( $context ),
			'item_type'          => $grade_result->get_item_type( $context ),
			'item_name'          => $grade_result->get_item_name( $context ),
			'status'             => $grade_result->get_status( $context ),
			'grade_name'         => $grade_result->get_grade_name( $context ),
			'grade_point'        => masteriyo_trim_zeros( $grade_result->get_grade_point( $context ) ),
			'grade_color'        => $grade_result->get_grade_color( $context ),
			'weight'             => $grade_result->get_weight( $context ),
			'earned_grade_point' => masteriyo_trim_zeros( $grade_result->get_earned_grade_point( $context ) ),
			'earned_percent'     => masteriyo_trim_zeros( $grade_result->get_earned_percent( $context ) ),
			'created_at'         => masteriyo_rest_prepare_date_response( $grade_result->get_created_at( $context ) ),
			'modified_at'        => masteriyo_rest_prepare_date_response( $grade_result->get_modified_at( $context ) ),
		);

		/**
		 * Filter grade rest response data.
		 *
		 * @since 2.5.20
		 *
		 * @param array $data Grade data.
		 * @param \Masteriyo\Addons\Gradebook\Models\GradeResult $grade_result Grade object.
		 * @param string $context What the value is for. Valid values are view and edit.
		 * @param Masteriyo\RestApi\Controllers\Version1\GradeResultsController $controller REST Grades controller object.
		 */
		return apply_filters( "masteriyo_rest_response_{$this->object_type}_children_data", $data, $grade_result, $context, $this );
	}

	/**
	 * Get the grades'schema, conforming to JSON Schema.
	 *
	 * @since 2.5.20
	 *
	 * @return array
	 **/
	public function get_item_schema() {
		$schema = array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => $this->object_type,
			'type'       => 'object',
			'properties' => array(
				'id'                 => array(
					'description' => __( 'Unique identifier for the resource.', 'learning-management-system' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'user_id'            => array(
					'description' => __( 'User ID', 'learning-management-system' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
				),
				'course_id'          => array(
					'description' => __( 'Course ID', 'learning-management-system' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
				),
				'item_id'            => array(
					'description' => __( 'Grade item id', 'learning-management-system' ),
					'type'        => 'integer',
					'context'     => array( 'view', 'edit' ),
				),
				'item_type'          => array(
					'description' => __( 'Grade result item type', 'learning-management-system' ),
					'type'        => 'string',
					'enum'        => GradeResultType::all(),
					'context'     => array( 'view', 'edit' ),
				),
				'item_name'          => array(
					'description' => __( 'Grade result item name', 'learning-management-system' ),
					'type'        => 'string',
					'readonly'    => true,
					'context'     => array( 'view', 'edit' ),
				),
				'status'             => array(
					'description' => __( 'Grade result item status', 'learning-management-system' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
				),
				'grade_id'           => array(
					'description' => __( 'Grade ID', 'learning-management-system' ),
					'type'        => 'string',
					'readonly'    => true,
					'context'     => array( 'view', 'edit' ),
				),
				'grade_color'        => array(
					'description' => __( 'Grade color', 'learning-management-system' ),
					'type'        => 'string',
					'readonly'    => true,
					'context'     => array( 'view', 'edit' ),
				),
				'grade_name'         => array(
					'description' => __( 'Grade name', 'learning-management-system' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
				),
				'grade_point'        => array(
					'description' => __( 'Grade points.', 'learning-management-system' ),
					'type'        => 'number',
					'context'     => array( 'view', 'edit' ),
				),
				'earned_grade_point' => array(
					'description' => __( 'Earned grade point', 'learning-management-system' ),
					'type'        => 'number',
					'context'     => array( 'view', 'edit' ),
				),
				'earned_percent'     => array(
					'description' => __( 'Earned percent', 'learning-management-system' ),
					'type'        => 'number',
					'context'     => array( 'view', 'edit' ),
				),
				'created_at'         => array(
					'description' => __( 'The date the grade was created as GMT', 'learning-management-system' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
				),
				'modified_at'        => array(
					'description' => __( 'The date the grade was last modified as GMT', 'learning-management-system' ),
					'type'        => 'string',
					'context'     => array( 'view', 'edit' ),
					'readonly'    => true,
				),
				'meta_data'          => array(
					'description' => __( 'Meta data', 'learning-management-system' ),
					'type'        => 'array',
					'context'     => array( 'view', 'edit' ),
					'items'       => array(
						'type'       => 'object',
						'properties' => array(
							'id'    => array(
								'description' => __( 'Meta ID', 'learning-management-system' ),
								'type'        => 'integer',
								'context'     => array( 'view', 'edit' ),
								'readonly'    => true,
							),
							'key'   => array(
								'description' => __( 'Meta key', 'learning-management-system' ),
								'type'        => 'string',
								'context'     => array( 'view', 'edit' ),
							),
							'value' => array(
								'description' => __( 'Meta value', 'learning-management-system' ),
								'type'        => 'mixed',
								'context'     => array( 'view', 'edit' ),
							),
						),
					),
				),
			),
		);

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

	/**
	 * Prepare a single grade result for create or update.
	 *
	 * @since 2.5.20
	 *
	 * @param WP_REST_Request $request Request object.
	 * @param bool            $creating If is creating a new object.
	 *
	 * @return WP_Error|\\Masteriyo\Addons\Gradebook\Models\GradeResultResult
	 */
	protected function prepare_object_for_database( $request, $creating = false ) {
		$id           = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
		$grade_result = masteriyo( 'grade-result' );

		if ( 0 !== $id ) {
			$grade_result->set_id( $id );
			$grade_result_repo = masteriyo( 'grade-result.store' );
			$grade_result_repo->read( $grade_result );
		}

		// User ID
		if ( isset( $request['user_id'] ) ) {
			$grade_result->set_user_id( $request['user_id'] );
		}

		// Course ID
		if ( isset( $request['course_id'] ) ) {
			$grade_result->set_course_id( $request['course_id'] );
		}

		// Item ID
		if ( isset( $request['item_id'] ) ) {
			$grade_result->set_item_id( $request['item_id'] );
		}

		// Item type.
		if ( isset( $request['item_type'] ) ) {
			$grade_result->set_item_type( $request['item_type'] );
		}

		// Item name.
		if ( isset( $request['item_name'] ) ) {
			$grade_result->set_item_name( $request['item_name'] );
		}

		// Status.
		if ( isset( $request['status'] ) ) {
			$grade_result->set_status( $request['status'] );
		}

		// Grade name
		if ( isset( $request['grade_name'] ) ) {
			$grade_result->set_grade_name( $request['grade_name'] );
		}

		// Grade point
		if ( isset( $request['grade_point'] ) ) {
			$grade_result->set_grade_point( $request['grade_point'] );
		}

		// Earned Grade point
		if ( isset( $request['earned_grade_point'] ) ) {
			$grade_result->set_earned_grade_point( $request['earned_grade_point'] );
		}

		// Earned percent
		if ( isset( $request['earned_percent'] ) ) {
			$grade_result->set_earned_percent( $request['earned_percent'] );
		}

		// Allow set meta_data.
		if ( isset( $request['meta_data'] ) && is_array( $request['meta_data'] ) ) {
			foreach ( $request['meta_data'] as $meta ) {
				$grade_result->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
			}
		}

		/**
		 * Filters an object before it is inserted via the REST API.
		 *
		 * @since 2.5.20
		 *
		 * @param Masteriyo\Database\Model $grade_result Grade object.
		 * @param WP_REST_Request $request  Request object.
		 * @param bool            $creating If is creating a new object.
		 */
		return apply_filters( "masteriyo_rest_pre_insert_{$this->object_type}_object", $grade_result, $request, $creating );
	}

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

	/**
	 * Check if a given request has access to delete an item.
	 *
	 * @since 2.5.20
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */

	public function delete_item_permissions_check( $request ) {
		return $this->check_grade_result_permission( 'delete', $request['id'] );
	}

	/**
	 * Check if a given request has access to update an item.
	 *
	 * @since 2.5.20
	 *
	 * @param  WP_REST_Request $request Full details about the request.
	 * @return WP_Error|boolean
	 */
	public function update_item_permissions_check( $request ) {
		return $this->check_grade_result_permission( 'update', $request['id'] );
	}

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

	/**
	 * Checks if a given request has access to get a specific item.
	 *
	 * @since 2.5.20
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 * @return boolean|WP_Error True if the request has read access for the item, WP_Error object otherwise.
	 */
	public function get_item_permissions_check( $request ) {
		if ( is_null( $this->permission ) ) {
			return new \WP_Error(
				'masteriyo_null_permission',
				__( 'Sorry, the permission object for this resource is null.', 'learning-management-system' )
			);
		}

		return true;
	}

	/**
	 * Check permissions for an item.
	 *
	 * @since 1.0.0
	 *
	 * @param string $post_type Post type.
	 * @param string $context   Request context.
	 * @param int    $object_id Post ID.
	 *
	 * @return bool
	 */
	protected function check_item_permission( $post_type, $context = 'read', $object_id = 0 ) {
		return true;
	}

	/**
	 * Prepare objects query.
	 *
	 * @since  2.5.20
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *
	 * @return array
	 */
	protected function prepare_objects_query( $request ) {
		$args = wp_parse_args(
			$request->get_params(),
			array(
				'page'        => 1,
				'per_page'    => 10,
				'status'      => '',
				'item_type'   => isset( $request['item_type'] ) ? $request['item_type'] : array(),
				'started_at'  => null,
				'modified_at' => null,
			)
		);

		$args['paged'] = $args['page'];

		if ( masteriyo_is_current_user_instructor() ) {
			$query = new WP_Query(
				array(
					'author__in'     => array( get_current_user_id() ),
					'post_type'      => PostType::COURSE,
					'posts_per_page' => -1,
					'fields'         => 'ids',
				)
			);

			$course_ids = $query->posts;

			$args['item'] = $course_ids;
		}

		/**
		 * Filter the query arguments for a request.
		 *
		 * Enables adding extra arguments or setting defaults for a post
		 * collection request.
		 *
		 * @since 2.5.20
		 *
		 * @param array           $args    Key value array of query var to query value.
		 * @param WP_REST_Request $request The request used.
		 */
		$args = apply_filters( "masteriyo_rest_{$this->object_type}_object_query", $args, $request );

		return $args;
	}

	/**
	 * Return summary for grade results.
	 *
	 * @since 2.5.20
	 *
	 * @param \Masteriyo\Addons\Gradebook\Models\GradeResult $grade_result
	 * @param string $context
	 *
	 * @return array
	 */
	protected function get_summary( $grade_result, $context ) {
		$grade_result_types      = array_diff( GradeResultType::all(), array( GradeResultType::COURSE ) );
		$grade_result_post_types = array_map(
			function( $grade_result_type ) {
				return 'mto-' . $grade_result_type;
			},
			$grade_result_types
		);

		$posts = get_posts(
			array(
				'post_type'      => $grade_result_post_types,
				'posts_per_page' => -1,
				'meta_query'     => array(
					array(
						'key'   => '_course_id',
						'value' => $grade_result->get_course_id(),
						'type'  => 'numeric',
					),
				),
			)
		);

		//  Calculate total number of grade result types in a course.
		$total = array_fill_keys( $grade_result_types, 0 );
		foreach ( $posts as $post ) {
			$item_type = str_replace( 'mto-', '', $post->post_type );

			if ( isset( $total[ $item_type ] ) ) {
				++$total[ $item_type ];
			}
		}

		// Calculate how many of them are graded.
		$completed_grade_results = array_fill_keys( $grade_result_types, 0 );
		$query                   = new GradeResultQuery(
			array(
				'parent'    => $grade_result->get_id(),
				'item_type' => $grade_result_types,
			)
		);
		$grade_results           = $query->get_grade_results();

		foreach ( $grade_results as $grade_result ) {
			if ( isset( $completed_grade_results[ $grade_result->get_item_type() ] ) ) {
				++$completed_grade_results[ $grade_result->get_item_type() ];
			}
		}

		// Create a summary.
		$summary = array();
		foreach ( $completed_grade_results as $type => $value ) {
			$summary[ $type ] = array(
				'completed' => $value,
				'pending'   => $total[ $type ] - $value,
				'total'     => $total[ $type ],

			);
		}

		return $summary;
	}

	/**
	 * Process objects collection.
	 *
	 * @since 2.5.20
	 *
	 * @param array $objects Courses data.
	 * @param array $query_args Query arguments.
	 * @param array $query_results Courses query result data.
	 *
	 * @return array
	 */
	protected function process_objects_collection( $objects, $query_args, $query_results ) {
		return array(
			'data' => $objects,
			'meta' => array(
				'total'        => $query_results['total'],
				'pages'        => $query_results['pages'],
				'current_page' => $query_args['paged'],
				'per_page'     => $query_args['per_page'],
			),
		);
	}

	/**
	 * Check permissions of grade results on REST API.
	 *
	 * @since 2.5.20
	 * @param string $context   Request context.
	 * @param int    $object_id Grade result ID.
	 * @return bool
	 */
	protected function check_grade_result_permission( $context = 'read', $object_id = 0 ) {
		$contexts = array(
			'read'   => 'read_grade_results',
			'create' => 'publish_grade_results',
			'update' => 'edit_grade_result',
			'delete' => 'delete_grade_result',
			'batch'  => 'edit_others_grade_results',
		);

		$cap        = $contexts[ $context ];
		$permission = current_user_can( $cap, $object_id );

		/**
		 * Filters permission for a grade result.
		 *
		 * @since 2.5.20
		 *
		 * @param boolean $permission True if permission granted.
		 * @param string $context Permission context.
		 * @param integer $object_id Object ID which requires permission, if available.
		 */
		return apply_filters( 'masteriyo_rest_check_grade_result_permissions', $permission, $context, $object_id );
	}

	/**
	 * Check if a given request has access to delete items.
	 *
	 * @since 2.6.6
	 *
	 * @param  \WP_REST_Request $request Full details about the request.
	 * @return \WP_Error|boolean
	 */
	public function delete_items_permissions_check( $request ) {
		if ( is_null( $this->permission ) ) {
			return new \WP_Error(
				'masteriyo_null_permission',
				__( 'Sorry, the permission object for this resource is null.', 'learning-management-system' )
			);
		}

		$instructor = masteriyo_get_current_instructor();
		if ( $instructor && ! $instructor->is_active() ) {
			return new \WP_Error(
				'masteriyo_rest_user_not_approved',
				__( 'Sorry, you are not approved by the manager.', 'learning-management-system' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		if ( ! $this->check_grade_result_permission( 'batch' ) ) {
			return new \WP_Error(
				'masteriyo_rest_cannot_read',
				__( 'Sorry, you are not allowed to delete resources', 'learning-management-system' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		return true;
	}

	/**
	 * Prepare objects query for batch.
	 *
	 * @param WP_REST_Request $request Full details about the request.
	 *P
	 * @since  2.6.6
	 * @return array
	 */
	protected function prepare_objects_query_for_batch( $request ) {
		$query_args = array(
			'status'    => GradeResultStatus::all(),
			'item_type' => GradeResultType::COURSE,
			'include'   => wp_parse_id_list( $request['ids'] ),
			'per_page'  => -1,
		);

		/**
		 * Filters objects query for batch operation.
		 *
		 * @since 2.6.6
		 *
		 * @param array $query_args Query arguments.
		 * @param WP_REST_Request $request
		 * @param \Masteriyo\RestApi\Controllers\Version1\PostsController $controller
		 */
		return apply_filters( 'masteriyo_rest_objects_query_for_batch', $query_args, $request, $this );
	}

	/**
	 * Delete multiple items.
	 *
	 * @since 2.6.6
	 *
	 * @param \WP_REST_Request $request Full details about the request.
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function delete_items( $request ) {

		$deleted_objects = array();

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

		$objects = $this->get_objects( $this->prepare_objects_query_for_batch( $request ) );

		$objects = isset( $objects['objects'] ) ? $objects['objects'] : array();

		foreach ( $objects as $object ) {
			if ( ! $this->check_item_permission( $this->post_type, 'delete', $object->get_id() ) ) {
				continue;
			}

			$data = $this->prepare_object_for_response( $object, $request );

			$object->delete( true );

			if ( 0 === $object->get_id() ) {
				$deleted_objects[] = $this->prepare_response_for_collection( $data );
			}
		}

		if ( empty( $deleted_objects ) ) {
			return new \WP_Error(
				'masteriyo_rest_cannot_bulk_delete',
				/* translators: %s: post type */
				sprintf( __( 'The %s cannot be bulk deleted.', 'learning-management-system' ), $this->object_type ),
				array( 'status' => 500 )
			);
		}

		/**
		 * Fires after a multiple objects is deleted or trashed via the REST API.
		 *
		 * @since 2.6.6
		 *
		 * @param array $deleted_objects Objects collection which are deleted.
		 * @param array $objects Objects which are supposed to be deleted.
		 * @param WP_REST_Request  $request  The request sent to the API.
		 */
		do_action( "masteriyo_rest_bulk_delete_{$this->object_type}_objects", $deleted_objects, $objects, $request );

		return rest_ensure_response( $deleted_objects );
	}
}
