<?php
/**
 * StudentReportsController Class
 *
 * This class provides REST API endpoints for fetching student reports based on their IDs.
 *
 * @since 2.6.11
 *
 * @package Masteriyo\RestApi\Controllers\Version1
 */

namespace Masteriyo\RestApi\Controllers\Version1;

defined( 'ABSPATH' ) || exit;

use Masteriyo\Enums\CourseProgressStatus;
use Masteriyo\Helper\Permission;
use Masteriyo\PostType\PostType;

/**
 * Class StudentReportsController
 *
 * @since 2.6.11
 */
class StudentReportsController extends RestController {

	/**
	 * @var string Endpoint namespace
	 *
	 * @since 2.6.11
	 */
	protected $namespace = 'masteriyo/v1';

	/**
	 * @var string Route base
	 *
	 * @since 2.6.11
	 */
	protected $rest_base = 'student-reports';

	/**
	 * @var Permission Permission class instance
	 *
	 * @since 2.6.11
	 */
	protected $permission = null;

	/**
	 * StudentReportsController constructor.
	 *
	 * @param Permission $permission
	 */
	public function __construct( Permission $permission ) {
		$this->permission = $permission;
	}

	/**
	 * Register the REST API routes for this controller.
	 *
	 * @since 2.6.11
	 *
	 * @return void
	 */
	public function register_routes() {
		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_student_report' ),
					'permission_callback' => array( $this, 'get_student_report_permission_check' ),
					'args'                => array(
						'context' => $this->get_context_param(
							array(
								'default' => 'view',
							)
						),
					),
				),
			)
		);
	}

	/**
	 * Fetch student report based on the ID.
	 *
	 * @since 2.6.11
	 *
	 * @param \WP_REST_Request $request Full details about the request, including the ID of the student.
	 *
	 * @return \WP_Error|\WP_REST_Response
	 */
	public function get_student_report( \WP_REST_Request $request ) {
		$student_id = isset( $request['id'] ) ? $request['id'] : 0;

		if ( 0 === $student_id ) {
			return new \WP_Error( 'invalid_student_id', 'Invalid Student ID', array( 'status' => 400 ) );
		}

		$report_data = array();

		$report_data['course_info']      = $this->get_course_info( $student_id );
		$report_data['lesson_info']      = $this->get_lesson_info( $student_id );
		$report_data['quiz_info']        = $this->get_quiz_info( $student_id );
		$report_data['user_info']        = $this->get_user_info( $student_id );
		$report_data['question_info']    = $this->get_question_info( $student_id );
		$report_data['assignment_info']  = $this->get_assignment_info( $student_id );
		$report_data['enrolled_courses'] = $this->get_enrolled_courses( $student_id );

		return rest_ensure_response( $report_data );
	}

	/**
	 * Fetch information related to courses for the student.
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id The student ID.
	 *
	 * @return array An associative array containing course-related information.
	 */
	private function get_course_info( $student_id ) {
		$enrolled_courses_count    = masteriyo_get_user_enrolled_courses_count( $student_id );
		$completed_courses_count   = masteriyo_get_user_courses_count_by_course_status( $student_id, CourseProgressStatus::COMPLETED );
		$in_progress_courses_count = masteriyo_get_user_courses_count_by_course_status( $student_id );

		return array(
			'enrolled_courses_count'    => $enrolled_courses_count,
			'completed_courses_count'   => $completed_courses_count,
			'in_progress_courses_count' => $in_progress_courses_count,
		);
	}

	/**
	 * Fetch information related to lessons
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id The student ID
	 *
	 * @return array An associative array containing lesson-related information.
	 */
	private function get_lesson_info( $student_id ) {

		global $wpdb;

		$total_lessons_count = 0;
		$completed_lessons   = 0;

		$course_ids = $this->get_enrolled_course_ids( $student_id );

		if ( empty( $course_ids ) ) {
			return array(
				'total_lessons_count' => 0,
				'completed_lessons'   => 0,
			);
		}

		foreach ( $course_ids as $course_id ) {
			$lesson_ids = $this->get_item_ids( $course_id, PostType::LESSON );

			$total_lessons_count += count( $lesson_ids );
		}

		$completed_lessons = $this->get_completed_item( $student_id, 'lesson' );

		return array(
			'total_lessons_count' => absint( $total_lessons_count ),
			'completed_lessons'   => absint( $completed_lessons ),
		);
	}

	/**
	 * Fetch information related to quizzes
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id The student ID
	 *
	 * @return array  An associative array containing quiz-related information.
	 */
	private function get_quiz_info( $student_id ) {
		global $wpdb;

		$total_quizzes_count = 0;
		$completed_quizzes   = 0;

		$course_ids = $this->get_enrolled_course_ids( $student_id );

		if ( empty( $course_ids ) ) {
			return array(
				'total_quizzes_count' => 0,
				'completed_quizzes'   => 0,
			);
		}

		foreach ( $course_ids as $course_id ) {
			$quiz_ids = $this->get_item_ids( $course_id, PostType::QUIZ );

			$total_quizzes_count += count( $quiz_ids );
		}

		$completed_quizzes = $this->get_completed_item( $student_id, 'quiz' );

		return array(
			'total_quizzes_count' => absint( $total_quizzes_count ),
			'completed_quizzes'   => absint( $completed_quizzes ),
		);
	}

	/**
	 * Fetch general user information
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id The student ID
	 *
	 * @return array  An associative array containing user-related information.
	 */
	private function get_user_info( int $student_id ) {
		$user = masteriyo_get_user( $student_id );

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

		return array(
			'id'           => $user->get_id(),
			'username'     => $user->get_username(),
			'email'        => $user->get_email(),
			'first_name'   => $user->get_first_name(),
			'last_name'    => $user->get_last_name(),
			'display_name' => $user->get_display_name(),
			'url'          => $user->get_url(),
			'avatar_url'   => $user->profile_image_url(),
			'date_created' => masteriyo_rest_prepare_date_response( $user->get_date_created() ),
		);
	}

	/**
	 * Fetch information related to questions for the student.
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id The student ID.
	 *
	 * @return array An associative array containing question-related information.
	 */
	private function get_question_info( $student_id ) {

		global $wpdb;

		$total_questions         = 0;
		$completed_questions     = 0;
		$total_incorrect_answers = 0;
		$total_correct_answers   = 0;

		$course_ids = $this->get_enrolled_course_ids( $student_id );

		if ( empty( $course_ids ) ) {
			return array(
				'total_questions'         => 0,
				'completed_questions'     => 0,
				'total_incorrect_answers' => 0,
				'total_correct_answers'   => 0,
			);
		}

		foreach ( $course_ids as $course_id ) {
			$question_ids = $this->get_item_ids( $course_id, PostType::QUESTION );

			$total_questions += count( $question_ids );
		}

		$completed_questions = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT SUM(total_answered_questions) FROM {$wpdb->prefix}masteriyo_quiz_attempts WHERE user_id = %d",
				$student_id
			)
		);

		$total_incorrect_answers = $wpdb->get_var(
			$wpdb->prepare(
				"SELECT SUM(total_incorrect_answers) FROM {$wpdb->prefix}masteriyo_quiz_attempts WHERE user_id = %d",
				$student_id
			)
		);

		$total_correct_answers = absint( $completed_questions ) - absint( $total_incorrect_answers );

		return array(
			'total_questions'         => absint( $total_questions ),
			'completed_questions'     => absint( $completed_questions ),
			'total_incorrect_answers' => absint( $total_incorrect_answers ),
			'total_correct_answers'   => absint( $total_correct_answers ),
		);
	}

	/**
	 * Fetch information related to assignments for the student.
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id The student ID.
	 *
	 * @return array An associative array containing assignment-related information.
	 */
	private function get_assignment_info( $student_id ) {
		global $wpdb;

		$total_assignments_count = 0;
		$completed_assignments   = 0;

		$course_ids = $this->get_enrolled_course_ids( $student_id );

		if ( empty( $course_ids ) ) {
			return array(
				'total_assignments_count' => 0,
				'completed_assignments'   => 0,
			);
		}

		foreach ( $course_ids as $course_id ) {
			$assignment_ids = $this->get_item_ids( $course_id, PostType::ASSIGNMENT );

			$total_assignments_count += count( $assignment_ids );
		}

		$completed_assignments = $this->get_completed_item( $student_id, 'assignment' );

		return array(
			'total_assignments_count' => absint( $total_assignments_count ),
			'completed_assignments'   => absint( $completed_assignments ),
		);
	}

	/**
	 * Get Enrolled Courses for a specific student.
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id The ID of the student.
	 * @return array An array containing information about the enrolled courses.
	 */
	private function get_enrolled_courses( $student_id ) {
		$enrolled_courses = masteriyo_get_user_enrolled_courses( $student_id );
		$courses          = array();

		foreach ( $enrolled_courses as $course ) {
			$course_id = $course->get_id();

			$total_lessons     = $this->get_item_ids( $course_id, PostType::LESSON );
			$completed_lessons = $this->get_completed_items( $student_id, $total_lessons, 'lesson' );

			$total_quizzes     = $this->get_item_ids( $course_id, PostType::QUIZ );
			$completed_quizzes = $this->get_completed_items( $student_id, $total_quizzes, 'quiz' );

			$total_assignments     = $this->get_item_ids( $course_id, PostType::ASSIGNMENT );
			$completed_assignments = $this->get_completed_items( $student_id, $total_assignments, 'assignment' );

			$total_questions         = $this->get_total_questions( $student_id, $course_id );
			$completed_questions     = $this->get_completed_questions( $student_id, $course_id );
			$total_incorrect_answers = $this->get_total_incorrect_answers( $student_id, $course_id );

			$courses[] = $this->prepare_course_info(
				$course,
				$student_id,
				$total_lessons,
				$completed_lessons,
				$total_quizzes,
				$completed_quizzes,
				$total_assignments,
				$completed_assignments,
				$total_questions,
				$completed_questions,
				$total_incorrect_answers
			);
		}

		return $courses;
	}

	/**
	 * Get the IDs of courses in which a user is enrolled.
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id The ID of the student.
	 *
	 * @return array The IDs of enrolled courses for the student, empty array on failure.
	 */
	private function get_enrolled_course_ids( $student_id ) {

		if ( ! is_int( $student_id ) || $student_id <= 0 ) {
			return array();
		}

		global $wpdb;

		$sql = "
    SELECT a.item_id
		FROM {$wpdb->prefix}masteriyo_user_activities a
		WHERE a.user_id = %d
		AND a.activity_type = 'course_progress'
		AND a.item_id IN (
			SELECT b.item_id FROM {$wpdb->prefix}masteriyo_user_items b WHERE b.status = 'active'
		)
		AND a.item_id IN (
			SELECT c.ID FROM {$wpdb->prefix}posts c WHERE c.post_status = 'publish'
		)
    ";

		$course_ids = $wpdb->get_col( $wpdb->prepare( $sql, $student_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

		if ( $wpdb->last_error ) {
			return array();
		}

		return $course_ids;
	}

	/**
	 * Get IDs of items (lessons, quizzes, assignments) for a specific course.
	 *
	 * @since 2.6.11
	 *
	 * @param int $course_id
	 * @param string $post_type
	 *
	 * @return array
	 */
	private function get_item_ids( $course_id, $post_type ) {
		return get_posts(
			array(
				'post_type'      => $post_type,
				'meta_key'       => '_course_id',
				'meta_value'     => $course_id,
				'posts_per_page' => -1,
				'fields'         => 'ids',
			)
		);
	}

	/**
	 * Get completed items for a specific student and course.
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id
	 * @param array $item_ids
	 * @param string $activity_type The type of activity (e.g., 'lesson', 'quiz', 'assignment').
	 *
	 * @return int
	 */
	private function get_completed_items( $student_id, $item_ids, $activity_type ) {
		global $wpdb;

		if ( empty( $item_ids ) ) {
			return 0;
		}

		$placeholders = implode( ', ', array_fill( 0, count( $item_ids ), '%d' ) );

		$sql = $wpdb->prepare(
			"SELECT COUNT(*)
         FROM {$wpdb->prefix}masteriyo_user_activities
         WHERE user_id = %d
         AND activity_type = %s
         AND activity_status = 'completed'",
			$student_id,
			$activity_type
		);

		$sql = str_replace(
			"AND activity_status = 'completed'",
			"AND item_id IN ( $placeholders ) AND activity_status = 'completed'",
			$sql
		);

		$sql = $wpdb->prepare( $sql, $item_ids ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

		return absint( $wpdb->get_var( $sql ) );  // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
	}

	/**
	 * Get the count of completed items for a specific student and activity type.
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id The ID of the student.
	 * @param string $activity_type The type of activity (e.g., 'lesson', 'quiz', 'assignment').
	 *
	 * @return int|false The count of completed items or false on error.
	 */
	private function get_completed_item( $student_id, $activity_type ) {
		if ( $student_id <= 0 || empty( $activity_type ) ) {
			return false;
		}

		global $wpdb;

		$sql = $wpdb->prepare(
			"SELECT COUNT(*)
         FROM {$wpdb->prefix}masteriyo_user_activities
         WHERE user_id = %d
         AND activity_type = %s
         AND activity_status = 'completed'",
			$student_id,
			$activity_type
		);

		$count = $wpdb->get_var( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared

		if ( $wpdb->last_error ) {
				return false;
		}

		return absint( $count );
	}

	/**
	 * Fetches total questions for a student in a course.
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id
	 * @param int $course_id
	 *
	 * @return int
	 */
	private function get_total_questions( $student_id, $course_id ) {
		global $wpdb;

		return $wpdb->get_var(
			$wpdb->prepare(
				"SELECT SUM(total_questions) FROM {$wpdb->prefix}masteriyo_quiz_attempts WHERE user_id = %d AND course_id = %d",
				$student_id,
				$course_id
			)
		);
	}

	/**
	 * Fetches completed questions for a student in a course.
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id
	 * @param int $course_id
	 *
	 * @return int
	 */
	private function get_completed_questions( $student_id, $course_id ) {
		global $wpdb;

		return $wpdb->get_var(
			$wpdb->prepare(
				"SELECT SUM(total_answered_questions) FROM {$wpdb->prefix}masteriyo_quiz_attempts WHERE user_id = %d AND course_id = %d",
				$student_id,
				$course_id
			)
		);
	}

	/**
	 * Fetches total incorrect answers for a student in a course.
	 *
	 * @since 2.6.11
	 *
	 * @param int $student_id
	 * @param int $course_id
	 *
	 * @return int
	 */
	private function get_total_incorrect_answers( $student_id, $course_id ) {
		global $wpdb;

		return $wpdb->get_var(
			$wpdb->prepare(
				"SELECT SUM(total_incorrect_answers) FROM {$wpdb->prefix}masteriyo_quiz_attempts WHERE user_id = %d AND course_id = %d",
				$student_id,
				$course_id
			)
		);
	}

	/**
	 * Prepares the course info to be returned.
	 *
	 * @since 2.6.11
	 *
	 * @param object $course
	 * @param int $student_id
	 * @param array $total_lessons
	 * @param int $completed_lessons
	 * @param array $total_quizzes
	 * @param int $completed_quizzes
	 * @param array $total_assignments
	 * @param int $completed_assignments
	 * @param int $total_questions
	 * @param int $completed_questions
	 * @param int $total_incorrect_answers
	 *
	 * @return array
	 */
	private function prepare_course_info( $course, $student_id, $total_lessons, $completed_lessons, $total_quizzes, $completed_quizzes, $total_assignments, $completed_assignments, $total_questions, $completed_questions, $total_incorrect_answers ) {
		return array(
			'id'                      => $course->get_id(),
			'name'                    => $course->get_name(),
			'progress'                => $course->get_progress_status( true, $student_id ),
			'status'                  => $course->get_status(),
			'date_created'            => masteriyo_rest_prepare_date_response( $course->get_date_created() ),
			'date_modified'           => masteriyo_rest_prepare_date_response( $course->get_date_modified() ),
			'price'                   => $course->get_price(),
			'price_type'              => $course->get_price_type(),
			'access_mode'             => $course->get_access_mode(),
			'started_at'              => masteriyo_rest_prepare_date_response( $course->progress->get_started_at() ),
			'total_lessons'           => count( $total_lessons ),
			'completed_lessons'       => absint( $completed_lessons ),
			'total_quizzes'           => count( $total_quizzes ),
			'completed_quizzes'       => absint( $completed_quizzes ),
			'total_assignments'       => count( $total_assignments ),
			'completed_assignments'   => absint( $completed_assignments ),
			'total_questions'         => absint( $total_questions ),
			'completed_questions'     => absint( $completed_questions ),
			'total_incorrect_answers' => absint( $total_incorrect_answers ),
		);
	}

	/**
	 * Check permission for fetching a student report.
	 *
	 * @since 2.6.11
	 *
	 * @param \WP_REST_Request $request Full details about the request, including the ID of the student.
	 *
	 * @return \WP_Error|boolean
	 */
	public function get_student_report_permission_check( \WP_REST_Request $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' )
			);
		}

		$user = get_user_by( 'id', (int) $request['id'] );

		if ( $user && ! $this->permission->rest_check_users_manipulation_permissions( 'read' ) ) {
			return new \WP_Error(
				'masteriyo_rest_cannot_read',
				__( 'Sorry, you are not allowed to read resources.', 'learning-management-system' ),
				array(
					'status' => rest_authorization_required_code(),
				)
			);
		}

		return true;
	}
}
