<?php
/**
 * Masteriyo Assignment setup.
 *
 * @package Masteriyo\Assignment
 *
 * @since 2.2.7
 */

namespace Masteriyo\Addons\Assignment;

use Error;
use Masteriyo\Addons\Assignment\Emails\NewAssignmentSubmissionEmailToAdmin;
use Masteriyo\Addons\Assignment\Emails\NewAssignmentSubmissionEmailToInstructor;
use Masteriyo\Addons\Assignment\Enums\AssignmentReplyStatus;
use Masteriyo\Addons\Assignment\Listener\NewAssignmentListener;
use Masteriyo\Addons\Assignment\Listener\NewAssignmentReplyListener;
use Masteriyo\Addons\Assignment\RestApi\AssignmentsController;
use Masteriyo\Addons\Assignment\RestApi\AssignmentRepliesController;
use Masteriyo\Enums\PostStatus;
use Masteriyo\PostType\PostType;
use Masteriyo\Query\UserCourseQuery;

defined( 'ABSPATH' ) || exit;

/**
 * Main Masteriyo Assignment class.
 *
 * @class Masteriyo\Addons\Assignment\AssignmentAddon
 */

class AssignmentAddon {
	/**
	 * Initialize the application.
	 *
	 * @since 2.3.5
	 */
	public function init() {
		$this->init_hooks();
	}

	/**
	 * Initialize hooks.
	 *
	 * @since 2.3.5
	 */
	public function init_hooks() {
		add_filter( 'masteriyo_admin_submenus', array( $this, 'add_assignment_submenu' ) );
		add_filter( 'masteriyo_register_post_types', array( $this, 'register_assignment_post_type' ) );
		add_filter( 'masteriyo_rest_api_get_rest_namespaces', array( $this, 'register_rest_namespaces' ) );
		add_action( 'masteriyo_single_course_before_section_content', array( $this, 'maybe_show_assignment_in_section_contents' ), 10, 3 );

		add_action(
			'wp_enqueue_scripts',
			function() {
				if ( masteriyo_is_learn_page() ) {
					wp_enqueue_editor();
				}
			}
		);

		add_filter( 'masteriyo_course_children_post_types', array( $this, 'include_assignment_post_type' ) );
		add_filter( 'masteriyo_course_progress_post_types', array( $this, 'include_assignment_post_type' ) );
		add_filter( 'masteriyo_section_children_post_types', array( $this, 'include_assignment_post_type' ) );

		add_filter( 'masteriyo_course_progress_item_types', array( $this, 'include_assignment_item_type' ) );
		add_filter( 'masteriyo_section_children_item_types', array( $this, 'include_assignment_item_type' ) );

		add_filter( 'masteriyo_single_course_curriculum_summaries', array( $this, 'include_assignment_in_curriculum_summary' ), 10, 3 );
		add_filter( 'masteriyo_single_course_curriculum_section_summaries', array( $this, 'include_assignment_in_curriculum_section_summary' ), 10, 4 );
		add_filter( 'masteriyo_rest_pre_insert_course_object', array( $this, 'save_course_data' ), 10, 3 );
		add_filter( 'masteriyo_rest_response_course_data', array( $this, 'append_course_data_in_response' ), 10, 3 );
		add_action( 'masteriyo_update_assignment_reply', array( $this, 'schedule_assignment_reply_update_notification_to_student' ), 10, 2 );

		add_filter( 'masteriyo_webhook_listeners', array( $this, 'register_listener_class' ) );

		add_filter( 'masteriyo_new_setting', array( $this, 'save_admin_setting' ), 10 );
		add_filter( 'masteriyo_rest_response_setting_data', array( $this, 'append_admin_setting_in_response' ), 10, 4 );

		add_filter( 'masteriyo_new_setting', array( $this, 'save_instructor_setting' ), 10 );
		add_filter( 'masteriyo_rest_response_setting_data', array( $this, 'append_instructor_setting_in_response' ), 10, 4 );

		add_action( 'masteriyo_new_assignment_reply', array( $this, 'schedule_new_assignment_reply_email_to_admin' ), 10, 2 );
		add_action( 'masteriyo_new_assignment_reply', array( $this, 'schedule_new_assignment_reply_email_to_instructor' ), 10, 2 );

		add_action( 'masteriyo_layout_1_single_course_curriculum_shortinfo_item', array( $this, 'shortinfo_item' ), 10, 1 );
		add_action( 'masteriyo_layout_1_single_course_curriculum_accordion_header_info_item', array( $this, 'header_info_item' ), 10, 1 );

		add_filter( 'masteriyo_courses_analytics_data', array( $this, 'append_assignment_data_in_response' ), 10, 4 );
		add_filter( 'masteriyo_post_type_default_labels', array( $this, 'append_post_type_default_label' ), 10 );
	}

	/**
	 * Add post type default label.
	 *
	 * @since 2.17.0
	 *
	 * @param string $post_type Post type slug.
	 *
	 * @return array default labels.
	 */
	public function append_post_type_default_label( $original_labels ) {
		$original_labels[ PostType::ASSIGNMENT ] = 'Assignments';

		return $original_labels;
	}

	/**
	 * Append course assignment count to course analytics resource.
	 *
	 * @since 2.14.4
	 *
	 * @param array $data Course data.
	 * @param \Masteriyo\Models\Course $course Course object.
	 * @param string $context What the value is for. Valid values are view and edit.
	 * @param \Masteriyo\RestApi\Controllers\Version1\CoursesController $controller REST courses controller object.
	 *
	 * @return array Course data.
	 */
	public function append_assignment_data_in_response( $data, $course, $context, $controller ) {
		$assignment_count         = $this->get_assignment_data( $course->get_id() );
		$data['assignment_count'] = $assignment_count['total'];

		return $data;
	}

	/**
	 * Get assignment count.
	 *
	 * @since 2.14.4
	 *
	 * @param array $course_ids Course IDs.
	 *
	 * @return array
	 */
	protected function get_assignment_data( $course_ids ) {
		$data = array(
			'total' => 0,
		);

		if ( $course_ids ) {
			$query         = new \WP_Query(
				array(
					'post_status'    => PostStatus::PUBLISH,
					'post_type'      => PostType::ASSIGNMENT,
					'posts_per_page' => 1,
					'meta_query'     => array(
						array(
							'key'     => '_course_id',
							'value'   => $course_ids,
							'compare' => 'IN',
						),
					),
					'fields'         => 'ids',
				)
			);
			$data['total'] = $query->found_posts;
		}

		return $data;
	}

	/**
	 * Displays a short information item for the Assignment addon.
	 *
	 * This function generates an HTML span element that displays the number of Assignment meetings
	 * associated with the given section.
	 *
	 * @since 2.10.0
	 *
	 * @param \Masteriyo\Models\Section $section The section object.
	 */
	public function header_info_item( $section ) {
		if ( ! $section instanceof \Masteriyo\Models\Section ) {
			return;
		}

		$assignment_count = get_course_section_children_count_by_section( $section->get_id(), PostType::ASSIGNMENT );

		if ( 0 === $assignment_count ) {
			return;
		}

		$html  = '<span class="masteriyo-single-body-accordion-info">';
		$html .= sprintf(
			/* translators: %1$s: Assignments count */
			esc_html( _nx( '%1$s Assignment', '%1$s Assignments', $assignment_count, 'Assignments Count', 'learning-management-system' ) ),
			esc_html( number_format_i18n( $assignment_count ) )
		);
		$html .= '</span>';

		echo $html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Displays a short information item for the Assignment addon.
	 *
	 * This function generates an HTML list item that displays the number of Assignment meetings
	 * associated with the given course.
	 *
	 * @since 2.10.0
	 *
	 * @param \Masteriyo\Models\Course $course The course object.
	 */
	public function shortinfo_item( $course ) {
		if ( ! $course instanceof \Masteriyo\Models\Course ) {
			return;
		}

		$assignment_count = get_course_section_children_count_by_course( $course->get_id(), PostType::ASSIGNMENT );

		if ( 0 === $assignment_count ) {
			return;
		}

		$html  = '<li class="masteriyo-single-body__main--curriculum-content-top--shortinfo-item">';
		$html .= sprintf(
			/* translators: %1$s: Assignments count */
			esc_html( _nx( '%1$s Assignment', '%1$s Assignments', $assignment_count, 'Assignments Count', 'learning-management-system' ) ),
			esc_html( number_format_i18n( $assignment_count ) )
		);
		$html .= '</li>';

		echo $html; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
	}

	/**
	 * Get the assignment completion and retake option to the single course.
	 *
	 * If the request is send from front end it will take that value otherwise it will take false as default.
	 *
	 * @since 2.8.3
	 *
	 * @param Masteriyo\Models\Course $course  Course object.
	 * @param WP_REST_Request $request  Request object.
	 * @param bool            $creating If is creating a new object.
	 */
	public function append_course_data_in_response( $data, $course, $context ) {

		$data['assignment_completion'] = $course->get_meta( '_assignment_completion' ) ?? false;
		$data['assignment_retake']     = $course->get_meta( '_assignment_retake' ) ?? false;

		return $data;
	}

	/**
	 * Save admin setting.
	 *
	 * Saves Assignment email for admin settings based on the request data.
	 *
	 * @since 2.12.0
	 */
	public function save_admin_setting() {
		$request = masteriyo_current_http_request();

		if ( ! masteriyo_is_rest_api_request() ) {
			return;
		}

		if ( ! isset( $request['emails']['admin']['new_assignment_submission'] ) ) {
			return;
		}

		$settings = masteriyo_array_only( $request['emails']['admin']['new_assignment_submission'], array_keys( InstructorEmailSetting::all() ) );
		$settings = masteriyo_parse_args( $settings, AdminEmailSetting::all() );

		$settings = $request['emails']['admin']['new_assignment_submission'];
		$settings = masteriyo_parse_args( $settings, AdminEmailSetting::all() );

		// Sanitization.
		$settings['enable']           = masteriyo_string_to_bool( $settings['enable'] );
		$settings['subject']          = sanitize_textarea_field( $settings['subject'] );
		$settings['from_address']     = sanitize_text_field( $settings['from_address'] );
		$settings['from_name']        = sanitize_text_field( $settings['from_name'] );
		$settings['reply_to_address'] = sanitize_text_field( $settings['reply_to_address'] );
		$settings['reply_to_name']    = sanitize_text_field( $settings['reply_to_name'] );
		$settings['to_address']       = sanitize_text_field( $settings['to_address'] );
		$settings['content']          = wp_kses_post( $settings['content'] );

		$recipients = $settings['recipients'];

		$settings['recipients'] = is_array( $recipients ) ? array_filter(
			array_map(
				function( $receiver ) {
					if ( empty( $receiver ) ) {
						return null;
					}
					return sanitize_text_field( $receiver );
				},
				$recipients
			)
		) : array();

		AdminEmailSetting::set_props( $settings );

		AdminEmailSetting::save();
	}

	/**
	 * Save setting for instructor.
	 *
	 * Saves assignment email settings for instructor based on the request data.
	 *
	 * @since 2.12.0
	 */
	public function save_instructor_setting() {
		$request = masteriyo_current_http_request();

		if ( ! masteriyo_is_rest_api_request() ) {
			return;
		}

		if ( ! isset( $request['emails']['instructor']['new_assignment_submission'] ) ) {
			return;
		}

		$settings = masteriyo_array_only( $request['emails']['instructor']['new_assignment_submission'], array_keys( InstructorEmailSetting::all() ) );
		$settings = masteriyo_parse_args( $settings, InstructorEmailSetting::all() );

		$settings = $request['emails']['instructor']['new_assignment_submission'];
		$settings = masteriyo_parse_args( $settings, InstructorEmailSetting::all() );

		// Sanitization.
		$settings['enable']           = masteriyo_string_to_bool( $settings['enable'] );
		$settings['subject']          = sanitize_textarea_field( $settings['subject'] );
		$settings['from_address']     = sanitize_text_field( $settings['from_address'] );
		$settings['from_name']        = sanitize_text_field( $settings['from_name'] );
		$settings['reply_to_address'] = sanitize_text_field( $settings['reply_to_address'] );
		$settings['reply_to_name']    = sanitize_text_field( $settings['reply_to_name'] );
		$settings['to_address']       = sanitize_text_field( $settings['to_address'] );
		$settings['content']          = wp_kses_post( $settings['content'] );

		$recipients = $settings['recipients'];

		$settings['recipients'] = is_array( $recipients ) ? array_filter(
			array_map(
				function( $receiver ) {
					if ( empty( $receiver ) ) {
						return null;
					}
					return sanitize_text_field( $receiver );
				},
				$recipients
			)
		) : array();

		InstructorEmailSetting::set_props( $settings );

		InstructorEmailSetting::save();
	}

	/**
	 * Adds assignment admin email settings to the global settings response.
	 *
	 * @since 2.12.0
	 *
	 * @param array $data The current settings data.
	 * @param \Masteriyo\Models\Setting $setting The settings object.
	 * @param string $context The context in which the settings are being retrieved.
	 * @param \Masteriyo\RestApi\Controllers\Version1\SettingsController $controller The REST settings controller object.
	 *
	 * @return array The modified settings data with assignment settings appended.
	 */
	public function append_admin_setting_in_response( $data, $object, $request, $controller ) {
		$data['emails']['admin']['new_assignment_submission'] = wp_parse_args( AdminEmailSetting::all() );

		return $data;
	}


	/**
	 * Adds assignment instructor email settings to the global settings response.
	 *
	 * @since 2.12.0
	 *
	 * @param array $data The current settings data.
	 * @param \Masteriyo\Models\Setting $setting The settings object.
	 * @param string $context The context in which the settings are being retrieved.
	 * @param \Masteriyo\RestApi\Controllers\Version1\SettingsController $controller The REST settings controller object.
	 *
	 * @return array The modified settings data with assignment settings appended.
	 */
	public function append_instructor_setting_in_response( $data, $object, $request, $controller ) {
		$data['emails']['instructor']['new_assignment_submission'] = wp_parse_args( InstructorEmailSetting::all() );

		return $data;
	}

	/**
	 * Adds the assignment completion and retake option to the single course.
	 *
	 * If the request is send from front end it will take that value otherwise it will take false as default.
	 *
	 * @since 2.8.3
	 *
	 * @param Masteriyo\Models\Course $course  Course object.
	 * @param WP_REST_Request $request  Request object.
	 * @param bool            $creating If is creating a new object.
	 */
	public function save_course_data( $course, $request, $creating ) {

		if ( isset( $request['assignment_completion'] ) ) {
			$course->update_meta_data( '_assignment_completion', $request['assignment_completion'] );
		}

		if ( isset( $request['assignment_retake'] ) ) {
			$course->update_meta_data( '_assignment_retake', $request['assignment_retake'] );
		}

		$course->save_meta_data();

		return $course;
	}

	/**
	 * Schedule new assignment reply email for admin.
	 *
	 * @since 2.12.0
	 *
	 * @param \Masteriyo\Models\AssignmentReply $order Order object.
	 * @param \Masteriyo\Repository\AssignmentReplyRepository $repository THe data store persisting the data.
	 *
	 */
	public static function schedule_new_assignment_reply_email_to_admin( $id, $assignment_reply ) {
		$email = new NewAssignmentSubmissionEmailToAdmin();

		if ( ! $email->is_enabled() ) {
			return;
		}

		$email->trigger( $assignment_reply->get_id() );
	}



	/**
	 * Schedule new assignment reply email for instructor.
	 *
	 * @since 2.12.0
	 *
	 * @param \Masteriyo\Models\AssignmentReply $id .
	 * @param \Masteriyo\Repository\AssignmentReplyRepository $repository THe data store persisting the data.
	 *
	 */
	public static function schedule_new_assignment_reply_email_to_instructor( $id, $assignment_reply ) {
		$email = new NewAssignmentSubmissionEmailToInstructor();

		if ( ! $email->is_enabled() ) {
			return;
		}

		$email->trigger( $assignment_reply->get_id() );
	}

	/**
	 * Schedule assignment reply notification to student.
	 *
	 * @since 2.8.0
	 *
	 * @param int $id Assignment Reply id.
	 * @param \Masteriyo\Models\Assignment_Reply $object The user course object.
	 * @param \Masteriyo\Models\assignment_reply $course_qa
	 */
	public function schedule_assignment_reply_update_notification_to_student( $id, $assignment_reply ) {

		$result = masteriyo_get_setting( 'notification.student.assignment_reply' );

		if ( isset( $result['enable'] ) && ! $result['enable'] ) {
			return;
		}

		if ( AssignmentReplyStatus::REVIEWED !== $assignment_reply->get_status() ) {
			return;
		}

		$assignment_data = masteriyo_get_assignment( $assignment_reply->get_assignment_id() );

		if ( ! isset( $assignment_data ) ) {
			return;
		}

		$query = new UserCourseQuery(
			array(
				'course_id' => $assignment_data->get_course_id(),
				'user_id'   => $assignment_reply->get_user_id(),
			)
		);

		$user_courses = $query->get_user_courses();

		masteriyo_set_notification( $id, current( $user_courses ), $result );
	}

	/**
	 * Register Listeners.
	 *
	 * @since 2.8.3
	 *
	 * @param array $listeners
	 * @return array
	 */
	public function register_listener_class( $listeners ) {
		$listeners[] = NewAssignmentListener::class;
		$listeners[] = NewAssignmentReplyListener::class;

		return $listeners;
	}

	/**
	 * Include assignment in single course curriculum.
	 *
	 * @since 2.3.5
	 *
	 * @param array $summaries Section summaries.
	 * @param \Masteriyo\Models\Course $course Course object.
	 * @param \Masteriyo\Models\Section $section Section object.
	 * @param \WP_Post[] $posts Children of section (lessons and quizzes).

	 * @return array
	 */
	public function include_assignment_in_curriculum_section_summary( $summaries, $course, $section, $posts ) {
		$assignment_count = array_reduce(
			$posts,
			function( $count, $post ) {
				if ( 'mto-assignment' === $post->post_type ) {
					++$count;
				}

				return $count;
			},
			0
		);

		if ( 0 === $assignment_count ) {
			return $summaries;
		}

		$assignment_summary = array(
			array(
				'wrapper_start' => '<span class="masteriyo-assignments-count">',
				'wrapper_end'   => '</span>',
				'content'       => sprintf(
					/* translators: %d: Course assignments count */
					esc_html( _nx( '%s Assignment', '%s Assignments', $assignment_count, 'Assignments Count', 'learning-management-system' ) ),
					esc_html( number_format_i18n( $assignment_count ) )
				),
			),
		);

		// @see https://stackoverflow.com/questions/3797239/insert-new-item-in-array-on-any-position-in-php
		array_splice( $summaries, 2, 0, $assignment_summary );

		return $summaries;
	}
	/**
	 * Include assignment in single course curriculum.
	 *
	 * @since 2.3.5
	 *
	 * @param array $summaries Summaries.
	 * @param \Masteriyo\Models\Course $course Course object.
	 * @param WP_Post[] $posts Array of sections, quizzes and sections.

	 * @return array
	 */
	public function include_assignment_in_curriculum_summary( $summaries, $course, $posts ) {
		$assignment_count = array_reduce(
			$posts,
			function( $count, $post ) {
				if ( 'mto-assignment' === $post->post_type ) {
					++$count;
				}

				return $count;
			},
			0
		);

		if ( 0 === $assignment_count ) {
			return $summaries;
		}

		$assignment_summary = array(
			array(
				'wrapper_start' => '<li>',
				'wrapper_end'   => '</li>',
				'content'       => sprintf(
					/* translators: %d: Course assignments count */
					esc_html( _nx( '%s Assignment', '%s Assignments', $assignment_count, 'Assignments Count', 'learning-management-system' ) ),
					esc_html( number_format_i18n( $assignment_count ) )
				),
			),
		);

		// @see https://stackoverflow.com/questions/3797239/insert-new-item-in-array-on-any-position-in-php
		array_splice( $summaries, 3, 0, $assignment_summary );

		return $summaries;
	}

	/**
	 * Include assignment item type.
	 *
	 * @since 2.3.5
	 *
	 * @param array $types Item types.
	 * @return array
	 */
	public function include_assignment_item_type( $types ) {

		if ( masteriyo_is_single_course_page() || is_user_logged_in() ) {
			return array_merge( $types, array( 'assignment' ) );
		}

		return $types;
	}

	/**
	 * Include assignment post type.
	 *
	 * @since 2.3.5
	 *
	 * @param array $types post types.
	 * @return array
	 */
	public function include_assignment_post_type( $types ) {

		if ( masteriyo_is_single_course_page() || is_user_logged_in() ) {
			return array_merge( $types, array( 'mto-assignment' ) );
		}

		return $types;
	}

	/**
	 * Register namespaces.
	 *
	 * @since 2.3.5
	 *
	 * @param array $namespaces
	 * @return array
	 */
	public function register_rest_namespaces( $namespaces ) {
		$namespaces['masteriyo/pro/v1']['assignment']         = AssignmentsController::class;
		$namespaces['masteriyo/pro/v1']['assignment/replies'] = AssignmentRepliesController::class;
		return $namespaces;
	}

	/**
	 * Register assignment post type.
	 *
	 * @since 2.3.5
	 *
	 * @param array $post_types
	 * @return array
	 */
	public function register_assignment_post_type( $post_types ) {
		$post_types['assignment']       = 'Masteriyo\Addons\Assignment\PostType\Assignment';
		$post_types['assignment-reply'] = 'Masteriyo\Addons\Assignment\PostType\AssignmentReply';

		return $post_types;
	}

	/**
	 * Add assignment submenu.
	 *
	 * @since 2.3.5
	 */
	public function add_assignment_submenu( $submenus ) {
		$submenus['assignment-submissions'] = array(
			'page_title' => __( 'Assignment Submissions', 'learning-management-system' ),
			'menu_title' => __( 'Assignment Submissions', 'learning-management-system' ),
			'capability' => 'read_assignment_replies',
			'position'   => 67,
		);

		return $submenus;
	}
}
