<?php
/**
 * Masteriyo Gradebook setup.
 *
 * @package Masteriyo\Gradebook
 *
 * @since 2.5.20
 */

namespace Masteriyo\Addons\Gradebook;

use Masteriyo\Constants;
use Masteriyo\Enums\OrderStatus;
use Masteriyo\Query\QuizAttemptQuery;
use Masteriyo\Pro\Enums\QuizGradingType;
use Masteriyo\Addons\Gradebook\PostType\Grade;
use Masteriyo\Addons\Gradebook\Models\GradeResult;
use Masteriyo\Addons\Gradebook\Enums\GradeResultType;
use Masteriyo\Addons\Gradebook\Query\GradeResultQuery;
use Masteriyo\Addons\Assignment\Models\AssignmentReply;
use Masteriyo\Addons\Gradebook\Enums\GradeResultStatus;
use Masteriyo\Addons\Assignment\Query\AssignmentReplyQuery;
use Masteriyo\Addons\Assignment\Enums\AssignmentReplyStatus;
use Masteriyo\Addons\Gradebook\Controllers\GradesController;
use Masteriyo\Addons\Gradebook\Controllers\GradeResultsController;
use Masteriyo\Enums\QuizAttemptStatus;

defined( 'ABSPATH' ) || exit;

/**
 * Main Masteriyo Gradebook class.
 *
 * @class Masteriyo\Addons\Gradebook\GradebookAddon
 */

class GradebookAddon {
	/**
	 * Initialize the app.
	 *
	 * @since 2.5.20
	 */
	public function init() {
		$this->init_hooks();
	}

	/**
	 * Initialize hooks.
	 *
	 * @since 2.5.20
	 */
	public function init_hooks() {
		add_filter( 'masteriyo_migrations_paths', array( $this, 'add_migrations' ) );

		add_filter( 'masteriyo_rest_api_get_rest_namespaces', array( $this, 'register_rest_namespaces' ) );
		add_filter( 'masteriyo_admin_submenus', array( $this, 'add_gradebook_submenu' ) );
		add_filter( 'masteriyo_register_post_types', array( $this, 'register_post_types' ) );

		add_filter( 'masteriyo_rest_quiz_schema', array( $this, 'add_gradebook_schema' ) );
		add_action( 'masteriyo_new_quiz', array( $this, 'save_gradebook_data' ), 10, 2 );
		add_action( 'masteriyo_update_quiz', array( $this, 'save_gradebook_data' ), 10, 2 );
		add_filter( 'masteriyo_rest_response_quiz_data', array( $this, 'append_gradebook_data' ), 10, 4 );

		add_filter( 'masteriyo_rest_assignment_schema', array( $this, 'add_gradebook_schema' ) );
		add_action( 'masteriyo_new_assignment', array( $this, 'save_gradebook_data' ), 10, 2 );
		add_action( 'masteriyo_update_assignment', array( $this, 'save_gradebook_data' ), 10, 2 );
		add_filter( 'masteriyo_rest_response_assignment_data', array( $this, 'append_gradebook_data' ), 10, 4 );

		add_action( 'masteriyo_new_assignment_reply', array( $this, 'save_assignment_submission_to_gradebook_results' ), 10, 2 );
		add_action( 'masteriyo_update_assignment_reply', array( $this, 'save_assignment_submission_to_gradebook_results' ), 10, 2 );
		add_action( 'masteriyo_after_delete_assignment_reply', array( $this, 'delete_assignment_submission_from_gradebook_results' ), 10, 2 );

		add_action( 'masteriyo_create_quiz_attempt', array( $this, 'save_quiz_attempt_to_gradebook_results' ), 10, 2 );
		add_action( 'masteriyo_update_quiz_attempt', array( $this, 'save_quiz_attempt_to_gradebook_results' ), 10, 2 );
		add_action( 'masteriyo_after_delete_quiz_attempt', array( $this, 'delete_quiz_attempt_from_gradebook_results' ), 10, 2 );

		add_action( 'masteriyo_single_course_main_content_tab', array( $this, 'render_gradebook_tab_single_course_page' ) );
		add_action( 'masteriyo_layout_1_single_course_main_content_tabbar', array( $this, 'render_gradebook_tab_single_course_page' ) );
		add_action( 'masteriyo_single_course_main_content', array( $this, 'render_gradebook_content_single_course_page' ) );
		add_action( 'masteriyo_layout_1_single_course_tabbar_content', array( $this, 'render_gradebook_content_single_course_page' ) );

		add_action( 'map_meta_cap', array( $this, 'map_meta_cap' ), 10, 4 );

		add_action( 'masteriyo_after_delete_grade_result', array( $this, 'recompute_course_grade_on_deletion_of_grade_result' ), 10, 2 );
	}

	/**
	 * Recompute course grade after grade result is delete.
	 *
	 * @since 2.5.20
	 *
	 * @param int $grade_id Grade ID.
	 * @param \Masteriyo\Addons\Gradebook\Models\GradeResult $grade_result Grade result object.
	 */
	public function recompute_course_grade_on_deletion_of_grade_result( $grade_id, $grade_result ) {
		// Bail early if the grade result which is course once.
		if ( GradeResultType::COURSE === $grade_result->get_item_type() ) {
			return;
		}

		$course = $grade_result->get_course();

		if ( $course ) {
			$final_grade_result = $this->create_course_grade_result( $course->get_id(), $grade_result->get_user_id() );
			$this->calculate_course_grades( $final_grade_result, $grade_result->get_user_id() );
		}
	}

	/**
	 * Map custom capabilities.
	 *
	 * @since 2.5.20
	 *
	 * @param string[] $caps    Primitive capabilities required of the user.
	 * @param string   $cap     Capability being checked.
	 * @param int      $user_id The user ID.
	 * @param array    $args    Adds context to the capability check, typically
	 *                          starting with an object ID.
	 * @return array
	 */
	public static function map_meta_cap( $caps, $cap, $user_id, $args ) {
		if ( ! masteriyo_ends_with( $cap, 'grade_result' ) ) {
			return $caps;

		}

		switch ( $cap ) {
			case 'edit_grade_result':
				$grade_result = masteriyo_get_grade_result( $args[0] );

				if ( ! $grade_result ) {
					$caps[] = 'do_not_allow';
					break;
				}

				$course = $grade_result->get_course();

				if ( $course && $course->get_author_id() && $user_id === $course->get_author_id() ) {
					$caps = array( 'edit_grade_results' );
				} else {
					$caps = user_can( $user_id, 'edit_others_grade_results' ) ? array( 'edit_grade_results' ) : array( 'do_not_allow' );
				}

				break;

			case 'delete_grade_result':
				$grade_result = masteriyo_get_grade_result( $args[0] );

				if ( ! $grade_result ) {
					$caps[] = 'do_not_allow';
					break;
				}

				$course = $grade_result->get_course();

				if ( $course && $course->get_author_id() && $user_id === $course->get_author_id() ) {
					$caps = array( 'delete_grade_results' );
				} else {
					$caps = user_can( $user_id, 'delete_others_grade_results' ) ? array( 'delete_grade_results' ) : array( 'do_not_allow' );
				}

				break;

		}

		return $caps;
	}
	/**
	 * Render gradebook tab in single course page.
	 *
	 * @since 2.5.20
	 *
	 * @param \Masteriyo\Models\Course $course
	 */
	public function render_gradebook_tab_single_course_page( $course ) {
		if ( ! is_user_logged_in() ) {
			return;
		}

		$final_grade_result = masteriyo_get_grade_result_item( $course->get_id(), GradeResultType::COURSE, get_current_user_id() );

		if ( empty( $final_grade_result ) ) {
			return;
		}

		require_once Constants::get( 'MASTERIYO_GRADEBOOK_ADDON_DIR' ) . '/templates/gradebook-tab.php';
	}

	/**
	 * Render gradebook tab content in single course page.
	 *
	 * @since 2.5.20
	 *
	 * @param \Masteriyo\Models\Course $course
	 */
	public function render_gradebook_content_single_course_page( $course ) {
		if ( ! is_user_logged_in() ) {
			return;
		}

		$final_grade_result = masteriyo_get_grade_result_item( $course->get_id(), GradeResultType::COURSE, get_current_user_id() );

		if ( empty( $final_grade_result ) ) {
			return;
		}

		$query = new GradeResultQuery(
			array(
				'user'   => get_current_user_id(),
				'parent' => $final_grade_result->get_id(),
			)
		);

		$grade_results = $query->get_grade_results();

		if ( empty( $grade_results ) ) {
			return;
		}

		require_once Constants::get( 'MASTERIYO_GRADEBOOK_ADDON_DIR' ) . '/templates/gradebook.php';
	}

	/**
	 * Create course grade result if not preset.
	 *
	 * @since 2.5.20
	 *
	 * @param int $course_id Course ID.
	 * @param int $user_id  User ID.
	 *
	 * @return \Masteriyo\Addons\Gradebook\Models\GradeResult
	 */
	public function create_course_grade_result( $course_id, $user_id ) {
		$course = masteriyo_get_course( $course_id );

		if ( ! $course ) {
			return;
		}

		$final_result = masteriyo_get_grade_result_item( $course->get_id(), GradeResultType::COURSE, $user_id );

		if ( empty( $final_result ) ) {
			$final_result = masteriyo_create_grade_result_object();
			$status       = masteriyo_calculate_course_grade_result_status( $course_id, $user_id );

			$final_result->set_status( $status );
			$final_result->set_user_id( $user_id );
			$final_result->set_item_id( $course->get_id() );
			$final_result->set_item_name( $course->get_name() );
			$final_result->set_item_type( GradeResultType::COURSE );
			$final_result->save();
		}

		return $final_result;
	}

	/**
	 * Calculate final course grades.
	 *
	 * @since 2.5.20
	 *
	 * @param \Masteriyo\Addons\Gradebook\Models\GradeResult $final_result Course/Final grade result.
	 * @param int $user_id User ID.
	 *
	 * @return \Masteriyo\Addons\Gradebook\Models\GradeResult
	 */
	public function calculate_course_grades( $final_result, $user_id ) {
		// Calculate overall grade based on the grade result items like quiz and assignment.
		$query = new GradeResultQuery(
			array(
				'parent'    => $final_result->get_id(),
				'user'      => $user_id,
				'item_type' => array_diff( GradeResultType::all(), array( GradeResultType::COURSE ) ),
			)
		);

		$grade_result_items = $query->get_grade_results();

		// Bail early if there not quiz attempts or assignments.
		if ( empty( $grade_result_items ) ) {
			return;
		}

		$status                     = masteriyo_calculate_course_grade_result_status( $final_result->get_id(), $user_id );
		$final_weight               = $this->calculate_final_weight( $grade_result_items );
		$final_earned_grade_percent = $this->calculate_final_earned_grade_percent( $grade_result_items );
		$final_earned_grade         = masteriyo_get_grade_from_percentage( $final_earned_grade_percent );
		$final_earned_grade_point   = $final_earned_grade ? $final_earned_grade->get_points() : 0;
		$final_grade_name           = $final_earned_grade ? $final_earned_grade->get_name() : 0;
		$highest_grade              = masteriyo_get_highest_grade();
		$grade_point                = $highest_grade ? $highest_grade->get_points() : 0;
		$grade_id                   = $final_earned_grade ? $final_earned_grade->get_id() : 0;
		$grade_color                = $final_earned_grade ? $final_earned_grade->get_color() : '';

		$course = $final_result->get_course();
		if ( $course ) {
			$final_result->set_status( $status );
			$final_result->set_user_id( $user_id );
			$final_result->set_grade_point( $grade_point );
			$final_result->set_grade_id( $grade_id );
			$final_result->set_grade_color( $grade_color );
			$final_result->set_item_id( $course->get_id() );
			$final_result->set_item_name( $course->get_name() );
			$final_result->set_weight( $final_weight );
			$final_result->set_earned_percent( $final_earned_grade_percent );
			$final_result->set_earned_grade_point( $final_earned_grade_point );
			$final_result->set_grade_name( $final_grade_name );
			$final_result->save();
		}

		return $final_result;
	}

	/**
	 * Calculate total weight from grade results.
	 *
	 * @since 2.5.20
	 *
	 * @param \Masteriyo\Addons\Gradebook\Models\GradeResult[] $grade_results
	 *
	 * @return int
	 */
	protected function calculate_final_weight( $grade_results ) {
		return array_reduce(
			$grade_results,
			function( $total, $grade_result ) {
				$total += $grade_result->get_weight();
				return $total;
			},
			0
		);
	}

	/**
	 * Return final earned grade percent
	 *
	 * @since 2.5.20
	 *
	 * @param \Masteriyo\Addons\Gradebook\Models\GradeResult[] $grade_results
	 * @return int
	 */
	protected function calculate_final_earned_grade_percent( $grade_results ) {
		$final_weight = $this->calculate_final_weight( $grade_results );

		$total_earned_grade_percent = array_reduce(
			$grade_results,
			function( $total, $grade_result ) {
				$total += ( $grade_result->get_earned_percent() * $grade_result->get_weight() );
				return $total;
			},
			0
		);

		return masteriyo_round( $total_earned_grade_percent / $final_weight, 2 );
	}


	/**
	 * Save quiz attempt to grade results.
	 *
	 * @since 2.5.20
	 *
	 * @param int $id Assignment ID.
	 * @param \Masteriyo\Models\QuizAttempt $quiz_attempt
	 */
	public function delete_quiz_attempt_from_gradebook_results( $id, $quiz_attempt ) {
		$quiz = masteriyo_get_quiz( $quiz_attempt->get_quiz_id() );
		if ( ! $quiz ) {
			return;
		}

		$course = masteriyo_get_course( $quiz->get_course_id() );
		if ( ! $course ) {
			return;
		}

		// Create final grade result if not present.
		$final_grade_result = $this->create_course_grade_result( $quiz->get_course_id(), $quiz_attempt->get_user_id() );

		$weight       = max( 1, absint( $quiz->get_meta( '_weight' ) ) );
		$grade_result = masteriyo_get_grade_result_item( $quiz->get_id(), GradeResultType::QUIZ, $quiz_attempt->get_user_id(), $final_grade_result->get_id() );

		if ( ! $grade_result ) {
			return;
		}

		$query = new QuizAttemptQuery(
			array(
				'quiz'    => $quiz->get_id(),
				'orderby' => 'id',
				'order'   => 'DESC',
			)
		);

		$new_quiz_attempt = current( $query->get_quiz_attempts() );

		// If not other assignment submissions can be found, delete the submission result form the gradebook
		if ( $new_quiz_attempt ) {
			$quiz_grade_type = masteriyo_get_setting( 'quiz.general.grading' );
			$quiz_grade_type = $quiz_grade_type ? $quiz_grade_type : QuizGradingType::LAST_ATTEMPT;
			$earned_marks    = masteriyo_grade_quiz( $quiz_attempt->get_quiz_id(), $quiz_attempt->get_user_id(), $quiz_grade_type );
			$full_marks      = masteriyo_get_quiz_full_marks( $quiz_attempt->get_quiz_id(), $quiz_attempt->get_user_id(), $quiz_grade_type );

			$highest_grade        = masteriyo_get_highest_grade();
			$grade_point          = $highest_grade ? $highest_grade->get_points() : 0;
			$earned_grade_percent = masteriyo_round( masteriyo_amount_to_percent( $earned_marks, $full_marks ), 2 );
			$earned_grade         = masteriyo_get_grade_from_percentage( $earned_grade_percent );
			$earned_grade_point   = $earned_grade ? $earned_grade->get_points() : 0;
			$grade_name           = $earned_grade ? $earned_grade->get_name() : '';
			$grade_id             = $earned_grade ? $earned_grade->get_id() : 0;
			$grade_color          = $earned_grade ? $earned_grade->get_color() : '';
			$status               = $quiz_attempt->is_reviewed() ? GradeResultStatus::COMPLETED : GradeResultStatus::PENDING;

			$grade_result->set_status( $status );
			$grade_result->set_item_type( GradeResultType::QUIZ );
			$grade_result->set_item_id( $quiz->get_id() );
			$grade_result->set_grade_point( $grade_point );
			$grade_result->set_grade_name( $grade_name );
			$grade_result->set_grade_id( $grade_id );
			$grade_result->set_grade_color( $grade_color );
			$grade_result->set_earned_percent( $earned_grade_percent );
			$grade_result->set_earned_grade_point( $earned_grade_point );
			$grade_result->set_parent_id( $final_grade_result->get_id() );
			$grade_result->set_item_name( $quiz->get_name() );
			$grade_result->set_weight( $weight );
			$grade_result->save();
		} else {
			$grade_result->delete();
		}

		// Calculate final/course grade book.
		$final_grade_result = $this->calculate_course_grades( $final_grade_result, $quiz_attempt->get_user_id() );
	}

	/**
	 * Save assignment response to grade results.
	 *
	 * @since 2.5.20
	 *
	 * @param int $id Assignment ID.
	 * @param \Masteriyo\Addons\Assignment\Models\AssignmentReply $reply Assignment reply object.
	 */
	public function delete_assignment_submission_from_gradebook_results( $id, $reply ) {
		$assignment = masteriyo_get_assignment( $reply->get_assignment_id() );
		if ( ! $assignment ) {
			return;
		}

		$course = masteriyo_get_course( $assignment->get_course_id() );
		if ( ! $course ) {
			return;
		}

		// Create final grade result if not present.
		$final_grade_result = $this->create_course_grade_result( $assignment->get_course_id(), $reply->get_user_id() );

		$weight       = max( 1, absint( $assignment->get_meta( '_weight' ) ) );
		$grade_result = masteriyo_get_grade_result_item( $assignment->get_id(), GradeResultType::ASSIGNMENT, $reply->get_user_id(), $final_grade_result->get_id() );

		if ( ! $grade_result ) {
			return;
		}

		$query = new AssignmentReplyQuery(
			array(
				'assignment' => $assignment->get_id(),
				'orderby'    => 'id',
				'order'      => 'DESC',
			)
		);

		$new_reply = current( $query->get_assignment_replies() );

		// If not other assignment submissions can be found, delete the submission result form the gradebook
		if ( $new_reply ) {
			$highest_grade        = masteriyo_get_highest_grade();
			$grade_point          = $highest_grade ? $highest_grade->get_points() : 0;
			$earned_grade_percent = masteriyo_round( masteriyo_amount_to_percent( $new_reply->get_earned_points(), $assignment->get_total_points() ), 2 );
			$earned_grade         = masteriyo_get_grade_from_percentage( $earned_grade_percent );
			$earned_grade_point   = $earned_grade ? $earned_grade->get_points() : 0;
			$grade_name           = $earned_grade ? $earned_grade->get_name() : '';
			$grade_id             = $earned_grade ? $earned_grade->get_id() : 0;
			$grade_color          = $earned_grade ? $earned_grade->get_color() : '';
			$status               = $reply->is_reviewed() ? GradeResultStatus::COMPLETED : GradeResultStatus::PENDING;

			$grade_result->set_status( $status );
			$grade_result->set_item_type( GradeResultType::ASSIGNMENT );
			$grade_result->set_item_id( $assignment->get_id() );
			$grade_result->set_grade_point( $grade_point );
			$grade_result->set_grade_name( $grade_name );
			$grade_result->set_grade_id( $grade_id );
			$grade_result->set_grade_color( $grade_color );
			$grade_result->set_earned_percent( $earned_grade_percent );
			$grade_result->set_earned_grade_point( $earned_grade_point );
			$grade_result->set_parent_id( $final_grade_result->get_id() );
			$grade_result->set_item_name( $assignment->get_name() );
			$grade_result->set_weight( $weight );
			$grade_result->save();
		} else {
			$grade_result->delete();
		}

		// Calculate final/course grade book.
		$final_grade_result = $this->calculate_course_grades( $final_grade_result, $reply->get_user_id() );
	}

	/**
	 * Save assignment response to grade results.
	 *
	 * @since 2.5.20
	 *
	 * @param int $id Assignment ID.
	 * @param \Masteriyo\Addons\Assignment\Models\AssignmentReply $reply Assignment reply object.
	 */
	public function save_assignment_submission_to_gradebook_results( $id, $reply ) {
		$assignment = masteriyo_get_assignment( $reply->get_assignment_id() );
		if ( ! $assignment ) {
			return;
		}

		$course = masteriyo_get_course( $assignment->get_course_id() );
		if ( ! $course ) {
			return;
		}

		// Create final grade result if not present.
		$final_grade_result = $this->create_course_grade_result( $course->get_id(), $reply->get_user_id() );

		$weight       = max( 1, absint( $assignment->get_meta( '_weight' ) ) );
		$grade_result = masteriyo_get_grade_result_item( $assignment->get_id(), GradeResultType::ASSIGNMENT, $reply->get_user_id(), $final_grade_result->get_id() );
		$grade_result = $grade_result ? $grade_result : masteriyo_create_grade_result_object();

		$highest_grade        = masteriyo_get_highest_grade();
		$grade_point          = $highest_grade ? $highest_grade->get_points() : 0;
		$earned_grade_percent = masteriyo_round( masteriyo_amount_to_percent( $reply->get_earned_points(), $assignment->get_total_points() ), 2 );
		$earned_grade         = masteriyo_get_grade_from_percentage( $earned_grade_percent );
		$earned_grade_point   = $earned_grade ? $earned_grade->get_points() : 0;
		$grade_name           = $earned_grade ? $earned_grade->get_name() : '';
		$grade_id             = $earned_grade ? $earned_grade->get_id() : 0;
		$grade_color          = $earned_grade ? $earned_grade->get_color() : '';
		$status               = $reply->is_reviewed() ? GradeResultStatus::COMPLETED : GradeResultStatus::PENDING;

		$grade_result->set_status( $status );
		$grade_result->set_item_type( GradeResultType::ASSIGNMENT );
		$grade_result->set_item_id( $assignment->get_id() );
		$grade_result->set_grade_point( $grade_point );
		$grade_result->set_grade_name( $grade_name );
		$grade_result->set_grade_id( $grade_id );
		$grade_result->set_grade_color( $grade_color );
		$grade_result->set_earned_percent( $earned_grade_percent );
		$grade_result->set_earned_grade_point( $earned_grade_point );
		$grade_result->set_parent_id( $final_grade_result->get_id() );
		$grade_result->set_item_name( $assignment->get_name() );
		$grade_result->set_weight( $weight );
		$grade_result->save();

		// Calculate final/course grade book.
		$final_grade_result = $this->calculate_course_grades( $final_grade_result, $reply->get_user_id() );
	}

	/**
	 * Save quiz attempt response to grade results.
	 *
	 * @since 2.5.20
	 *
	 * @param int $id Quiz attempt ID.
	 * @param \Masteriyo\Models\QuizAttempt $quiz_attempt Quiz attempt object.
	 */
	public function save_quiz_attempt_to_gradebook_results( $id, $quiz_attempt ) {
		$quiz = masteriyo_get_quiz( $quiz_attempt->get_quiz_id() );
		if ( ! $quiz ) {
			return;
		}

		$course = masteriyo_get_course( $quiz->get_course_id() );
		if ( ! $course ) {
			return;
		}

		$user = masteriyo_get_user( $quiz_attempt->get_user_id() );

		if ( is_wp_error( $user ) || is_null( $user ) ) {
			return;
		}

		// Create final grade result if not present.
		$final_grade_result = $this->create_course_grade_result( $quiz->get_course_id(), $quiz_attempt->get_user_id() );

		$quiz_grade_type = masteriyo_get_setting( 'quiz.general.grading' );
		$quiz_grade_type = $quiz_grade_type ? $quiz_grade_type : QuizGradingType::LAST_ATTEMPT;
		$earned_marks    = masteriyo_grade_quiz( $quiz->get_id(), $user->get_id(), $quiz_grade_type );
		$full_marks      = masteriyo_get_quiz_full_marks( $quiz, $user, $quiz_grade_type );

		$highest_grade        = masteriyo_get_highest_grade();
		$grade_point          = $highest_grade ? $highest_grade->get_points() : 0;
		$earned_grade_percent = masteriyo_round( masteriyo_amount_to_percent( $earned_marks, $full_marks ), 2 );
		$earned_grade         = masteriyo_get_grade_from_percentage( $earned_grade_percent );
		$earned_grade_point   = $earned_grade ? $earned_grade->get_points() : 0;
		$grade_name           = $earned_grade ? $earned_grade->get_name() : '';
		$grade_id             = $earned_grade ? $earned_grade->get_id() : 0;
		$grade_color          = $earned_grade ? $earned_grade->get_color() : '';

		$weight       = max( 1, absint( $quiz->get_meta( '_weight' ) ) );
		$grade_result = masteriyo_get_grade_result_item( $quiz->get_id(), GradeResultType::QUIZ, $quiz_attempt->get_user_id(), $final_grade_result->get_id() );
		$grade_result = $grade_result ? $grade_result : masteriyo_create_grade_result_object();
		$status       = $quiz_attempt->is_reviewed() ? GradeResultStatus::COMPLETED : GradeResultStatus::PENDING;

		$grade_result->set_item_type( GradeResultType::QUIZ );
		$grade_result->set_status( $status );
		$grade_result->set_item_id( $quiz->get_id() );
		$grade_result->set_grade_point( $grade_point );
		$grade_result->set_grade_name( $grade_name );
		$grade_result->set_grade_id( $grade_id );
		$grade_result->set_grade_color( $grade_color );
		$grade_result->set_earned_percent( $earned_grade_percent );
		$grade_result->set_earned_grade_point( $earned_grade_point );
		$grade_result->set_parent_id( $final_grade_result->get_id() );
		$grade_result->set_item_name( $quiz->get_name() );
		$grade_result->set_weight( $weight );
		$grade_result->save();

		// Calculate final/course grade book.
		$final_grade_result = $this->calculate_course_grades( $final_grade_result, $quiz_attempt->get_user_id() );
	}

	/**
	 * Add migrations.
	 *
	 * @since 2.5.20
	 *
	 * @param array $migrations
	 * @return array
	 */
	public function add_migrations( $migrations ) {
		$migrations[] = plugin_dir_path( MASTERIYO_GRADEBOOK_ADDON_FILE ) . 'migrations';

		return $migrations;
	}

	/**
	 * Add gradebook submenu.
	 *
	 * @since 2.5.20
	 */
	public function add_gradebook_submenu( $submenus ) {
		$submenus['gradebook/results'] = array(
			'page_title' => __( 'Gradebook', 'learning-management-system' ),
			'menu_title' => __( 'Gradebook', 'learning-management-system' ),
			'capability' => 'edit_courses',
			'position'   => 71,
		);

		return $submenus;
	}

	/**
	 * Register post types.
	 *
	 * @since 2.5.20
	 *
	 * @param array $post_types
	 * @return array
	 */
	public function register_post_types( $post_types ) {
		$post_types['grade'] = Grade::class;

		return $post_types;
	}

	/**
	 * Register namespaces.
	 *
	 * @since 2.5.20
	 *
	 * @param array $namespaces
	 * @return array
	 */
	public function register_rest_namespaces( $namespaces ) {
		$namespaces['masteriyo/pro/v1']['grades']        = GradesController::class;
		$namespaces['masteriyo/pro/v1']['grade-results'] = GradeResultsController::class;
		return $namespaces;
	}

	/**
	 * Add content drip fields to course schema.
	 *
	 * @since 2.5.20
	 *
	 * @param array $schema
	 * @return array
	 */
	public function add_gradebook_schema( $schema ) {
		$schema = wp_parse_args(
			$schema,
			array(
				'weight' => array(
					'description' => __( 'Grading weight', 'learning-management-system' ),
					'type'        => 'number',
					'default'     => 0,
					'context'     => array( 'view', 'edit' ),
				),
			)
		);

		return $schema;
	}

	/**
	 * Save content drip data to assignment or quiz or assignment.
	 *
	 * @since 2.5.20
	 *
	 * @param integer $id The Assignment or quiz ID.
	 * @param \Masteriyo\Addons\Assignment\Models\Assignment|\Masteriyo\Models\Quiz $object Assignment Or Quiz object
	 */
	public function save_gradebook_data( $id, $object ) {
		$request = masteriyo_current_http_request();

		if ( null === $request ) {
			return;
		}

		if ( ! isset( $request['weight'] ) ) {
			return;
		}

		$object->update_meta_data( '_weight', absint( $request['weight'] ) );

		$object->save_meta_data();
	}

	/**
	 * Append content drip to assignment or quiz or assignment response.
	 *
	 * @since 2.5.20
	 *
	 * @param array $data Assignment/Quiz data.
	 * @param \Masteriyo\Addons\Assignment\Assignment|\Masteriyo\Models\Quiz $object Assignment or Quiz object. object.
	 * @param string $context What the value is for. Valid values are view and edit.
	 * @param \Masteriyo\Addons\Assignment\RestApi\AssignmentsController|\Masteriyo\RestApi\Controllers\Version1\QuizesController $controller REST courses controller object.
	 */
	public function append_gradebook_data( $data, $object, $context, $controller ) {
		$data['weight'] = absint( $object->get_meta( '_weight' ) );

		return $data;
	}
}
