<?php
defined('ABSPATH') || die();

require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/WLSM_M_School.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/WLSM_M_Role.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/WLSM_Config.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/WLSM_Notify.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/WLSM_M_Admin.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/WLSM_Helper.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/WLSM_Email.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/staff/WLSM_M_Staff_Class.php';
require_once WLSM_PLUGIN_DIR_PATH . 'includes/helpers/staff/WLSM_M_Staff_General.php';

class WLSM_Staff_General
{
	public static function get_class_sections()
	{
		$current_user = WLSM_M_Role::can(array('manage_admissions', 'manage_students', 'manage_invoices'));

		if (! $current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		try {
			ob_start();
			global $wpdb;

			if (! wp_verify_nonce($_POST['nonce'], 'get-class-sections')) {
				die();
			}

			$class_id = isset($_POST['class_id']) ? absint($_POST['class_id']) : 0;

			$all_sections = isset($_POST['all_sections']) ? absint($_POST['all_sections']) : 0;

			// Checks if class exists in the school.
			$class_school = WLSM_M_Staff_Class::get_class($school_id, $class_id);

			if (! $class_school) {
				throw new Exception(esc_html__('Class not found.', 'school-management-system'));
			}

			$class_school_id = $class_school->ID;

			$sections = WLSM_M_Staff_General::fetch_class_sections($class_school_id);

			if ($all_sections) {
				$all_sections = (object) array('ID' => '', 'label' => esc_html__('All Sections', 'school-management-system'));
				array_unshift($sections, $all_sections);
			}

			$sections = array_map(function ($section) {
				$section->label = WLSM_M_Staff_Class::get_section_label_text($section->label);
				return $section;
			}, $sections);

			wp_send_json($sections);
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json(array());
		}
	}

	public static function get_section_students()
	{
		$current_user = WLSM_M_Role::can(array('manage_invoices'));

		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			if (! wp_verify_nonce($_POST['nonce'], 'get-section-students')) {
				die();
			}

			$class_id   = isset($_POST['class_id']) ? absint($_POST['class_id']) : 0;
			$section_id = isset($_POST['section_id']) ? absint($_POST['section_id']) : 0;

			$only_active = isset($_POST['only_active']) ? (bool) $_POST['only_active'] : 1;

			// Checks if class exists in the school.
			$class_school = WLSM_M_Staff_Class::get_class($school_id, $class_id);

			if (! $class_school) {
				throw new Exception(esc_html__('Class not found.', 'school-management-system'));
			}

			$class_school_id = $class_school->ID;

			if ($section_id) {
				// Checks if section exists.
				$section = WLSM_M_Staff_Class::get_section($school_id, $section_id, $class_school_id);

				if (! $section) {
					throw new Exception(esc_html__('Section not found.', 'school-management-system'));
				}

				$students = WLSM_M_Staff_General::fetch_section_students($session_id, $section->ID, $only_active);
			} else {
				$students = WLSM_M_Staff_General::fetch_class_students($session_id, $class_school_id, $only_active);
			}

			wp_send_json($students);
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json(array());
		}
	}

	public static function add_admission()
	{
		$current_user = WLSM_M_Role::can('manage_admissions');
		self::save_student($current_user);
	}

	public static function edit_student()
	{
		$current_user = WLSM_M_Role::can('manage_students');
		self::save_student($current_user);
	}

	public static function save_student($current_user)
	{
		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		$page_url = WLSM_M_Staff_General::get_students_page_url();

		try {
			ob_start();
			global $wpdb;

			$student_id = isset($_POST['student_id']) ? absint($_POST['student_id']) : 0;

			if ($student_id) {
				if (! wp_verify_nonce($_POST['edit-student-' . $student_id], 'edit-student-' . $student_id)) {
					die();
				}
			} else {
				if (! wp_verify_nonce($_POST['add-admission'], 'add-admission')) {
					die();
				}
			}

			$user_id = NULL;

			// Checks if student exists.
			if ($student_id) {
				$student = WLSM_M_Staff_General::get_student($school_id, $session_id, $student_id);

				if (! $student) {
					throw new Exception(esc_html__('Student not found.', 'school-management-system'));
				}

				$user_id = $student->user_id;
			}

			// Personal Detail.
			$name          = isset($_POST['name']) ? sanitize_text_field($_POST['name']) : '';
			$gender        = isset($_POST['gender']) ? sanitize_text_field($_POST['gender']) : '';
			$dob           = isset($_POST['dob']) ? DateTime::createFromFormat(WLSM_Config::date_format(), sanitize_text_field($_POST['dob'])) : NULL;
			$address       = isset($_POST['address']) ? sanitize_text_field($_POST['address']) : '';
			$email         = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
			$phone         = isset($_POST['phone']) ? sanitize_text_field($_POST['phone']) : '';
			$religion      = isset($_POST['religion']) ? sanitize_text_field($_POST['religion']) : '';
			$caste         = isset($_POST['caste']) ? sanitize_text_field($_POST['caste']) : '';
			$blood_group   = isset($_POST['blood_group']) ? sanitize_text_field($_POST['blood_group']) : '';

			// Admission Detail.
			$admission_date    = isset($_POST['admission_date']) ? DateTime::createFromFormat(WLSM_Config::date_format(), sanitize_text_field($_POST['admission_date'])) : NULL;
			$class_id          = isset($_POST['class_id']) ? absint($_POST['class_id']) : 0;
			$section_id        = isset($_POST['section_id']) ? absint($_POST['section_id']) : 0;
			$admission_number  = isset($_POST['admission_number']) ? sanitize_text_field($_POST['admission_number']) : '';
			$roll_number       = isset($_POST['roll_number']) ? sanitize_text_field($_POST['roll_number']) : '';
			$photo             = (isset($_FILES['photo']) && is_array($_FILES['photo'])) ? $_FILES['photo'] : NULL;

			// Parent Detail.
			$father_name       = isset($_POST['father_name']) ? sanitize_text_field($_POST['father_name']) : '';
			$father_phone      = isset($_POST['father_phone']) ? sanitize_text_field($_POST['father_phone']) : '';
			$father_occupation = isset($_POST['father_occupation']) ? sanitize_text_field($_POST['father_occupation']) : '';
			$mother_name       = isset($_POST['mother_name']) ? sanitize_text_field($_POST['mother_name']) : '';
			$mother_phone      = isset($_POST['mother_phone']) ? sanitize_text_field($_POST['mother_phone']) : '';
			$mother_occupation = isset($_POST['mother_occupation']) ? sanitize_text_field($_POST['mother_occupation']) : '';

			// Student Login Detail.
			$new_or_existing   = isset($_POST['student_new_or_existing']) ? sanitize_text_field($_POST['student_new_or_existing']) : '';
			$existing_username = isset($_POST['existing_username']) ? sanitize_text_field($_POST['existing_username']) : '';
			$new_login_email   = isset($_POST['new_login_email']) ? sanitize_text_field($_POST['new_login_email']) : '';
			$new_password      = isset($_POST['new_password']) ? sanitize_text_field($_POST['new_password']) : '';
			$username          = isset($_POST['username']) ? sanitize_text_field($_POST['username']) : '';
			$login_email       = isset($_POST['login_email']) ? sanitize_text_field($_POST['login_email']) : '';
			$password          = isset($_POST['password']) ? $_POST['password'] : '';

			// Status.
			$is_active = isset($_POST['is_active']) ? (bool) $_POST['is_active'] : 1;

			if ($student_id) {
				$class_id = $student->class_id;
			} else {
				$inquiry_id = isset($_POST['inquiry_id']) ? absint($_POST['inquiry_id']) : 0;
			}

			// Start validation.
			$errors = array();

			// Personal Detail.
			if (empty($name)) {
				$errors['name'] = esc_html__('Please specify student name.', 'school-management-system');
			}
			if (strlen($name) > 60) {
				$errors['name'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
			}
			if (! empty($religion) && strlen($religion) > 40) {
				$errors['religion'] = esc_html__('Maximum length cannot exceed 40 characters.', 'school-management-system');
			}
			if (! empty($caste) && strlen($caste) > 40) {
				$errors['caste'] = esc_html__('Maximum length cannot exceed 40 characters.', 'school-management-system');
			}
			if (! empty($phone) && strlen($phone) > 40) {
				$errors['phone'] = esc_html__('Maximum length cannot exceed 40 characters.', 'school-management-system');
			}
			if (! empty($email)) {
				if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
					$errors['email'] = esc_html__('Please provide a valid email.', 'school-management-system');
				} elseif (strlen($email) > 60) {
					$errors['email'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
				}
			}
			if (! in_array($gender, array_keys(WLSM_Helper::gender_list()))) {
				throw new Exception(esc_html__('Please specify gender.', 'school-management-system'));
			}
			if (! empty($blood_group) && ! in_array($blood_group, array_keys(WLSM_Helper::blood_group_list()))) {
				throw new Exception(esc_html__('Please specify blood group.', 'school-management-system'));
			}
			if (! empty($dob)) {
				$dob = $dob->format('Y-m-d');
			} else {
				$dob = NULL;
			}

			// Admission Detail.
			if (empty($admission_date)) {
				$errors['admission_date'] = esc_html__('Please provide admission date.', 'school-management-system');
			} else {
				$admission_date = $admission_date->format('Y-m-d');
			}
			if (empty($admission_number)) {
				$errors['admission_number'] = esc_html__('Please provide admission number.', 'school-management-system');
			}
			if (strlen($admission_number) > 60) {
				$errors['admission_number'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
			}
			if (! empty($roll_number) && strlen($roll_number) > 30) {
				$errors['roll_number'] = esc_html__('Maximum length cannot exceed 30 characters.', 'school-management-system');
			}
			if (! $student_id) {
				if (empty($class_id)) {
					$errors['class_id'] = esc_html__('Please select a class.', 'school-management-system');
					wp_send_json_error($errors);
				}
			}
			if (empty($section_id)) {
				$errors['section_id'] = esc_html__('Please select section.', 'school-management-system');
				wp_send_json_error($errors);
			}
			if (isset($photo['tmp_name']) && ! empty($photo['tmp_name'])) {
				$finfo = finfo_open(FILEINFO_MIME_TYPE);
				$mime  = finfo_file($finfo, $photo['tmp_name']);
				finfo_close($finfo);

				if (! in_array($mime, WLSM_Helper::get_image_mime())) {
					$errors['photo'] = esc_html__('Please provide photo in JPG, JPEG or PNG format.', 'school-management-system');
				}
			}

			// Parent Detail.
			if (! empty($father_name) && strlen($father_name) > 60) {
				$errors['father_name'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
			}
			if (! empty($father_phone) && strlen($father_phone) > 40) {
				$errors['father_phone'] = esc_html__('Maximum length cannot exceed 40 characters.', 'school-management-system');
			}
			if (! empty($father_occupation) && strlen($father_occupation) > 60) {
				$errors['father_occupation'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
			}
			if (! empty($mother_name) && strlen($mother_name) > 60) {
				$errors['mother_name'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
			}
			if (! empty($mother_phone) && strlen($mother_phone) > 40) {
				$errors['mother_phone'] = esc_html__('Maximum length cannot exceed 40 characters.', 'school-management-system');
			}
			if (! empty($mother_occupation) && strlen($mother_occupation) > 60) {
				$errors['mother_occupation'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
			}

			// Checks if class exists in the school.
			if ($student_id) {
				$class_school_id = $student->class_school_id;
			} else {
				$class_school = WLSM_M_Staff_Class::get_class($school_id, $class_id);
				if (! $class_school) {
					$errors['class_id'] = esc_html__('Class not found.', 'school-management-system');
					wp_send_json_error($errors);
				}

				$class_school_id = $class_school->ID;
			}

			// Checks if section exists.
			$section = WLSM_M_Staff_Class::get_section($school_id, $section_id, $class_school_id);
			if (! $section) {
				$errors['section_id'] = esc_html__('Section not found.', 'school-management-system');
				wp_send_json_error($errors);
			}

			// Checks if admission number already exists for this session.
			if (! $student_id) {
				$student_exists = WLSM_M_Staff_General::get_admitted_student_id($school_id, $session_id, $admission_number);

				if ($student_exists) {
					$errors['admission_number'] = esc_html__('Admission number already exists in this session.', 'school-management-system');
				}
			}

			// Checks if roll number already exists in the class for this session.
			if (! empty($roll_number)) {
				if ($student_id) {
					$student_exists = WLSM_M_Staff_General::get_student_with_roll_number($school_id, $session_id, $class_id, $roll_number, $student_id);
				} else {
					$student_exists = WLSM_M_Staff_General::get_student_with_roll_number($school_id, $session_id, $class_id, $roll_number);
				}

				if ($student_exists) {
					$errors['roll_number'] = esc_html__('Roll number already exists in this class.', 'school-management-system');
				}
			}

			// Student Login Detail.
			if ('existing_user' === $new_or_existing) {
				if (! $user_id) {
					if (empty($existing_username)) {
						$errors['existing_username'] = esc_html__('Please provide existing username.', 'school-management-system');
					}
				} else {
					if (empty($new_login_email)) {
						$errors['new_login_email'] = esc_html__('Please provide login email.', 'school-management-system');
					}
				}
			} elseif ('new_user' === $new_or_existing) {
				if (empty($username)) {
					$errors['username'] = esc_html__('Please provide username.', 'school-management-system');
				}
				if (empty($login_email)) {
					$errors['login_email'] = esc_html__('Please provide login email.', 'school-management-system');
				}
				if (! filter_var($login_email, FILTER_VALIDATE_EMAIL)) {
					$errors['login_email'] = esc_html__('Please provide a valid email.', 'school-management-system');
				}
				if (empty($password)) {
					$errors['password'] = esc_html__('Please provide login password.', 'school-management-system');
				}
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			try {
				$wpdb->query('BEGIN;');

				if ($student_id) {
					$message = esc_html__('Student updated successfully.', 'school-management-system');
				} else {
					$message = esc_html__('Admission added successfully.', 'school-management-system');
				}

				// Student user data.
				$update_student_user_id = NULL;
				if ('existing_user' === $new_or_existing) {
					if (! $user_id) {
						// Existing user.
						$user = get_user_by('login', $existing_username);
						if (! $user) {
							throw new Exception(esc_html__('Username does not exist.', 'school-management-system'));
						}

						$user_id = $user->ID;

						// Check if user already has a student record.
						if ($student_id) {
							$user_has_student_record = $wpdb->get_row($wpdb->prepare('SELECT sr.ID FROM %i as sr WHERE sr.user_id = %d AND sr.ID != %d', WLSM_STUDENT_RECORDS, $user_id, $student_id));
						} else {
							$user_has_student_record = $wpdb->get_row($wpdb->prepare('SELECT sr.ID FROM %i as sr WHERE sr.user_id = %d', WLSM_STUDENT_RECORDS, $user_id));
						}

						if ($user_has_student_record) {
							throw new Exception(esc_html__('The user already has a student record.', 'school-management-system'));
						}

						if (! $student_id) {
							$staff = WLSM_M_Admin::staff_in_school($school_id, $user_id);

							if ($staff) {
								throw new Exception(
									/* translators: %s: role */
									sprintf(esc_html__('User already exists with this username having a role of "%s".', 'school-management-system'), WLSM_M_Role::get_role_text($staff->role))
								);
							}

							if (user_can($user_id, WLSM_ADMIN_CAPABILITY)) {
								throw new Exception(esc_html__('User is a multi-school administrator.', 'school-management-system'));
							}
						}
					} else {
						// Update email and password of existing user.
						$user_data = array(
							'ID'         => $user_id,
							'user_email' => $new_login_email,
						);

						if (! empty($new_password)) {
							$user_data['user_pass'] = $new_password;
						}

						$user_id = wp_update_user($user_data);
						if (is_wp_error($user_id)) {
							throw new Exception($user_id->get_error_message());
						}
					}
				} elseif ('new_user' === $new_or_existing) {
					// New user.
					$user_data = array(
						'user_email' => $login_email,
						'user_login' => $username,
						'user_pass'  => $password,
					);

					$user_id = wp_insert_user($user_data);
					if (is_wp_error($user_id)) {
						throw new Exception($user_id->get_error_message());
					}
				} else {
					$user_id = NULL;
				}

				$update_student_user_id = $user_id;

				// Student record data.
				$student_record_data = array(
					'certificate_number' => $certificate_number,
					'updated_at'         => current_time('mysql'),
					'admission_number'  => $admission_number,
					'name'              => $name,
					'gender'            => $gender,
					'dob'               => $dob,
					'phone'             => $phone,
					'email'             => $email,
					'address'           => $address,
					'religion'          => $religion,
					'caste'             => $caste,
					'blood_group'       => $blood_group,
					'father_name'       => $father_name,
					'father_phone'      => $father_phone,
					'father_occupation' => $father_occupation,
					'mother_name'       => $mother_name,
					'mother_phone'      => $mother_phone,
					'mother_occupation' => $mother_occupation,
					'admission_date'    => $admission_date,
					'roll_number'       => $roll_number,
					'section_id'        => $section_id,
					'user_id'           => $update_student_user_id,
					'is_active'         => $is_active,
				);

				if ($student_id) {
					$student_record_data['photo_id'] = $student->photo_id;
				}

				if (! empty($photo)) {
					$photo = media_handle_upload('photo', 0);
					if (is_wp_error($photo)) {
						throw new Exception($photo->get_error_message());
					}
					$student_record_data['photo_id'] = $photo;
				}

				if ($student_id) {
					$student_record_data['updated_at'] = current_time('mysql');

					$success = $wpdb->update(WLSM_STUDENT_RECORDS, $student_record_data, array('ID' => $student_id));

					$is_insert = false;
				} else {
					$student_record_data['session_id'] = $session_id;

					$enrollment_number = WLSM_M_Staff_General::get_enrollment_number($school_id);

					$student_record_data['enrollment_number'] = $enrollment_number;

					$success = $wpdb->insert(WLSM_STUDENT_RECORDS, $student_record_data);

					$new_student_id = $wpdb->insert_id;
					$student_id     = $new_student_id;

					$is_insert = true;

					$message .= '&nbsp;<a href="' . esc_url($page_url) . '&action=save&id=' . $student_id . '">' . esc_html__('Edit Student', 'school-management-system') . '</a>';

					if ($inquiry_id) {
						// Update inquiry status to inactive.
						$inquiry_data = array(
							'is_active'  => 0,
							'updated_at' => current_time('mysql')
						);

						$wpdb->update(
							WLSM_INQUIRIES,
							$inquiry_data,
							array('ID' => $inquiry_id, 'school_id' => $school_id)
						);
					}
				}

				$buffer = ob_get_clean();
				if (! empty($buffer)) {
					throw new Exception($buffer);
				}

				if (false === $success) {
					throw new Exception($wpdb->last_error);
				}

				$wpdb->query('COMMIT;');

				if (isset($new_student_id)) {
					// Notify for student admission.
					$data = array(
						'school_id'  => $school_id,
						'session_id' => $session_id,
						'student_id' => $new_student_id,
						'password'   => $password,
					);

					wp_schedule_single_event(time() + 30, 'wlsm_notify_for_student_admission', $data);
				}

				wp_send_json_success(array('message' => $message));
			} catch (Exception $exception) {
				$wpdb->query('ROLLBACK;');
				wp_send_json_error($exception->getMessage());
			}
		}
		wp_send_json_error($errors);
	}

	public static function get_students()
	{
		$current_user = WLSM_M_Role::can('manage_students');

		if (! $current_user) {
			die();
		}

		$school_id     = $current_user['school']['id'];
		$session_id    = $current_user['session']['ID'];
		$session_label = $current_user['session']['label'];

		if (! wp_verify_nonce($_POST['get-students'], 'get-students')) {
			die();
		}

		$from_table = isset($_POST['from_table']) ? (bool) ($_POST['from_table']) : false;

		$output = array(
			'draw'            => 1,
			'recordsTotal'    => 0,
			'recordsFiltered' => 0,
			'data'            => [],
		);

		$search_students_by = isset($_POST['search_students_by']) ? sanitize_text_field($_POST['search_students_by']) : '';

		$search_field   = isset($_POST['search_field']) ? sanitize_text_field($_POST['search_field']) : '';
		$search_keyword = isset($_POST['search_keyword']) ? sanitize_text_field($_POST['search_keyword']) : '';

		$class_id   = isset($_POST['class_id']) ? absint($_POST['class_id']) : 0;
		$section_id = isset($_POST['section_id']) ? absint($_POST['section_id']) : 0;

		try {
			ob_start();
			global $wpdb;

			// Start validation.
			$errors = array();

			if (! in_array($search_students_by, array('search_by_keyword', 'search_by_class'))) {
				throw new Exception(esc_html__('Please specify search criteria.', 'school-management-system'));
			}

			if ('search_by_keyword' === $search_students_by) {
				if (! empty($search_field) && empty($search_keyword)) {
					$errors['search_keyword'] = esc_html__('Please enter search keyword.', 'school-management-system');
				} elseif (! empty($search_keyword) && empty($search_field)) {
					$errors['search_field'] = esc_html__('Please specify search field.', 'school-management-system');
				}

				$filter = array(
					'search_field'   => $search_field,
					'search_keyword' => $search_keyword,
				);
			} else {
				if (empty($class_id)) {
					$errors['class_id'] = esc_html__('Please select a class.', 'school-management-system');
				}

				$filter = array(
					'class_id'   => $class_id,
					'section_id' => $section_id,
				);
			}
		} catch (Exception $exception) {
			if ($from_table) {
				echo json_encode($output);
				die();
			}
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			if (! $from_table) {
				wp_send_json_success();
			}
			try {
				$filter['search_by'] = $search_students_by;

				$page_url = WLSM_M_Staff_General::get_students_page_url();

				$query = WLSM_M_Staff_General::fetch_students_query($school_id, $session_id, $filter);

				$query_filter = $query;

				// Grouping.
				$group_by = ' ' . WLSM_M_Staff_General::fetch_students_query_group_by();

				$query        .= $group_by;
				$query_filter .= $group_by;

				$condition = '';
				if (esc_sql($_POST['search']['value'])) {
					$search_value = sanitize_text_field(esc_sql($_POST['search']['value']));
					if ('' !== $search_value) {
						$search_value_like = '%' . $wpdb->esc_like($search_value) . '%';

						$condition .= $wpdb->prepare(
							'(sr.name LIKE %s) OR ' .
								'(sr.admission_number LIKE %s) OR ' .
								'(sr.enrollment_number LIKE %s) OR ' .
								'(sr.phone LIKE %s) OR ' .
								'(sr.email LIKE %s) OR ' .
								'(sr.father_name LIKE %s) OR ' .
								'(sr.father_phone LIKE %s) OR ' .
								'(u.user_email LIKE %s) OR ' .
								'(u.user_login LIKE %s) OR ' .
								'(c.label LIKE %s) OR ' .
								'(se.label LIKE %s) OR ' .
								'(sr.roll_number LIKE %s)',
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like,
							$search_value_like
						);

						$search_value_lowercase = strtolower($search_value);
						if (preg_match('/^inac(|t|ti|tiv|tive)$/', $search_value_lowercase)) {
							$is_active = 0;
						} elseif (preg_match('/^acti(|v|ve)$/', $search_value_lowercase)) {
							$is_active = 1;
						}
						if (isset($is_active)) {
							$condition .= $wpdb->prepare(' OR (sr.is_active = %d)', $is_active);
						}

						$admission_date = DateTime::createFromFormat(WLSM_Config::date_format(), $search_value);

						if ($admission_date) {
							$format_admission_date = 'Y-m-d';
						} else {
							if ('d-m-Y' === WLSM_Config::date_format()) {
								if (! $admission_date) {
									$admission_date        = DateTime::createFromFormat('m-Y', $search_value);
									$format_admission_date = 'Y-m';
								}
							} elseif ('d/m/Y' === WLSM_Config::date_format()) {
								if (! $admission_date) {
									$admission_date        = DateTime::createFromFormat('m/Y', $search_value);
									$format_admission_date = 'Y-m';
								}
							} elseif ('Y-m-d' === WLSM_Config::date_format()) {
								if (! $admission_date) {
									$admission_date        = DateTime::createFromFormat('Y-m', $search_value);
									$format_admission_date = 'Y-m';
								}
							} elseif ('Y/m/d' === WLSM_Config::date_format()) {
								if (! $admission_date) {
									$admission_date        = DateTime::createFromFormat('Y/m', $search_value);
									$format_admission_date = 'Y-m';
								}
							}

							if (! $admission_date) {
								$admission_date        = DateTime::createFromFormat('Y', $search_value);
								$format_admission_date = 'Y';
							}
						}

						if ($admission_date && isset($format_admission_date)) {
							$admission_date = $admission_date->format($format_admission_date);
							$admission_date = $wpdb->prepare(' OR (sr.admission_date LIKE %s)', '%' . $wpdb->esc_like($admission_date) . '%');

							$condition .= $admission_date;
						}

						$query_filter .= (' HAVING ' . $condition);
					}
				}

				// Ordering.
				$columns = array('sr.name', 'sr.admission_number', 'sr.phone', 'sr.email', 'c.label', 'se.label', 'sr.roll_number', 'sr.father_name', 'sr.father_phone', 'u.user_email', 'u.user_login', 'sr.admission_date', 'sr.enrollment_number', 'sr.is_active');
				if (esc_sql(isset($_POST['order'])) && esc_sql($columns[$_POST['order']['0']['column']])) {
					$order_by  = sanitize_text_field(esc_sql($columns[$_POST['order']['0']['column']]));
			$order_dir = ( strtoupper( sanitize_text_field( $_POST['order']['0']['dir'] ) ) === 'ASC' ) ? 'ASC' : 'DESC';

					$query_filter .= ' ORDER BY ' . $order_by . ' ' . $order_dir;
				} else {
					$query_filter .= ' ORDER BY sr.ID DESC';
				}

				// Limiting.
				$limit = '';
				if (-1 != $_POST['length']) {
					$start  = absint(esc_sql($_POST['start']));
					$length = absint(esc_sql($_POST['length']));

					$limit  = ' LIMIT ' . $start . ', ' . $length;
				}

				// Total query.
				$rows_query = WLSM_M_Staff_General::fetch_students_query_count($school_id, $session_id, $filter);

				// Total rows count.
				// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query is built from safely prepared fragments.
				$total_rows_count = $wpdb->get_var($rows_query);

				// Filtered rows count.
				if ($condition) {
					// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query and dynamic segments are built from safely prepared fragments.
					$filter_rows_count = $wpdb->get_var($rows_query . ' AND (' . $condition . ')');
				} else {
					$filter_rows_count = $total_rows_count;
				}

				// Filtered limit rows.
				// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query_filter and $limit are built from safely prepared fragments.
				$filter_rows_limit = $wpdb->get_results($query_filter . $limit);

				$data = array();
				if (count($filter_rows_limit)) {
					foreach ($filter_rows_limit as $row) {
						// Table columns.
						$data[] = array(
							esc_html(WLSM_M_Staff_Class::get_name_text($row->student_name)),
							esc_html(WLSM_M_Staff_Class::get_admission_no_text($row->admission_number)),
							esc_html(WLSM_M_Staff_Class::get_phone_text($row->phone)),
							esc_html(WLSM_M_Staff_Class::get_name_text($row->email)),
							esc_html(WLSM_M_Class::get_label_text($row->class_label)),
							esc_html(WLSM_M_Staff_Class::get_section_label_text($row->section_label)),
							esc_html(WLSM_M_Staff_Class::get_roll_no_text($row->roll_number)),
							esc_html(WLSM_M_Staff_Class::get_name_text($row->father_name)),
							esc_html(WLSM_M_Staff_Class::get_phone_text($row->father_phone)),
							esc_html(WLSM_M_Staff_Class::get_name_text($row->login_email)),
							esc_html(WLSM_M_Staff_Class::get_name_text($row->username)),
							esc_html(WLSM_Config::get_date_text($row->admission_date)),
							esc_html($row->enrollment_number),
							esc_html(WLSM_M_Staff_Class::get_status_text($row->is_active)),
							'<a class="text-primary wlsm-view-session-records" data-nonce="' . esc_attr(wp_create_nonce('view-session-records-' . $row->ID)) . '" data-student="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Session Records', 'school-management-system') . '" data-close="' . esc_attr__('Close', 'school-management-system') . '"><span class="dashicons dashicons-search"></span></a>',
							'<a class="text-success wlsm-print-id-card" data-nonce="' . esc_attr(wp_create_nonce('print-id-card-' . $row->ID)) . '" data-id-card="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Print ID Card', 'school-management-system') . '" data-close="' . esc_attr__('Close', 'school-management-system') . '"><i class="fas fa-print"></i></a>',
							'<a class="text-primary" href="' . esc_url($page_url . "&action=save&id=" . $row->ID) . '"><span class="dashicons dashicons-edit"></span></a>&nbsp;&nbsp;
							<a class="text-danger wlsm-delete-student" data-nonce="' . esc_attr(wp_create_nonce('delete-student-' . $row->ID)) . '" data-student="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Please Confirm!', 'school-management-system') . '" data-message-content="' .
								sprintf(
									/* translators: %s: session label */
									esc_attr__('This will delete student record for the session %s.', 'school-management-system'),
									esc_html(WLSM_M_Session::get_label_text($session_label))
								) . '" data-cancel="' . esc_attr__('Cancel', 'school-management-system') . '" data-submit="' . esc_attr__('Confirm', 'school-management-system') . '"><span class="dashicons dashicons-trash"></span></a>'
						);
					}
				}

				$output = array(
					'draw'            => intval(esc_sql($_POST['draw'])),
					'recordsTotal'    => $total_rows_count,
					'recordsFiltered' => $filter_rows_count,
					'data'            => $data,
					'export'          => array(
						'nonce'  => wp_create_nonce('export-staff-students-table'),
						'action' => 'wlsm-export-staff-students-table'
					)
				);

				echo json_encode($output);
				die();
			} catch (Exception $exception) {
				if ($from_table) {
					echo json_encode($output);
					die();
				}
				wp_send_json_error($exception->getMessage());
			}
		}

		if ($from_table) {
			echo json_encode($output);
			die();
		}
		wp_send_json_error($errors);
	}

	public static function delete_student()
	{
		$current_user = WLSM_M_Role::can('manage_students');

		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$student_id = isset($_POST['student_id']) ? absint($_POST['student_id']) : 0;

			if (! wp_verify_nonce($_POST['delete-student-' . $student_id], 'delete-student-' . $student_id)) {
				die();
			}

			// Checks if student exists.
			$student = WLSM_M_Staff_General::get_student($school_id, $session_id, $student_id);

			if (! $student) {
				throw new Exception(esc_html__('Student not found.', 'school-management-system'));
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		try {
			$wpdb->query('BEGIN;');

			$success = $wpdb->delete(WLSM_STUDENT_RECORDS, array('ID' => $student_id));
			$message = esc_html__('Student record deleted successfully.', 'school-management-system');

			$exception = ob_get_clean();
			if (! empty($exception)) {
				throw new Exception($exception);
			}

			if (false === $success) {
				throw new Exception($wpdb->last_error);
			}

			$wpdb->query('COMMIT;');

			wp_send_json_success(array('message' => $message));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}

	public static function view_session_records()
	{
		$current_user = WLSM_M_Role::can('manage_students');

		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		$session_label = $current_user['session']['label'];

		try {
			ob_start();
			global $wpdb;

			$student_id = isset($_POST['student_id']) ? absint($_POST['student_id']) : 0;

			if (! wp_verify_nonce($_POST['view-session-records-' . $student_id], 'view-session-records-' . $student_id)) {
				die();
			}

			// Checks if student exists.
			$student = WLSM_M_Staff_General::fetch_student($school_id, $session_id, $student_id);

			if (! $student) {
				throw new Exception(esc_html__('Student not found.', 'school-management-system'));
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		$student_old_records = array();
		$student_new_records = array();

		$student_current_session_record = array(
			array(
				'is_current'        => true,
				'session_label'     => $session_label,
				'enrollment_number' => $student->enrollment_number,
				'student_name'      => WLSM_M_Staff_Class::get_name_text($student->student_name),
				'class_label'       => WLSM_M_Class::get_label_text($student->class_label),
				'section_label'     => WLSM_M_Staff_Class::get_section_label_text($student->section_label),
				'roll_number'       => WLSM_M_Staff_Class::get_roll_no_text($student->roll_number),
			),
		);

		$to_student_record   = $student->ID;
		$from_student_record = $student->ID;

		while ($to_student_record = self::student_old_record_exists($to_student_record)) {
			$student = WLSM_M_Staff_General::get_student_record($school_id, $to_student_record);
			if ($student) {
				$student_old_record_data = array(
					'is_current'        => false,
					'session_label'     => $student->session_label,
					'enrollment_number' => $student->enrollment_number,
					'student_name'      => WLSM_M_Staff_Class::get_name_text($student->student_name),
					'class_label'       => WLSM_M_Class::get_label_text($student->class_label),
					'section_label'     => WLSM_M_Staff_Class::get_section_label_text($student->section_label),
					'roll_number'       => WLSM_M_Staff_Class::get_roll_no_text($student->roll_number),
				);
				array_push($student_old_records, $student_old_record_data);
			}
		}

		while ($from_student_record = self::student_new_record_exists($from_student_record)) {
			$student = WLSM_M_Staff_General::get_student_record($school_id, $from_student_record);
			if ($student) {
				$student_new_record_data = array(
					'is_current'        => false,
					'session_label'     => $student->session_label,
					'enrollment_number' => $student->enrollment_number,
					'student_name'      => WLSM_M_Staff_Class::get_name_text($student->student_name),
					'class_label'       => WLSM_M_Class::get_label_text($student->class_label),
					'section_label'     => WLSM_M_Staff_Class::get_section_label_text($student->section_label),
					'roll_number'       => WLSM_M_Staff_Class::get_roll_no_text($student->roll_number),
				);
				array_push($student_new_records, $student_new_record_data);
			}
		}

		$student_records = array_merge(array_reverse($student_old_records), $student_current_session_record, $student_new_records);

		ob_start();
?>
		<div class="wlsm">
			<?php
			foreach ($student_records as $student_record) {
			?>
				<ul class="border-bottom">
					<li>
						<span class="wlsm-font-bold"><?php esc_html_e('Session', 'school-management-system'); ?></span>:
						<span <?php if ($student_record['is_current']) {
									echo esc_attr('class="wlsm-font-bold text-primary"');
								} ?>>
							<?php echo esc_html($student_record['session_label']); ?>
						</span>
					</li>
					<li>
						<span class="wlsm-font-bold"><?php esc_html_e('Enrollment Number', 'school-management-system'); ?></span>:
						<span><?php echo esc_html($student_record['enrollment_number']); ?></span>
					</li>
					<li>
						<span class="wlsm-font-bold"><?php esc_html_e('Student Name', 'school-management-system'); ?></span>:
						<span><?php echo esc_html($student_record['student_name']); ?></span>
					</li>
					<li>
						<span class="wlsm-font-bold"><?php esc_html_e('Class', 'school-management-system'); ?></span>:
						<span><?php echo esc_html($student_record['class_label']); ?></span>
					</li>
					<li>
						<span class="wlsm-font-bold"><?php esc_html_e('Section', 'school-management-system'); ?></span>:
						<span><?php echo esc_html($student_record['section_label']); ?></span>
					</li>
					<li>
						<span class="wlsm-font-bold"><?php esc_html_e('Roll Number', 'school-management-system'); ?></span>:
						<span><?php echo esc_html($student_record['roll_number']); ?></span>
					</li>
				</ul>
			<?php
			}
			?>
		</div>

		<?php
		$html = ob_get_clean();

		wp_send_json_success(array('html' => $html));
	}

	public static function print_id_card()
	{
		$current_user = WLSM_M_Role::can('manage_students');

		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$student_id = isset($_POST['student_id']) ? absint($_POST['student_id']) : 0;

			if (! wp_verify_nonce($_POST['print-id-card-' . $student_id], 'print-id-card-' . $student_id)) {
				die();
			}

			// Checks if student exists.
			$student = WLSM_M_Staff_General::fetch_student($school_id, $session_id, $student_id);

			if (! $student) {
				throw new Exception(esc_html__('Student not found.', 'school-management-system'));
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		ob_start();
		require_once WLSM_PLUGIN_DIR_PATH . 'admin/inc/school/print/id_card.php';
		$html = ob_get_clean();

		wp_send_json_success(array('html' => $html));
	}

	public static function print_id_cards()
	{
		$current_user = WLSM_M_Role::can('manage_students');

		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		$session_label = $current_user['session']['label'];

		if (! wp_verify_nonce($_POST['print-id-cards'], 'print-id-cards')) {
			die();
		}

		$class_id    = isset($_POST['class_id']) ? absint($_POST['class_id']) : 0;
		$section_id  = isset($_POST['section_id']) ? absint($_POST['section_id']) : 0;
		$only_active = isset($_POST['only_active']) ? (bool) ($_POST['only_active']) : 1;

		try {
			ob_start();
			global $wpdb;

			// Start validation.
			$errors = array();

			if (empty($class_id)) {
				$errors['class_id'] = esc_html__('Please select a class.', 'school-management-system');
			} else {
				// Checks if class exists in the school.
				$class_school = WLSM_M_Staff_Class::get_class($school_id, $class_id);

				if (! $class_school) {
					$errors['class_id'] = esc_html__('Class not found.', 'school-management-system');
				} else {
					$class_school_id = $class_school->ID;

					if ($section_id) {
						$section = WLSM_M_Staff_Class::fetch_section($school_id, $section_id, $class_school_id);
						if (! $section) {
							$errors['section_id'] = esc_html__('Section not found.', 'school-management-system');
						} else {
							$section_label = WLSM_M_Staff_Class::get_section_label_text($section->label);
						}
					} else {
						$section_label = esc_html__('All', 'school-management-system');
					}

					$class       = WLSM_M_Class::fetch_class($class_id);
					$class_label = WLSM_M_Class::get_label_text($class->label);
				}
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			$filter = array(
				'class_id'   => $class_id,
				'section_id' => $section_id,
				'search_by'  => 'search_by_class'
			);

			$query = WLSM_M_Staff_General::fetch_students_query($school_id, $session_id, $filter);

			// Grouping.
			$group_by = ' ' . WLSM_M_Staff_General::fetch_students_query_group_by();

			$query .= $group_by;
			$query .= ' ORDER BY sr.roll_number ASC, sr.ID ASC';

			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query is built from safely prepared fragments.
			$students = $wpdb->get_results($query);

			$students = array_filter($students, function ($student) use ($only_active) {
				if ($only_active && ! $student->is_active) {
					return false;
				}
				return true;
			});

			ob_start();
			require_once WLSM_PLUGIN_DIR_PATH . 'admin/inc/school/print/id_cards.php';
			$html = ob_get_clean();

			$json = json_encode(array(
				'message_title' => esc_html__('Print ID Cards', 'school-management-system'),
			));

			wp_send_json_success(array('html' => $html, 'json' => $json));
		}

		wp_send_json_error($errors);
	}

	public static function student_old_record_exists($to_student_record)
	{
		global $wpdb;
		$student = $wpdb->get_row($wpdb->prepare('SELECT pm.from_student_record FROM %i as pm WHERE pm.to_student_record = %d', WLSM_PROMOTIONS, $to_student_record));
		if ($student) {
			return $student->from_student_record;
		}
		return false;
	}

	public static function student_new_record_exists($from_student_record)
	{
		global $wpdb;
		$student = $wpdb->get_row($wpdb->prepare('SELECT pm.to_student_record FROM %i as pm WHERE pm.from_student_record = %d', WLSM_PROMOTIONS, $from_student_record));
		if ($student) {
			return $student->to_student_record;
		}
		return false;
	}

	public static function fetch_employees()
	{
		$current_user = WLSM_M_Role::can('manage_employees');
		self::fetch_staff_records($current_user, WLSM_M_Role::get_employee_key());
	}

	public static function fetch_staff_records($current_user, $role)
	{
		if (! $current_user) {
			die();
		}

		if ( ! wp_verify_nonce( isset( $_POST['nonce'] ) ? $_POST['nonce'] : '', 'wlsm-security' ) ) {
			die();
		}
		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		global $wpdb;

		if (WLSM_M_Role::get_admin_key() === $role) {
			$page_url = WLSM_M_Staff_General::get_admins_page_url();
		} else {
			$page_url = WLSM_M_Staff_General::get_employees_page_url();
		}

		$query = WLSM_M_Staff_General::fetch_staff_query($school_id, $role);

		$query_filter = $query;

		// Grouping.
		$group_by = ' ' . WLSM_M_Staff_General::fetch_staff_query_group_by();

		$query        .= $group_by;
		$query_filter .= $group_by;

		// Searching.
		$condition = '';
		if (esc_sql($_POST['search']['value'])) {
			$search_value = sanitize_text_field($_POST['search']['value']);
			if ('' !== $search_value) {
				$search_value_like = '%' . $wpdb->esc_like($search_value) . '%';

				$condition .= $wpdb->prepare(
					'(a.name LIKE %s) OR ' .
						'(a.phone LIKE %s) OR ' .
						'(a.email LIKE %s) OR ' .
						'(a.salary LIKE %s) OR ' .
						'(a.designation LIKE %s) OR ' .
						'(r.name LIKE %s) OR ' .
						'(u.user_email LIKE %s) OR ' .
						'(u.user_login LIKE %s)',
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like,
					$search_value_like
				);

				$search_value_lowercase = strtolower($search_value);
				if (preg_match('/^inac(|t|ti|tiv|tive)$/', $search_value_lowercase)) {
					$is_active = 0;
				} elseif (preg_match('/^acti(|v|ve)$/', $search_value_lowercase)) {
					$is_active = 1;
				}
				if (isset($is_active)) {
					$condition .= $wpdb->prepare(' OR (a.is_active = %d)', $is_active);
				}

				$joining_date = DateTime::createFromFormat(WLSM_Config::date_format(), $search_value);
				if ($joining_date) {
					$format_joining_date = 'Y-m-d';
				} else {
					if ('d-m-Y' === WLSM_Config::date_format()) {
						$joining_date = DateTime::createFromFormat('m-Y', $search_value);
						$format_joining_date = 'Y-m';
					} elseif ('d/m/Y' === WLSM_Config::date_format()) {
						$joining_date = DateTime::createFromFormat('m/Y', $search_value);
						$format_joining_date = 'Y-m';
					} elseif ('Y-m-d' === WLSM_Config::date_format()) {
						$joining_date = DateTime::createFromFormat('Y-m', $search_value);
						$format_joining_date = 'Y-m';
					} elseif ('Y/m/d' === WLSM_Config::date_format()) {
						$joining_date = DateTime::createFromFormat('Y/m', $search_value);
						$format_joining_date = 'Y-m';
					}

					if (!$joining_date) {
						$joining_date = DateTime::createFromFormat('Y', $search_value);
						$format_joining_date = 'Y';
					}
				}

				if ($joining_date && isset($format_joining_date)) {
					$joining_date = $joining_date->format($format_joining_date);
					$condition .= $wpdb->prepare(' OR (a.joining_date LIKE %s)', '%' . $wpdb->esc_like($joining_date) . '%');
				}

				// Finally, construct the query condition
				$query_filter .= ' HAVING ' . $condition;
			}
		}

		// Ordering.
		$columns = array('a.name', 'a.phone', 'a.email', 'a.salary', 'a.designation', 'r.name', 'u.user_email', 'u.user_login', 'a.joining_date', 'a.is_active');
		if (esc_sql(isset($_POST['order'])) && esc_sql($columns[$_POST['order']['0']['column']])) {
			$order_by  = sanitize_text_field(esc_sql($columns[$_POST['order']['0']['column']]));
			$order_dir = ( strtoupper( sanitize_text_field( $_POST['order']['0']['dir'] ) ) === 'ASC' ) ? 'ASC' : 'DESC';

			$query_filter .= ' ORDER BY ' . $order_by . ' ' . $order_dir;
		} else {
			$query_filter .= ' ORDER BY a.joining_date DESC, a.ID DESC';
		}

		// Limiting.
		$limit = '';
		if (-1 != $_POST['length']) {
			$start  = absint(esc_sql($_POST['start']));
			$length = absint(esc_sql($_POST['length']));

			$limit  = ' LIMIT ' . $start . ', ' . $length;
		}

		// Total query.
		$rows_query = WLSM_M_Staff_General::fetch_staff_query_count($school_id, $role);

		// Total rows count.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query is built from safely prepared fragments.
		$total_rows_count = $wpdb->get_var($rows_query);

		// Filtered rows count.
		if ($condition) {
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query and dynamic segments are built from safely prepared fragments.
			$filter_rows_count = $wpdb->get_var($rows_query . ' AND (' . $condition . ')');
		} else {
			$filter_rows_count = $total_rows_count;
		}

		// Filtered limit rows.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query_filter and $limit are built from safely prepared fragments.
		$filter_rows_limit = $wpdb->get_results($query_filter . $limit);

		$data = array();

		if (count($filter_rows_limit)) {
			foreach ($filter_rows_limit as $row) {
				if ($row->assigned_by_manager) {
					$delete_staff = '';
				} else {
					/* translators: %s: staff role */
					$delete_staff = '<a class="text-danger wlsm-delete-staff" data-nonce="' . esc_attr(wp_create_nonce('delete-staff-' . $row->ID)) . '" data-staff="' . esc_attr($row->ID) . '" href="#" data-role="' . esc_attr($role) . '" data-message-title="' . esc_attr__('Please Confirm!', 'school-management-system') . '" data-message-content="' . sprintf(esc_attr__('This will delete the %s.', 'school-management-system'), strtolower(WLSM_M_Role::get_role_text($role))) . '" data-cancel="' . esc_attr__('Cancel', 'school-management-system') . '" data-submit="' . esc_attr__('Confirm', 'school-management-system') . '"><span class="dashicons dashicons-trash"></span></a>';
				}

				if (WLSM_M_Role::get_admin_key() === $role) {
					$role_name = WLSM_M_Role::get_admin_text();
				} else {
					$role_name = $row->role_name;
				}

				// Table columns.
				$data[] = array(
					esc_html(WLSM_M_Staff_Class::get_name_text($row->name)),
					esc_html(WLSM_M_Staff_Class::get_phone_text($row->phone)),
					esc_html(WLSM_M_Staff_Class::get_name_text($row->email)),
					esc_html(WLSM_Config::get_money_text($row->salary)),
					esc_html(WLSM_M_Staff_Class::get_designation_text($row->designation)),
					esc_html($role_name),
					esc_html(WLSM_M_Staff_Class::get_name_text($row->login_email)),
					esc_html(WLSM_M_Staff_Class::get_name_text($row->username)),
					esc_html(WLSM_Config::get_date_text($row->joining_date)),
					esc_html(WLSM_M_Staff_Class::get_status_text($row->is_active)),
					'<a class="text-primary" href="' . esc_url($page_url . "&action=save&id=" . $row->ID) . '"><span class="dashicons dashicons-edit"></span></a>&nbsp;&nbsp;' . $delete_staff
				);
			}
		}

		$output = array(
			'draw'            => intval(esc_sql($_POST['draw'])),
			'recordsTotal'    => $total_rows_count,
			'recordsFiltered' => $filter_rows_count,
			'data'            => $data,
		);

		echo json_encode($output);
		die();
	}

	public static function save_employee()
	{
		$current_user = WLSM_M_Role::can('manage_employees');
		self::save_staff($current_user, WLSM_M_Role::get_employee_key());
	}

	public static function save_staff($current_user, $role)
	{
		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		if (WLSM_M_Role::get_admin_key() === $role) {
			$page_url = WLSM_M_Staff_General::get_admins_page_url();
		} else {
			$page_url = WLSM_M_Staff_General::get_employees_page_url();
		}

		try {
			ob_start();
			global $wpdb;

			$admin_id = isset($_POST['staff_id']) ? absint($_POST['staff_id']) : 0;

			if ($admin_id) {
				if (! wp_verify_nonce($_POST['edit-' . $role . '-' . $admin_id], 'edit-' . $role . '-' . $admin_id)) {
					die();
				}
			} else {
				if (! wp_verify_nonce($_POST['add-' . $role . ''], 'add-' . $role . '')) {
					die();
				}
			}

			$user_id = NULL;

			$assigned_by_manager = 0;

			// Checks if staff exists.
			if ($admin_id) {
				$admin = WLSM_M_Staff_General::get_staff($school_id, $role, $admin_id);

				if (! $admin) {
					/* translators: %s: staff role */
					throw new Exception(sprintf(esc_html__('%s not found.', 'school-management-system'), WLSM_M_Role::get_role_text($role)));
				}

				$assigned_by_manager = $admin->assigned_by_manager;

				$user_id  = $admin->user_id;
				$staff_id = $admin->staff_id;
			}

			// Personal Detail.
			$name          = isset($_POST['name']) ? sanitize_text_field($_POST['name']) : '';
			$gender        = isset($_POST['gender']) ? sanitize_text_field($_POST['gender']) : '';
			$dob           = isset($_POST['dob']) ? DateTime::createFromFormat(WLSM_Config::date_format(), sanitize_text_field($_POST['dob'])) : NULL;
			$address       = isset($_POST['address']) ? sanitize_text_field($_POST['address']) : '';
			$email         = isset($_POST['email']) ? sanitize_text_field($_POST['email']) : '';
			$phone         = isset($_POST['phone']) ? sanitize_text_field($_POST['phone']) : '';
			$salary        = isset($_POST['salary']) ? WLSM_Config::sanitize_money($_POST['salary']) : 0;

			// Joining Detail.
			$joining_date = isset($_POST['joining_date']) ? DateTime::createFromFormat(WLSM_Config::date_format(), sanitize_text_field($_POST['joining_date'])) : NULL;
			$leaving_date = isset($_POST['leaving_date']) ? DateTime::createFromFormat(WLSM_Config::date_format(), sanitize_text_field($_POST['leaving_date'])) : NULL;
			$staff_role   = isset($_POST['role']) ? absint($_POST['role']) : 0;
			$designation  = isset($_POST['designation']) ? sanitize_text_field($_POST['designation']) : '';

			// Permissions
			$permissions = (isset($_POST['permission']) && is_array($_POST['permission'])) ? $_POST['permission'] : array();

			// Login Detail.
			$new_or_existing   = isset($_POST['staff_new_or_existing']) ? sanitize_text_field($_POST['staff_new_or_existing']) : '';
			$existing_username = isset($_POST['existing_username']) ? sanitize_text_field($_POST['existing_username']) : '';
			$new_login_email   = isset($_POST['new_login_email']) ? sanitize_text_field($_POST['new_login_email']) : '';
			$new_password      = isset($_POST['new_password']) ? sanitize_text_field($_POST['new_password']) : '';
			$username          = isset($_POST['username']) ? sanitize_text_field($_POST['username']) : '';
			$login_email       = isset($_POST['login_email']) ? sanitize_text_field($_POST['login_email']) : '';
			$password          = isset($_POST['password']) ? $_POST['password'] : '';

			// Status.
			$is_active = isset($_POST['is_active']) ? (bool) $_POST['is_active'] : 1;

			// Start validation.
			$errors = array();

			// Personal Detail.
			if (empty($name)) {
				$errors['name'] = esc_html__('Please specify name.', 'school-management-system');
			}
			if (strlen($name) > 60) {
				$errors['name'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
			}
			if (! empty($phone) && strlen($phone) > 40) {
				$errors['phone'] = esc_html__('Maximum length cannot exceed 40 characters.', 'school-management-system');
			}
			if (! empty($email)) {
				if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
					$errors['email'] = esc_html__('Please provide a valid email.', 'school-management-system');
				} elseif (strlen($email) > 60) {
					$errors['email'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
				}
			}
			if (! empty($designation) && strlen($designation) > 80) {
				$errors['designation'] = esc_html__('Maximum length cannot exceed 80 characters.', 'school-management-system');
			}
			if (! in_array($gender, array_keys(WLSM_Helper::gender_list()))) {
				$gender = NULL;
			}
			if (! empty($dob)) {
				$dob = $dob->format('Y-m-d');
			} else {
				$dob = NULL;
			}

			// Joining Detail.
			if (! empty($joining_date)) {
				$joining_date = $joining_date->format('Y-m-d');
			} else {
				$joining_date = NULL;
			}
			if (! empty($leaving_date)) {
				$leaving_date = $leaving_date->format('Y-m-d');
			} else {
				$leaving_date = NULL;
			}

			if (WLSM_M_Role::get_admin_key() === $role) {
				$staff_role = NULL;
			}

			// Permissions.
			if (empty($staff_role)) {
				$staff_role = NULL;
			} else {
				$staff_role_exists = WLSM_M_Staff_General::fetch_role($school_id, $staff_role);
				if (! $staff_role_exists) {
					$errors['role'] = esc_html__('Please select valid staff role.', 'school-management-system');
				} else {
					$role_permissions = $staff_role_exists->permissions;
					if (is_serialized($role_permissions)) {
						$role_permissions = unserialize($role_permissions);
						$permissions      = array_unique(array_merge($role_permissions, $permissions));
					}
				}
			}

			$permissions = WLSM_M_Role::get_role_permissions($role, $permissions);

			// Login Detail.
			if ('existing_user' === $new_or_existing) {
				if (! $user_id) {
					if (empty($existing_username)) {
						$errors['existing_username'] = esc_html__('Please provide existing username.', 'school-management-system');
					}
				} else {
					if (empty($new_login_email)) {
						$errors['new_login_email'] = esc_html__('Please provide login email.', 'school-management-system');
					}
				}
			} elseif ('new_user' === $new_or_existing) {
				if (empty($username)) {
					$errors['username'] = esc_html__('Please provide username.', 'school-management-system');
				}
				if (empty($login_email)) {
					$errors['login_email'] = esc_html__('Please provide login email.', 'school-management-system');
				}
				if (! filter_var($login_email, FILTER_VALIDATE_EMAIL)) {
					$errors['login_email'] = esc_html__('Please provide a valid email.', 'school-management-system');
				}
				if (empty($password)) {
					$errors['password'] = esc_html__('Please provide login password.', 'school-management-system');
				}
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			try {
				$wpdb->query('BEGIN;');

				if ($admin_id) {
					/* translators: %s: staff role */
					$message = sprintf(esc_html__('%s updated successfully.', 'school-management-system'), WLSM_M_Role::get_role_text($role));
				} else {
					/* translators: %s: staff role */
					$message = sprintf(esc_html__('%s added successfully.', 'school-management-system'), WLSM_M_Role::get_role_text($role));
				}

				// Staff user data.
				$update_staff_user_id = NULL;
				if ('existing_user' === $new_or_existing) {
					if (! $user_id) {
						if (! $assigned_by_manager) {
							// Existing user.
							$user = get_user_by('login', $existing_username);
							if (! $user) {
								throw new Exception(esc_html__('Username does not exist.', 'school-management-system'));
							}

							$user_id = $user->ID;

							if (user_can($user_id, WLSM_ADMIN_CAPABILITY)) {
								throw new Exception(esc_html__('User is a multi-school administrator.', 'school-management-system'));
							}

							// Check if user already has a staff record in the school.
							if ($admin_id) {
								$user_has_staff_record = $wpdb->get_row($wpdb->prepare('SELECT sf.ID FROM %i as sf WHERE sf.user_id = %d AND sf.school_id = %d AND sf.ID != %d', WLSM_STAFF, $user_id, $school_id, $staff_id));
							} else {
								$user_has_staff_record = $wpdb->get_row($wpdb->prepare('SELECT sf.ID FROM %i as sf WHERE sf.user_id = %d AND sf.school_id = %d', WLSM_STAFF, $user_id, $school_id));
							}

							if ($user_has_staff_record) {
								throw new Exception(esc_html__('The user already has a staff record.', 'school-management-system'));
							}
						}
					} else {
						$user_data = array(
							'ID'         => $user_id,
							'user_email' => $new_login_email,
						);

						if (! empty($new_password)) {
							$user_data['user_pass'] = $new_password;
						}

						$user_id = wp_update_user($user_data);
						if (is_wp_error($user_id)) {
							throw new Exception($user_id->get_error_message());
						}
					}
				} elseif ('new_user' === $new_or_existing) {
					// New user.
					$user_data = array(
						'user_email' => $login_email,
						'user_login' => $username,
						'user_pass'  => $password,
					);

					$user_id = wp_insert_user($user_data);
					if (is_wp_error($user_id)) {
						throw new Exception($user_id->get_error_message());
					}
				} else {
					if (! $assigned_by_manager) {
						$user_id = NULL;
					}
				}

				$update_staff_user_id = $user_id;

				// Admin data.
				$admin_data = array(
					'name'         => $name,
					'gender'       => $gender,
					'dob'          => $dob,
					'phone'        => $phone,
					'email'        => $email,
					'address'      => $address,
					'salary'       => $salary,
					'designation'  => $designation,
					'joining_date' => $joining_date,
					'leaving_date' => $leaving_date,
					'updated_at'   => current_time('mysql'),
					'role_id'      => $staff_role,
					'is_active'    => $is_active,
				);

				$staff_data = array(
					'role'        => $role,
					'permissions' => serialize($permissions),
					'user_id'     => $user_id,
				);

				if ($admin_id) {
					// Update staff.
					$staff_data['updated_at'] = current_time('mysql');

					$success = $wpdb->update(WLSM_STAFF, $staff_data, array('ID' => $staff_id));
				} else {
					// Add staff.
					$staff_data['school_id'] = $school_id;
					$success = $wpdb->insert(WLSM_STAFF, $staff_data);
					$staff_id = $wpdb->insert_id;
				}

				$buffer = ob_get_clean();
				if (! empty($buffer)) {
					throw new Exception($buffer);
				}

				if ($admin_id) {
					// Update admin.
					$admin_data['updated_at'] = current_time('mysql');

					$success = $wpdb->update(WLSM_ADMINS, $admin_data, array('ID' => $admin_id, 'staff_id' => $staff_id));
				} else {
					// Add admin.
					$admin_data['staff_id'] = $staff_id;

					$success = $wpdb->insert(WLSM_ADMINS, $admin_data);
				}

				$buffer = ob_get_clean();
				if (! empty($buffer)) {
					throw new Exception($buffer);
				}

				if (false === $success) {
					throw new Exception($wpdb->last_error);
				}

				$current_school_exists = false;
				if ($current_school_id = get_user_meta($user_id, 'wlsm_school_id', true)) {
					$staff_in_school = WLSM_M_Admin::user_in_school($current_school_id, $user_id);

					if ($staff_in_school) {
						$current_school_exists = true;
					}
				}

				if (! $current_school_exists) {
					update_user_meta($user_id, 'wlsm_school_id', $school_id);
				}

				$wpdb->query('COMMIT;');

				$reload = false;
				if ($admin_id) {
					$reload = true;
				}

				wp_send_json_success(array('message' => $message, 'reload' => $reload));
			} catch (Exception $exception) {
				$wpdb->query('ROLLBACK;');
				wp_send_json_error($exception->getMessage());
			}
		}
		wp_send_json_error($errors);
	}

	public static function delete_employee()
	{
		$current_user = WLSM_M_Role::can('manage_employees');
		self::delete_staff($current_user, WLSM_M_Role::get_employee_key());
	}

	public static function delete_staff($current_user, $role)
	{
		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$admin_id = isset($_POST['staff_id']) ? absint($_POST['staff_id']) : 0;

			if (! wp_verify_nonce($_POST['delete-staff-' . $admin_id], 'delete-staff-' . $admin_id)) {
				die();
			}

			// Checks if staff exists.
			$admin = WLSM_M_Staff_General::get_staff($school_id, $role, $admin_id);

			if (! $admin) {
				/* translators: %s: staff role */
				throw new Exception(sprintf(esc_html__('%s not found.', 'school-management-system'), WLSM_M_Role::get_role_text($role)));
			}

			if ($admin->assigned_by_manager) {
				die();
			}

			$staff_id = $admin->staff_id;
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		try {
			$wpdb->query('BEGIN;');

			$success = $wpdb->delete(WLSM_STAFF, array('ID' => $staff_id));
			/* translators: %s: staff role */
			$message = sprintf(esc_html__('%s deleted successfully.', 'school-management-system'), WLSM_M_Role::get_role_text($role));

			$exception = ob_get_clean();
			if (! empty($exception)) {
				throw new Exception($exception);
			}

			if (false === $success) {
				throw new Exception($wpdb->last_error);
			}

			$wpdb->query('COMMIT;');

			wp_send_json_success(array('message' => $message));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}

	public static function fetch_roles()
	{
		// Check if the current user has the required capability.
		$current_user = WLSM_M_Role::can('manage_roles');

		if (! $current_user) {
			die();
		}

		if ( ! wp_verify_nonce( isset( $_POST['nonce'] ) ? $_POST['nonce'] : '', 'wlsm-security' ) ) {
			die();
		}

		$school_id = $current_user['school']['id'];

		global $wpdb;

		$page_url = WLSM_M_Staff_General::get_roles_page_url();

		// Create a prepared statement to prevent SQL injection.
		$query = // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- fetch_role_query is built from safely prepared fragments.
			WLSM_M_Staff_General::fetch_role_query($school_id);

		$query_filter = $query;

		// Grouping.
		$group_by = ' ' . WLSM_M_Staff_General::fetch_role_query_group_by();

		$query .= $group_by;
		$query_filter .= $group_by;

		// Searching.
		$condition = '';
		if (esc_sql($_POST['search']['value'])) {
			$search_value = sanitize_text_field(esc_sql($_POST['search']['value']));
			if ('' !== $search_value) {
				// Sanitize and validate user input.
				$search_value_like = '%' . $wpdb->esc_like($search_value) . '%';
				$condition .= $wpdb->prepare('(%s LIKE %s)', 'r.name', $search_value_like);

				$query_filter .= (' HAVING ' . $condition);
			}
		}

		// Ordering.
		$columns = array('r.name');
		if (esc_sql(isset($_POST['order'])) && esc_sql($columns[$_POST['order']['0']['column']])) {
			$order_by  = sanitize_text_field(esc_sql($columns[$_POST['order']['0']['column']]));
			$order_dir = ( strtoupper( sanitize_text_field( $_POST['order']['0']['dir'] ) ) === 'ASC' ) ? 'ASC' : 'DESC';

			$query_filter .= $wpdb->prepare(' ORDER BY %s %s', $order_by, $order_dir);
		} else {
			$query_filter .= ' ORDER BY r.name ASC';
		}

		// Limiting.
		$limit = '';
		if (-1 != $_POST['length']) {
			$start  = absint(esc_sql($_POST['start']));
			$length = absint(esc_sql($_POST['length']));

			$limit  = $wpdb->prepare(' LIMIT %d, %d', $start, $length);
		}

		// Total query.
		$rows_query = WLSM_M_Staff_General::fetch_role_query_count($school_id);

		// Total rows count.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query is built from safely prepared fragments.
		$total_rows_count = $wpdb->get_var($rows_query);

		// Filtered rows count.
		if ($condition) {
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query and dynamic segments are built from safely prepared fragments.
			$filter_rows_count = $wpdb->get_var($rows_query . ' AND (' . $condition . ')');
		} else {
			$filter_rows_count = $total_rows_count;
		}

		// Filtered limit rows.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query_filter and $limit are built from safely prepared fragments.
		$filter_rows_limit = $wpdb->get_results($query_filter . $limit);

		$data = array();

		if (count($filter_rows_limit)) {
			foreach ($filter_rows_limit as $row) {
				// Table columns.
				$data[] = array(
					esc_html($row->name),
					'<a class="text-primary" href="' . esc_url($page_url . "&action=save&id=" . $row->ID) . '"><span class="dashicons dashicons-edit"></span></a>&nbsp;&nbsp;
					<a class="text-danger wlsm-delete-role" data-nonce="' . esc_attr(wp_create_nonce('delete-role-' . $row->ID)) . '" data-role="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Please Confirm!', 'school-management-system') . '" data-message-content="' . esc_attr__('This will delete the role.', 'school-management-system') . '" data-cancel="' . esc_attr__('Cancel', 'school-management-system') . '" data-submit="' . esc_attr__('Confirm', 'school-management-system') . '"><span class="dashicons dashicons-trash"></span></a>'
				);
			}
		}

		$output = array(
			'draw'            => intval(esc_sql($_POST['draw'])),
			'recordsTotal'    => $total_rows_count,
			'recordsFiltered' => $filter_rows_count,
			'data'            => $data,
		);

		// Return JSON-encoded output.
		echo json_encode($output);
		die();
	}


	public static function save_role()
	{
		$current_user = WLSM_M_Role::can('manage_roles');

		if (! $current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		try {
			ob_start();
			global $wpdb;

			$role_id = isset($_POST['role_id']) ? absint($_POST['role_id']) : 0;

			if ($role_id) {
				if (! wp_verify_nonce($_POST['edit-role-' . $role_id], 'edit-role-' . $role_id)) {
					die();
				}
			} else {
				if (! wp_verify_nonce($_POST['add-role'], 'add-role')) {
					die();
				}
			}

			// Checks if role exists.
			if ($role_id) {
				$role = WLSM_M_Staff_General::get_role($school_id, $role_id);

				if (! $role) {
					throw new Exception(esc_html__('Role not found.', 'school-management-system'));
				}
			}

			$name        = isset($_POST['name']) ? sanitize_text_field($_POST['name']) : '';
			$permissions = (isset($_POST['permission']) && is_array($_POST['permission'])) ? $_POST['permission'] : array();

			// Start validation.
			$errors = array();

			if (empty($name)) {
				$errors['name'] = esc_html__('Please provide role name.', 'school-management-system');
			}
			if (strlen($name) > 60) {
				$errors['name'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
			}

			if (WLSM_M_Role::get_admin_key() === strtolower(trim($name))) {
				$errors['name'] = esc_html__('This role is reserved for school administrators.', 'school-management-system');
				wp_send_json_error($errors);
			}

			// Checks if role already exists with this name.
			if ($role_id) {
				$role_exist = $wpdb->get_var($wpdb->prepare('SELECT COUNT(*) as count FROM %i as r WHERE r.name = %s AND r.school_id = %d AND r.ID != %d', WLSM_ROLES, $name, $school_id, $role_id));
			} else {
				$role_exist = $wpdb->get_var($wpdb->prepare('SELECT COUNT(*) as count FROM %i as r WHERE r.name = %s AND r.school_id = %d', WLSM_ROLES, $name, $school_id));
			}

			if ($role_exist) {
				$errors['name'] = esc_html__('Role already exists with this name.', 'school-management-system');
			}

			$permissions = array_intersect($permissions, array_keys(WLSM_M_Role::get_permissions(array('manage_admins'))));
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			try {
				$wpdb->query('BEGIN;');

				if ($role_id) {
					$message = esc_html__('Role updated successfully.', 'school-management-system');
					$reset   = false;
				} else {
					$message = esc_html__('Role added successfully.', 'school-management-system');
					$reset   = true;
				}

				$permissions = serialize($permissions);

				// Role data.
				$data = array(
					'name'        => $name,
					'permissions' => $permissions,
					'updated_at'  => current_time('mysql'),
				);

				if ($role_id) {
					$data['updated_at'] = current_time('mysql');

					$success = $wpdb->update(WLSM_ROLES, $data, array('ID' => $role_id, 'school_id' => $school_id));
				} else {
					$data['school_id'] = $school_id;

					$success = $wpdb->insert(WLSM_ROLES, $data);
				}

				$buffer = ob_get_clean();
				if (! empty($buffer)) {
					throw new Exception($buffer);
				}

				if (false === $success) {
					throw new Exception($wpdb->last_error);
				}

				$wpdb->query('COMMIT;');

				wp_send_json_success(array('message' => $message, 'reset' => $reset));
			} catch (Exception $exception) {
				$wpdb->query('ROLLBACK;');
				wp_send_json_error($exception->getMessage());
			}
		}
		wp_send_json_error($errors);
	}

	public static function delete_role()
	{
		$current_user = WLSM_M_Role::can('manage_roles');

		if (! $current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		try {
			ob_start();
			global $wpdb;

			$role_id = isset($_POST['role_id']) ? absint($_POST['role_id']) : 0;

			if (! wp_verify_nonce($_POST['delete-role-' . $role_id], 'delete-role-' . $role_id)) {
				die();
			}

			// Checks if role exists.
			$role = WLSM_M_Staff_General::get_role($school_id, $role_id);

			if (! $role) {
				throw new Exception(esc_html__('Role not found.', 'school-management-system'));
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		try {
			$wpdb->query('BEGIN;');

			$success = $wpdb->delete(WLSM_ROLES, array('ID' => $role_id));
			$message = esc_html__('Role deleted successfully.', 'school-management-system');

			$exception = ob_get_clean();
			if (! empty($exception)) {
				throw new Exception($exception);
			}

			if (false === $success) {
				throw new Exception($wpdb->last_error);
			}

			$wpdb->query('COMMIT;');

			wp_send_json_success(array('message' => $message));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}

	public static function get_role_permissions()
	{
		$current_user = WLSM_M_Role::can('manage_employees');

		if (! $current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		try {
			ob_start();
			global $wpdb;

			if (! wp_verify_nonce($_POST['nonce'], 'get-role-permissions')) {
				die();
			}

			$role_id = isset($_POST['role_id']) ? absint($_POST['role_id']) : 0;

			// Checks if role exists in the school.
			$role = WLSM_M_Staff_General::fetch_role($school_id, $role_id);

			if (! $role) {
				throw new Exception(esc_html__('Role not found.', 'school-management-system'));
			}

			$permissions = array();

			if ($role->permissions) {
				$permissions = $role->permissions;
				if (is_serialized($permissions)) {
					$permissions = unserialize($permissions);
				}
			}

			wp_send_json($permissions);
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json(array());
		}
	}

	public static function fetch_inquiries()
	{
		$current_user = WLSM_M_Role::can('manage_inquiries');

		if (! $current_user) {
			die();
		}

		if ( ! wp_verify_nonce( isset( $_POST['nonce'] ) ? $_POST['nonce'] : '', 'wlsm-security' ) ) {
			die();
		}

		$school_id = $current_user['school']['id'];

		global $wpdb;

		$page_url = WLSM_M_Staff_General::get_inquiries_page_url();

		$query = WLSM_M_Staff_General::fetch_inquiry_query($school_id);

		$query_filter = $query;

		// Grouping.
		$group_by = ' ' . WLSM_M_Staff_General::fetch_inquiry_query_group_by();

		$query        .= $group_by;
		$query_filter .= $group_by;

		// Searching.
		$condition = '';
		if (esc_sql($_POST['search']['value'])) {
			$search_value = sanitize_text_field(esc_sql($_POST['search']['value']));
			if ('' !== $search_value) {
				$search_value = '%' . $search_value . '%'; // Add wildcard characters for partial matching

				$condition .= $wpdb->prepare(
					'(iq.name LIKE %s) OR ' .
						'(iq.phone LIKE %s) OR ' .
						'(iq.email LIKE %s) OR ' .
						'(iq.message LIKE %s) OR ' .
						'(iq.note LIKE %s) OR ' .
						'(c.label LIKE %s)',
					$search_value,
					$search_value,
					$search_value,
					$search_value,
					$search_value,
					$search_value
				);

				$search_value_lowercase = strtolower($search_value);
				if (preg_match('/^inac(|t|ti|tiv|tive)$/', $search_value_lowercase)) {
					$is_active = 0;
				} elseif (preg_match('/^acti(|v|ve)$/', $search_value_lowercase)) {
					$is_active = 1;
				}
				if (isset($is_active)) {
					$condition .= $wpdb->prepare(' OR (iq.is_active = %d)', $is_active);
				}

				$created_at = DateTime::createFromFormat(WLSM_Config::date_format(), $search_value);

				if ($created_at) {
					$format_created_at = 'Y-m-d';
				} else {
					if ('d-m-Y' === WLSM_Config::date_format()) {
						if (! $created_at) {
							$created_at        = DateTime::createFromFormat('m-Y', $search_value);
							$format_created_at = 'Y-m';
						}
					} elseif ('d/m/Y' === WLSM_Config::date_format()) {
						if (! $created_at) {
							$created_at        = DateTime::createFromFormat('m/Y', $search_value);
							$format_created_at = 'Y-m';
						}
					} elseif ('Y-m-d' === WLSM_Config::date_format()) {
						if (! $created_at) {
							$created_at        = DateTime::createFromFormat('Y-m', $search_value);
							$format_created_at = 'Y-m';
						}
					} elseif ('Y/m/d' === WLSM_Config::date_format()) {
						if (! $created_at) {
							$created_at        = DateTime::createFromFormat('Y/m', $search_value);
							$format_created_at = 'Y-m';
						}
					}

					if (! $created_at) {
						$created_at        = DateTime::createFromFormat('Y', $search_value);
						$format_created_at = 'Y';
					}
				}

				if ($created_at && isset($format_created_at)) {
					$created_at = $created_at->format($format_created_at);
					$created_at = ' OR (iq.created_at LIKE "%' . $created_at . '%")';

					$condition .= $created_at;
				}

				$next_follow_up = DateTime::createFromFormat(WLSM_Config::date_format(), $search_value);

				if ($next_follow_up) {
					$format_next_follow_up = 'Y-m-d';
				} else {
					if ('d-m-Y' === WLSM_Config::date_format()) {
						if (! $next_follow_up) {
							$next_follow_up        = DateTime::createFromFormat('m-Y', $search_value);
							$format_next_follow_up = 'Y-m';
						}
					} elseif ('d/m/Y' === WLSM_Config::date_format()) {
						if (! $next_follow_up) {
							$next_follow_up        = DateTime::createFromFormat('m/Y', $search_value);
							$format_next_follow_up = 'Y-m';
						}
					} elseif ('Y-m-d' === WLSM_Config::date_format()) {
						if (! $next_follow_up) {
							$next_follow_up        = DateTime::createFromFormat('Y-m', $search_value);
							$format_next_follow_up = 'Y-m';
						}
					} elseif ('Y/m/d' === WLSM_Config::date_format()) {
						if (! $next_follow_up) {
							$next_follow_up        = DateTime::createFromFormat('Y/m', $search_value);
							$format_next_follow_up = 'Y-m';
						}
					}

					if (! $next_follow_up) {
						$next_follow_up        = DateTime::createFromFormat('Y', $search_value);
						$format_next_follow_up = 'Y';
					}
				}

				if ($next_follow_up && isset($format_next_follow_up)) {
					$next_follow_up = $next_follow_up->format($format_next_follow_up);
					$next_follow_up = ' OR (iq.next_follow_up LIKE "%' . $next_follow_up . '%")';

					$condition .= $next_follow_up;
				}

				$query_filter .= (' HAVING ' . $condition);
			}
		}

		// Ordering.
		$columns = array('c.label', 'iq.name', 'iq.phone', 'iq.email', 'iq.message', 'iq.created_at', 'iq.next_follow_up', 'iq.is_active');
		if (esc_sql(isset($_POST['order'])) && esc_sql($columns[$_POST['order']['0']['column']])) {
			$order_by  = sanitize_text_field(esc_sql($columns[$_POST['order']['0']['column']]));
			$order_dir = ( strtoupper( sanitize_text_field( $_POST['order']['0']['dir'] ) ) === 'ASC' ) ? 'ASC' : 'DESC';

			$query_filter .= ' ORDER BY ' . $order_by . ' ' . $order_dir;
		} else {
			$query_filter .= ' ORDER BY iq.ID DESC';
		}

		// Limiting.
		$limit = '';
		if (-1 != $_POST['length']) {
			$start  = absint(esc_sql($_POST['start']));
			$length = absint(esc_sql($_POST['length']));

			$limit  = ' LIMIT ' . $start . ', ' . $length;
		}

		// Total query.
		$rows_query = WLSM_M_Staff_General::fetch_inquiry_query_count($school_id);

		// Total rows count.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query is built from safely prepared fragments.
		$total_rows_count = $wpdb->get_var($rows_query);

		// Filtered rows count.
		if ($condition) {
			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $rows_query and dynamic segments are built from safely prepared fragments.
			$filter_rows_count = $wpdb->get_var($rows_query . ' AND (' . $condition . ')');
		} else {
			$filter_rows_count = $total_rows_count;
		}

		// Filtered limit rows.
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $query_filter and $limit are built from safely prepared fragments.
		$filter_rows_limit = $wpdb->get_results($query_filter . $limit);

		$data = array();

		if (count($filter_rows_limit)) {
			$students_page_url = WLSM_M_Staff_General::get_students_page_url();
			foreach ($filter_rows_limit as $row) {
				if ($row->message) {
					$view_message = '<a class="text-primary wlsm-view-inquiry-message" data-nonce="' . esc_attr(wp_create_nonce('view-inquiry-message-' . $row->ID)) . '" data-inquiry="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Inquiry Message', 'school-management-system') . '" data-close="' . esc_attr__('Close', 'school-management-system') . '"><span class="dashicons dashicons-search"></span></a>';
				} else {
					$view_message = '-';
				}

				if ($row->is_active) {
					$add_admission = '<br><small class="wlsm-font-bold"><a href="' . esc_url($students_page_url . '&action=save&inquiry_id=' . $row->ID) . '">' . esc_html__('Add Admission', 'school-management-system') . '</small></a>';
				} else {
					$add_admission = '';
				}

				// Table columns.
				$data[] = array(
					esc_html(WLSM_M_Class::get_label_text($row->class_label)),
					esc_html(WLSM_M_Staff_Class::get_name_text($row->name)),
					esc_html(WLSM_M_Staff_Class::get_phone_text($row->phone)),
					esc_html(WLSM_M_Staff_Class::get_name_text($row->email)),
					$view_message,
					esc_html(WLSM_Config::get_date_text($row->created_at)),
					esc_html($row->next_follow_up ? WLSM_Config::get_date_text($row->next_follow_up) : '-'),
					esc_html(WLSM_M_Staff_General::get_inquiry_status_text($row->is_active)) . $add_admission,
					'<a class="text-primary" href="' . esc_url($page_url . "&action=save&id=" . $row->ID) . '"><span class="dashicons dashicons-edit"></span></a>&nbsp;&nbsp;
					<a class="text-danger wlsm-delete-inquiry" data-nonce="' . esc_attr(wp_create_nonce('delete-inquiry-' . $row->ID)) . '" data-inquiry="' . esc_attr($row->ID) . '" href="#" data-message-title="' . esc_attr__('Please Confirm!', 'school-management-system') . '" data-message-content="' . esc_attr__('This will delete the inquiry.', 'school-management-system') . '" data-cancel="' . esc_attr__('Cancel', 'school-management-system') . '" data-submit="' . esc_attr__('Confirm', 'school-management-system') . '"><span class="dashicons dashicons-trash"></span></a>'
				);
			}
		}

		$output = array(
			'draw'            => intval(esc_sql($_POST['draw'])),
			'recordsTotal'    => $total_rows_count,
			'recordsFiltered' => $filter_rows_count,
			'data'            => $data,
			'export'          => array(
				'nonce'  => wp_create_nonce('export-staff-inquiries-table'),
				'action' => 'wlsm-export-staff-inquiries-table'
			)
		);

		echo json_encode($output);
		die();
	}

	public static function save_inquiry()
	{
		$current_user = WLSM_M_Role::can('manage_inquiries');

		if (! $current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		try {
			ob_start();
			global $wpdb;

			$inquiry_id = isset($_POST['inquiry_id']) ? absint($_POST['inquiry_id']) : 0;

			if ($inquiry_id) {
				if (! wp_verify_nonce($_POST['edit-inquiry-' . $inquiry_id], 'edit-inquiry-' . $inquiry_id)) {
					die();
				}
			} else {
				if (! wp_verify_nonce($_POST['add-inquiry'], 'add-inquiry')) {
					die();
				}
			}

			// Checks if inquiry exists.
			if ($inquiry_id) {
				$inquiry = WLSM_M_Staff_General::get_inquiry($school_id, $inquiry_id);

				if (! $inquiry) {
					throw new Exception(esc_html__('Inquiry not found.', 'school-management-system'));
				}
			}

			// Inquiry.
			$name     = isset($_POST['name']) ? sanitize_text_field($_POST['name']) : '';
			$phone    = isset($_POST['phone']) ? sanitize_text_field($_POST['phone']) : '';
			$email    = isset($_POST['email']) ? sanitize_email($_POST['email']) : '';
			$message  = isset($_POST['message']) ? sanitize_text_field($_POST['message']) : '';
			$class_id = isset($_POST['class_id']) ? absint($_POST['class_id']) : 0;

			// Status.
			$is_active      = isset($_POST['is_active']) ? (bool) $_POST['is_active'] : 1;
			$next_follow_up = isset($_POST['next_follow_up']) ? DateTime::createFromFormat(WLSM_Config::date_format(), sanitize_text_field($_POST['next_follow_up'])) : NULL;
			$note           = isset($_POST['note']) ? sanitize_text_field($_POST['note']) : '';

			// Start validation.
			$errors = array();

			if (empty($name)) {
				$errors['name'] = esc_html__('Please specify name.', 'school-management-system');
			}
			if (strlen($name) > 60) {
				$errors['name'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
			}

			if (! empty($phone) && strlen($phone) > 40) {
				$errors['phone'] = esc_html__('Maximum length cannot exceed 40 characters.', 'school-management-system');
			}

			if (! empty($email)) {
				if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
					$errors['email'] = esc_html__('Please provide a valid email.', 'school-management-system');
				} elseif (strlen($email) > 60) {
					$errors['email'] = esc_html__('Maximum length cannot exceed 60 characters.', 'school-management-system');
				}
			}

			if (! empty($next_follow_up)) {
				$next_follow_up = $next_follow_up->format('Y-m-d');
			} else {
				$next_follow_up = NULL;
			}

			if (! empty($class_id)) {
				$class_school = WLSM_M_Staff_Class::get_class($school_id, $class_id);
				if (! $class_school) {
					$errors['class_id'] = esc_html__('Class not found.', 'school-management-system');
				} else {
					$class_school_id = $class_school->ID;
				}
			} else {
				$class_school_id = NULL;
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			try {
				$wpdb->query('BEGIN;');

				// Inquiry data.
				$data = array(
					'name'            => $name,
					'phone'           => $phone,
					'email'           => $email,
					'message'         => $message,
					'note'            => $note,
					'is_active'       => $is_active,
					'next_follow_up'  => $next_follow_up,
					'class_school_id' => $class_school_id,
				);

				if ($inquiry_id) {
					$data['updated_at'] = current_time('mysql');

					$success = $wpdb->update(WLSM_INQUIRIES, $data, array('ID' => $inquiry_id, 'school_id' => $school_id));
				} else {
					$data['school_id'] = $school_id;

					$success = $wpdb->insert(WLSM_INQUIRIES, $data);
				}

				$buffer = ob_get_clean();
				if (! empty($buffer)) {
					throw new Exception($buffer);
				}

				if (false === $success) {
					throw new Exception($wpdb->last_error);
				}

				if ($inquiry_id) {
					$message = esc_html__('Inquiry updated successfully.', 'school-management-system');
					$reset   = false;
				} else {
					$message = esc_html__('Inquiry added successfully.', 'school-management-system');
					$reset   = true;
				}

				$wpdb->query('COMMIT;');

				wp_send_json_success(array('message' => $message, 'reset' => $reset));
			} catch (Exception $exception) {
				$wpdb->query('ROLLBACK;');
				wp_send_json_error($exception->getMessage());
			}
		}
		wp_send_json_error($errors);
	}

	public static function delete_inquiry()
	{
		$current_user = WLSM_M_Role::can('manage_inquiries');

		if (! $current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		try {
			ob_start();
			global $wpdb;

			$inquiry_id = isset($_POST['inquiry_id']) ? absint($_POST['inquiry_id']) : 0;

			if (! wp_verify_nonce($_POST['delete-inquiry-' . $inquiry_id], 'delete-inquiry-' . $inquiry_id)) {
				die();
			}

			// Checks if inquiry exists.
			$inquiry = WLSM_M_Staff_General::get_inquiry($school_id, $inquiry_id);

			if (! $inquiry) {
				throw new Exception(esc_html__('Inquiry not found.', 'school-management-system'));
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		try {
			$wpdb->query('BEGIN;');

			$success = $wpdb->delete(WLSM_INQUIRIES, array('ID' => $inquiry_id));
			$message = esc_html__('Inquiry deleted successfully.', 'school-management-system');

			$exception = ob_get_clean();
			if (! empty($exception)) {
				throw new Exception($exception);
			}

			if (false === $success) {
				throw new Exception($wpdb->last_error);
			}

			$wpdb->query('COMMIT;');

			wp_send_json_success(array('message' => $message));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}

	public static function view_inquiry_message()
	{
		$current_user = WLSM_M_Role::can('manage_inquiries');

		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		try {
			ob_start();
			global $wpdb;

			$inquiry_id = isset($_POST['inquiry_id']) ? absint($_POST['inquiry_id']) : 0;

			if (! wp_verify_nonce($_POST['view-inquiry-message-' . $inquiry_id], 'view-inquiry-message-' . $inquiry_id)) {
				die();
			}

			// Checks if inquiry exists.
			$inquiry = WLSM_M_Staff_General::get_inquiry_message($school_id, $inquiry_id);

			if (! $inquiry) {
				throw new Exception(esc_html__('Inquiry not found.', 'school-management-system'));
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		wp_send_json_success(esc_html(WLSM_Config::get_note_text($inquiry->message)));
	}

	public static function manage_promotion()
	{
		$current_user = WLSM_M_Role::can('manage_promote');

		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		$session_label = $current_user['session']['label'];

		if (! wp_verify_nonce($_POST['nonce'], 'manage-promotion')) {
			die();
		}

		try {
			ob_start();
			global $wpdb;

			$new_session_id = isset($_POST['promote_to_session']) ? absint($_POST['promote_to_session']) : 0;
			$from_class_id  = isset($_POST['from_class']) ? absint($_POST['from_class']) : 0;
			$to_class_id    = isset($_POST['to_class']) ? absint($_POST['to_class']) : 0;

			// Start validation.
			$errors = array();

			if (empty($new_session_id)) {
				$errors['promote_to_session'] = esc_html__('Please select new session.', 'school-management-system');
			} elseif ($session_id == $new_session_id) {
				$errors['promote_to_session'] = esc_html__('New session must be different from current session.', 'school-management-system');
			} elseif (! WLSM_M_Staff_General::is_next_session($session_id, $new_session_id)) {
				$errors['promote_to_session'] = esc_html__('Start date of new session must be after end date of current session.', 'school-management-system');
			} else {
				// Check if new session exists.
				$new_session = WLSM_M_Session::fetch_session($new_session_id);
				if (! $new_session) {
					$errors['promote_to_session'] = esc_html__('Session not found.', 'school-management-system');
				}
			}

			if (empty($from_class_id)) {
				$errors['from_class'] = esc_html__('Please select promotion from class.', 'school-management-system');
			} else {
				// Check if old class exists in the school.
				$from_class = WLSM_M_Staff_General::get_class_school($school_id, $from_class_id);

				if (! $from_class) {
					$errors['from_class'] = esc_html__('Class not found.', 'school-management-system');
				} else {
					// Get sections of old class.
					$from_sections = WLSM_M_Staff_General::fetch_class_sections($from_class->ID);
				}
			}

			if (empty($to_class_id)) {
				$errors['to_class'] = esc_html__('Please select promotion to class.', 'school-management-system');
			} else {
				if ($from_class_id == $to_class_id) {
					$errors['to_class'] = esc_html__('Promotion to class can\'t be the same.', 'school-management-system');
				} else {
					// Check if new class exists in the school.
					$to_class = WLSM_M_Staff_General::get_class_school($school_id, $to_class_id);

					if (! $to_class) {
						$errors['to_class'] = esc_html__('Class not found.', 'school-management-system');
					} else {
						// Get sections of new class.
						$to_sections = WLSM_M_Staff_General::fetch_class_sections($to_class->ID);
					}
				}
			}

			// Get class students in current session.
			$students = WLSM_M_Staff_General::get_class_students($school_id, $session_id, $from_class_id);
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			try {
				ob_start();

				if (count($students)) {
		?>
					<input type="hidden" name="promote_to_session_final" value="<?php echo esc_attr($new_session_id); ?>">
					<input type="hidden" name="from_class_final" value="<?php echo esc_attr($from_class_id); ?>">
					<input type="hidden" name="to_class_final" value="<?php echo esc_attr($to_class_id); ?>">

					<!-- Map sections. -->
					<div class="wlsm-form-section">
						<div class="row">
							<div class="col-md-12">
								<div class="wlsm-form-sub-heading wlsm-font-bold">
									<?php esc_html_e('Map Class Sections', 'school-management-system'); ?>
									<br>
									<small class="text-dark">
										<em><?php esc_html_e('Select sections mapping of old class to the new class.', 'school-management-system'); ?></em>
									</small>
								</div>
							</div>
						</div>

						<?php if (count($from_sections)) { ?>
							<div class="row mt-2 mb-1">
								<div class="col-md-6">
									<div class="wlsm-font-bold h6">
										<?php
										/* translators: %s: class name */
										printf(esc_html__('From Class: %s', 'school-management-system'), esc_html(WLSM_M_Class::get_label_text($from_class->label)));
										?>
									</div>
								</div>
								<div class="col-md-6">
									<div class="wlsm-font-bold h6">
										<?php
										/* translators: %s: class name */
										printf(esc_html__('To Class: %s', 'school-management-system'), esc_html(WLSM_M_Class::get_label_text($to_class->label)));
										?>
									</div>
								</div>
							</div>
						<?php } ?>

						<?php
						foreach ($from_sections as $key => $from_section) {
						?>
							<hr>
							<div class="form-row mt-1">
								<div class="form-group col-md-6">
									<label class="wlsm-font-bold" for="wlsm-from-section-<?php echo esc_attr($key); ?>">
										<span class="wlsm-important">*</span> <?php esc_html_e('Students From Section', 'school-management-system'); ?>:
									</label>
									<input type="hidden" name="from_section[]" value="<?php echo esc_attr($from_section->ID); ?>">
									<div class="ml-2">
										<?php echo esc_html(WLSM_M_Staff_Class::get_section_label_text($from_section->label)); ?>
									</div>
								</div>

								<div class="form-group col-md-6">
									<label class="wlsm-font-bold" for="wlsm-to-section-<?php echo esc_attr($key); ?>">
										<span class="wlsm-important">*</span> <?php esc_html_e('Assign to Section', 'school-management-system'); ?>:
									</label>
									<select name="to_section[]" id="wlsm-to-section-<?php echo esc_attr($key); ?>" class="form-control">
										<?php
										foreach ($to_sections as $key => $to_section) {
										?>
											<option value="<?php echo esc_attr($to_section->ID); ?>">
												<?php echo esc_html(WLSM_M_Staff_Class::get_section_label_text($to_section->label)); ?>
											</option>
										<?php
										}
										?>
									</select>
								</div>
							</div>
						<?php
						}
						?>
					</div>

					<!-- Students to be promoted. -->
					<div class="wlsm-form-section">
						<div class="row">
							<div class="col-md-12">
								<div class="wlsm-form-sub-heading wlsm-font-bold">
									<?php
									printf(
										wp_kses(
											/* translators: %s: class name */
											__('Students of Class: <span class="text-secondary">%s</span>', 'school-management-system'),
											array('span' => array('class' => array()))
										),
										esc_html(WLSM_M_Class::get_label_text($from_class->label))
									);
									?>
									<br>
									<small class="text-dark">
										<em><?php esc_html_e('Select students to enroll in next session.', 'school-management-system'); ?></em>
									</small>
								</div>
							</div>
						</div>
						<div class="table-responsive w-100">
							<table class="table table-bordered wlsm-students-to-promote-table">
								<thead>
									<tr class="bg-primary text-white">
										<th><input type="checkbox" name="select_all" id="wlsm-select-all" value="1"></th>
										<th><?php esc_html_e('Enrollment Number', 'school-management-system'); ?></th>
										<th><?php esc_html_e('Student Name', 'school-management-system'); ?></th>
										<th><?php esc_html_e('Section', 'school-management-system'); ?></th>
										<th><?php esc_html_e('Roll Number', 'school-management-system'); ?></th>
										<th><?php esc_html_e('Options', 'school-management-system'); ?></th>
									</tr>
								</thead>
								<tbody>
									<?php foreach ($students as $row) { ?>
										<tr>
											<td>
												<input type="checkbox" class="wlsm-select-single" name="student[<?php echo esc_attr($row->ID); ?>]" value="<?php echo esc_attr($row->ID); ?>">
											</td>
											<td>
												<?php echo esc_html($row->enrollment_number); ?>
											</td>
											<td>
												<?php echo esc_html(WLSM_M_Staff_Class::get_name_text($row->name)); ?>
											</td>
											<td>
												<?php echo esc_html(WLSM_M_Staff_Class::get_section_label_text($row->section_label)); ?>
											</td>
											<td>
												<?php echo esc_html(WLSM_M_Staff_Class::get_roll_no_text($row->roll_number)); ?>
											</td>
											<td>
												<select name="new_session_class[<?php echo esc_attr($row->ID); ?>]">
													<option value="<?php echo esc_attr($to_class_id); ?>">
														<?php
														/* translators: %s: class name */
														printf(esc_html__('Enroll to Class - %s', 'school-management-system'), esc_html(WLSM_M_Class::get_label_text($to_class->label)));
														?>
													</option>
													<option value="<?php echo esc_attr($from_class_id); ?>">
														<?php
														/* translators: %s: class name */
														printf(esc_html__('Enroll to Class - %s', 'school-management-system'), esc_html(WLSM_M_Class::get_label_text($from_class->label)));
														?>
													</option>
												</select>
											</td>
										</tr>
									<?php } ?>
								</tbody>
							</table>
						</div>
					</div>

					<div class="row mt-2 mb-2">
						<div class="col-md-12 text-center">
							<?php
							printf(
								wp_kses(
									/* translators: 1: current session, 2: new session */
									__('Session From <span class="wlsm-font-bold">%1$s</span> to <span class="wlsm-font-bold">%2$s</span>', 'school-management-system'),
									array('span' => array('class' => array()))
								),
								esc_html(WLSM_M_Session::get_label_text($session_label)),
								esc_html(WLSM_M_Session::get_label_text($new_session->label))
							);
							?>
						</div>
					</div>

					<div class="row mt-2">
						<div class="col-md-12 text-center">
							<button type="submit" class="btn btn-sm btn-success" id="wlsm-promote-student-btn" data-message-title="<?php esc_attr_e('Confirm Promotion!', 'school-management-system'); ?>" data-message-content="<?php esc_attr_e('Are you sure to enroll these selected students for the next session?', 'school-management-system'); ?>" data-submit="<?php esc_attr_e('Promote', 'school-management-system'); ?>" data-cancel="<?php esc_attr_e('Cancel', 'school-management-system'); ?>">
								<?php
								echo esc_html(_n('Promote Student', 'Promote Students', $students, 'school-management-system'));
								?>
							</button>
						</div>
					</div>
				<?php
				} else {
				?>
					<div class="alert alert-warning wlsm-font-bold">
						<i class="fas fa-exclamation-triangle"></i>
						<?php esc_html_e('There is no student in this class or students were already promoted.', 'school-management-system'); ?>
					</div>
				<?php
				}
				$html = ob_get_clean();

				wp_send_json_success(array('html' => $html));
			} catch (Exception $exception) {
				$buffer = ob_get_clean();
				if (! empty($buffer)) {
					$response = $buffer;
				} else {
					$response = $exception->getMessage();
				}
				wp_send_json_error($response);
			}
		}
		wp_send_json_error($errors);
	}

	public static function promote_student()
	{
		$current_user = WLSM_M_Role::can('manage_promote');

		if (! $current_user) {
			die();
		}

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		if (! wp_verify_nonce($_POST['promote-student-' . $session_id], 'promote-student-' . $session_id)) {
			die();
		}

		try {
			ob_start();
			global $wpdb;

			$new_session_id = isset($_POST['promote_to_session_final']) ? absint($_POST['promote_to_session_final']) : 0;
			$from_class_id  = isset($_POST['from_class_final']) ? absint($_POST['from_class_final']) : 0;
			$to_class_id    = isset($_POST['to_class_final']) ? absint($_POST['to_class_final']) : 0;

			// Start validation.
			if (empty($new_session_id)) {
				wp_send_json_error(esc_html__('Please select new session.', 'school-management-system'));
			} elseif ($session_id == $new_session_id) {
				wp_send_json_error(esc_html__('New session must be different from current session.', 'school-management-system'));
			} elseif (! WLSM_M_Staff_General::is_next_session($session_id, $new_session_id)) {
				wp_send_json_error(esc_html__('Start date of new session must be after end date of current session.', 'school-management-system'));
			} else {
				// Check if new session exists.
				$new_session = WLSM_M_Session::fetch_session($new_session_id);
				if (! $new_session) {
					wp_send_json_error(esc_html__('Session not found.', 'school-management-system'));
				}
			}

			if (empty($from_class_id)) {
				wp_send_json_error(esc_html__('Please select promotion from class.', 'school-management-system'));
			} else {
				// Check if old class exists in the school.
				$from_class = WLSM_M_Staff_General::get_class_school($school_id, $from_class_id);

				if (! $from_class) {
					wp_send_json_error(esc_html__('Class not found.', 'school-management-system'));
				} else {
					// Get sections of old class.
					$from_sections = WLSM_M_Staff_General::fetch_class_sections($from_class->ID);
				}
			}

			if (empty($to_class_id)) {
				wp_send_json_error(esc_html__('Please select promotion to class.', 'school-management-system'));
			} else {
				if ($from_class_id == $to_class_id) {
					wp_send_json_error(esc_html__('Promotion to class can\'t be the same.', 'school-management-system'));
				} else {
					// Check if new class exists in the school.
					$to_class = WLSM_M_Staff_General::get_class_school($school_id, $to_class_id);

					if (! $to_class) {
						wp_send_json_error(esc_html__('Class not found.', 'school-management-system'));
					} else {
						// Get sections of new class.
						$to_sections = WLSM_M_Staff_General::fetch_class_sections($to_class->ID);
					}
				}
			}

			// Get class students in current session.
			$students = WLSM_M_Staff_General::get_class_students_data($school_id, $session_id, $from_class_id);

			$all_student_ids = array_map(function ($student) {
				return $student->ID;
			}, $students);

			$all_from_sections_ids = array_map(function ($section) {
				return $section->ID;
			}, $from_sections);

			$all_to_sections_ids = array_map(function ($section) {
				return $section->ID;
			}, $to_sections);

			$from_section_ids = (isset($_POST['from_section']) && is_array($_POST['from_section'])) ? $_POST['from_section'] : array();
			$to_section_ids   = (isset($_POST['to_section']) && is_array($_POST['to_section'])) ? $_POST['to_section'] : array();
			$student_ids      = (isset($_POST['student']) && is_array($_POST['student'])) ? $_POST['student'] : array();
			$new_class_ids    = (isset($_POST['new_session_class']) && is_array($_POST['new_session_class'])) ? $_POST['new_session_class'] : array();

			$student_ids_keys   = array_keys($student_ids);
			$new_class_ids_keys = array_keys($new_class_ids);

			if (! count($student_ids)) {
				wp_send_json_error(esc_html__('Please select students.', 'school-management-system'));
			} elseif ((array_intersect($student_ids, $all_student_ids) != $student_ids) || ($student_ids_keys != array_values($student_ids))) {
				wp_send_json_error(esc_html__('Please select valid students.', 'school-management-system'));
			} elseif (array_intersect($student_ids_keys, $new_class_ids_keys) != $student_ids_keys) {
				wp_send_json_error(esc_html__('Invalid selection of students or new classes.', 'school-management-system'));
			}

			if (array_intersect($new_class_ids, array($from_class_id, $to_class_id)) != $new_class_ids) {
				wp_send_json_error(esc_html__('Please select valid class for each student.', 'school-management-system'));
			}

			if (count($all_from_sections_ids) != count($to_section_ids)) {
				wp_send_json_error(esc_html__('Please select corresponding new sections for mapping.', 'school-management-system'));
			} elseif (array_intersect($to_section_ids, $all_to_sections_ids) != $to_section_ids) {
				wp_send_json_error(esc_html__('Please select valid new sections for mapping.', 'school-management-system'));
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		try {
			$wpdb->query('BEGIN;');

			foreach ($student_ids_keys as $student_id) {
				if (isset($students[$student_id])) {
					$student = $students[$student_id];

					// Update old record.
					$old_student_data = array(
						'user_id'    => NULL,
						'is_active'  => 0,
						'updated_at' => current_time('mysql'),
					);

					$success = $wpdb->update(WLSM_STUDENT_RECORDS, $old_student_data, array('ID' => $student_id));

					$buffer = ob_get_clean();
					if (! empty($buffer)) {
						throw new Exception($buffer);
					}

					if (false === $success) {
						throw new Exception($wpdb->last_error);
					}

					// Insert new record.
					$old_section_id = $student->section_id;
					$section_index  = array_search($old_section_id, $from_section_ids);

					if ($from_class_id == $new_class_ids[$student_id]) {
						// Keep in the same class.
						$new_section_id = $old_section_id;
					} else {
						// Promote to a new class.
						$new_section_id = $to_section_ids[$section_index];
					}

					if (! $new_section_id) {
						throw new Exception(esc_html__('Please select corresponding new sections for mapping.', 'school-management-system'));
					}

					// Student data.
					$student_data = array(
						'enrollment_number' => WLSM_M_Staff_General::get_enrollment_number($school_id),
						'admission_number'  => $student->admission_number,
						'name'              => $student->name,
						'gender'            => $student->gender,
						'dob'               => $student->dob,
						'phone'             => $student->phone,
						'email'             => $student->email,
						'address'           => $student->address,
						'religion'          => $student->religion,
						'caste'             => $student->caste,
						'blood_group'       => $student->blood_group,
						'father_name'       => $student->father_name,
						'father_phone'      => $student->father_phone,
						'father_occupation' => $student->father_occupation,
						'mother_name'       => $student->mother_name,
						'mother_phone'      => $student->mother_phone,
						'mother_occupation' => $student->mother_occupation,
						'admission_date'    => $student->admission_date,
						'roll_number'       => $student->roll_number,
						'photo_id'          => $student->photo_id,
						'section_id'        => $new_section_id,
						'session_id'        => $new_session_id,
						'user_id'           => $student->user_id,
						'is_active'         => 1,
					);

					$success = $wpdb->insert(WLSM_STUDENT_RECORDS, $student_data);

					$buffer = ob_get_clean();
					if (! empty($buffer)) {
						throw new Exception($buffer);
					}

					if (false === $success) {
						throw new Exception($wpdb->last_error);
					}

					$old_student_id = $student_id;
					$new_student_id = $wpdb->insert_id;

					// Insert promotion record.
					$promotion_data = array(
						'from_student_record' => $old_student_id,
						'to_student_record'   => $new_student_id,
					);
					$success = $wpdb->insert(WLSM_PROMOTIONS, $promotion_data);

					$buffer = ob_get_clean();
					if (! empty($buffer)) {
						throw new Exception($buffer);
					}

					if (false === $success) {
						throw new Exception($wpdb->last_error);
					}
				} else {
					throw new Exception(esc_html__('Please select valid students.', 'school-management-system'));
				}
			}

			$wpdb->query('COMMIT;');

			$message = esc_html__('Students promoted successfully.', 'school-management-system');

			wp_send_json_success(array('message' => $message));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}

	public static function fetch_stats_payments()
	{
		$current_user = WLSM_M_Role::can('manage_invoices');

		if (! $current_user) {
			die();
		}

		if (! wp_verify_nonce($_REQUEST['security'], 'wlsm-security')) {
			die();
		}

		global $wpdb;

		$current_school = $current_user['school'];

		$can_delete_payments = WLSM_M_Role::check_permission(array('delete_payments'), $current_school['permissions']);

		$school_id  = $current_user['school']['id'];
		$session_id = $current_user['session']['ID'];

		$invoices_page_url = WLSM_M_Staff_Accountant::get_invoices_page_url();

		// Last 15 Payments
		$payments = $wpdb->get_results(
			$wpdb->prepare('SELECT sr.name as student_name, sr.admission_number, sr.phone, sr.father_name, sr.father_phone, p.ID, p.receipt_number, p.amount, p.payment_method, p.transaction_id, p.created_at, p.invoice_label, p.invoice_payable, p.invoice_id, i.label as invoice_title, c.label as class_label, se.label as section_label FROM %i as p JOIN %i as s ON s.ID = p.school_id JOIN %i as sr ON sr.ID = p.student_record_id JOIN %i as ss ON ss.ID = sr.session_id JOIN %i as se ON se.ID = sr.section_id JOIN %i as cs ON cs.ID = se.class_school_id JOIN %i as c ON c.ID = cs.class_id LEFT OUTER JOIN %i as i ON i.ID = p.invoice_id WHERE p.school_id = %d AND ss.ID = %d GROUP BY p.ID ORDER BY p.ID DESC LIMIT 15', WLSM_PAYMENTS, WLSM_SCHOOLS, WLSM_STUDENT_RECORDS, WLSM_SESSIONS, WLSM_SECTIONS, WLSM_CLASS_SCHOOL, WLSM_CLASSES, WLSM_INVOICES, $school_id, $session_id)
		);

		$output['data'] = array();

		foreach ($payments as $row) {
			if ($row->invoice_id) {
				$invoice_title = '<a target="_blank" href="' . esc_url($invoices_page_url . '&action=save&id=' . $row->invoice_id) . '">' . esc_html(WLSM_M_Staff_Accountant::get_invoice_title_text($row->invoice_title)) . '</a>';
			} else {
				$invoice_title = '<span class="text-danger">' . esc_html__('Deleted', 'school-management-system') . '<br><span class="text-secondary">' . $row->invoice_label . '<br><small>' . esc_html(WLSM_Config::get_money_text($row->invoice_payable))  . ' ' . esc_html__('Payable', 'school-management-system') . '</small></span></span>';
			}

			$data = array(
				esc_html(WLSM_M_Invoice::get_receipt_number_text($row->receipt_number)),
				esc_html(WLSM_Config::get_money_text($row->amount)),
				esc_html(WLSM_M_Invoice::get_payment_method_text($row->payment_method)),
				esc_html(WLSM_M_Invoice::get_transaction_id_text($row->transaction_id)),
				esc_html(WLSM_Config::get_date_text($row->created_at)),
				$invoice_title,
				esc_html(WLSM_M_Staff_Class::get_name_text($row->student_name)),
				esc_html(WLSM_M_Staff_Class::get_admission_no_text($row->admission_number)),
				esc_html(WLSM_M_Class::get_label_text($row->class_label)),
				esc_html(WLSM_M_Staff_Class::get_section_label_text($row->section_label)),
				esc_html(WLSM_M_Staff_Class::get_phone_text($row->phone)),
				esc_html(WLSM_M_Staff_Class::get_name_text($row->father_name)),
				esc_html(WLSM_M_Staff_Class::get_phone_text($row->father_phone))
			);

			if ($can_delete_payments) {
				ob_start();
				?>
				<a class="text-danger wlsm-delete-payment" data-nonce="<?php echo esc_attr(wp_create_nonce('delete-payment-' . $row->ID)); ?>" data-payment="<?php echo esc_attr($row->ID); ?>" href="#" data-message-title="<?php esc_attr_e('Please Confirm!', 'school-management-system'); ?>" data-message-content="<?php esc_attr_e('This will delete the payment.', 'school-management-system'); ?>" data-cancel="<?php esc_attr_e('Cancel', 'school-management-system'); ?>" data-submit="<?php esc_attr_e('Confirm', 'school-management-system'); ?>"><span class="dashicons dashicons-trash"></span></a>
<?php
				$delete_payment = ob_get_clean();
				array_push($data, $delete_payment);
			}

			$output['data'][] = $data;
		}

		echo json_encode($output);
		die();
	}

	public static function save_school_general_settings()
	{
		$current_user = WLSM_M_Role::can('manage_settings');

		if (! $current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		try {
			ob_start();
			global $wpdb;

			if (! wp_verify_nonce($_POST['save-school-general-settings'], 'save-school-general-settings')) {
				die();
			}

			$school_logo        = (isset($_FILES['school_logo']) && is_array($_FILES['school_logo'])) ? $_FILES['school_logo'] : NULL;
			$remove_school_logo = isset($_POST['remove_school_logo']) ? (bool) $_POST['remove_school_logo'] : 0;

			// Start validation.
			$errors = array();

			if (! $remove_school_logo && (isset($school_logo['tmp_name']) && ! empty($school_logo['tmp_name']))) {
				$finfo = finfo_open(FILEINFO_MIME_TYPE);
				$mime  = finfo_file($finfo, $school_logo['tmp_name']);
				finfo_close($finfo);

				if (! in_array($mime, WLSM_Helper::get_image_mime())) {
					$errors['school_logo'] = esc_html__('Please provide school logo in JPG, JPEG or PNG format.', 'school-management-system');
				}
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			try {
				$wpdb->query('BEGIN;');

				$general = $wpdb->get_row($wpdb->prepare('SELECT ID, setting_value FROM %i WHERE school_id = %d AND setting_key = "general"', WLSM_SETTINGS, $school_id));

				if ($remove_school_logo) {
					$school_logo = NULL;
				} else {
					if (! empty($school_logo)) {
						$school_logo = media_handle_upload('school_logo', 0);
						if (is_wp_error($school_logo)) {
							throw new Exception($school_logo->get_error_message());
						}
					}
				}

				if (! $school_logo) {
					$school_logo = NULL;
				}

				$general_data = array(
					'school_logo' => $school_logo,
				);

				if (! $general) {
					$wpdb->insert(
						WLSM_SETTINGS,
						array(
							'setting_key'   => 'general',
							'setting_value' => serialize($general_data),
							'school_id'     => $school_id,
						)
					);
				} else {
					$general_saved_data = unserialize($general->setting_value);

					if (isset($general_saved_data['school_logo']) && ! empty($general_saved_data['school_logo'])) {
						if ($remove_school_logo) {
							// If remove school logo is checked, delete saved logo.
							$school_logo_delete_id = $general_saved_data['school_logo'];
						} elseif (! $general_data['school_logo']) {
							// If no school logo is provided from input, use saved school logo.
							$general_data['school_logo'] = $general_saved_data['school_logo'];
						} else {
							// If school logo is provided from input, delete saved school logo.
							$school_logo_delete_id = $general_saved_data['school_logo'];
						}
					}
					$wpdb->update(
						WLSM_SETTINGS,
						array('setting_value' => serialize($general_data)),
						array('ID'            => $general->ID)
					);
					if (isset($school_logo_delete_id)) {
						wp_delete_attachment($school_logo_delete_id, true);
					}
				}

				$buffer = ob_get_clean();
				if (! empty($buffer)) {
					throw new Exception($buffer);
				}

				$wpdb->query('COMMIT;');

				$message = esc_html__('General settings saved.', 'school-management-system');

				wp_send_json_success(array('message' => $message));
			} catch (Exception $exception) {
				$wpdb->query('ROLLBACK;');
				wp_send_json_error($exception->getMessage());
			}
		}
		wp_send_json_error($errors);
	}

	public static function save_school_email_carrier_settings()
	{
		$current_user = WLSM_M_Role::can('manage_settings');

		if (! $current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		try {
			ob_start();
			global $wpdb;

			if (! wp_verify_nonce($_POST['save-school-email-carrier-settings'], 'save-school-email-carrier-settings')) {
				die();
			}

			$email_carrier = isset($_POST['email_carrier']) ? sanitize_text_field($_POST['email_carrier']) : 'wp_mail';

			$wp_mail_from_name  = isset($_POST['wp_mail_from_name']) ? sanitize_text_field($_POST['wp_mail_from_name']) : '';
			$wp_mail_from_email = isset($_POST['wp_mail_from_email']) ? sanitize_text_field($_POST['wp_mail_from_email']) : '';

			$smtp_from_name  = isset($_POST['smtp_from_name']) ? sanitize_text_field($_POST['smtp_from_name']) : '';
			$smtp_host       = isset($_POST['smtp_host']) ? sanitize_text_field($_POST['smtp_host']) : '';
			$smtp_username   = isset($_POST['smtp_username']) ? sanitize_text_field($_POST['smtp_username']) : '';
			$smtp_password   = isset($_POST['smtp_password']) ? $_POST['smtp_password'] : '';
			$smtp_encryption = isset($_POST['smtp_encryption']) ? sanitize_text_field($_POST['smtp_encryption']) : '';
			$smtp_port       = isset($_POST['smtp_port']) ? sanitize_text_field($_POST['smtp_port']) : '';

			// Start validation.
			$errors = array();

			if (! in_array($email_carrier, array_keys(WLSM_Email::email_carriers()))) {
				$errors['email_carrier'] = esc_html__('Please select a valid email carrier.', 'school-management-system');
			}

			if (! empty($wp_mail_from_email) && ! filter_var($wp_mail_from_email, FILTER_VALIDATE_EMAIL)) {
				$errors['wp_mail_from_email'] = esc_html__('Please provide a valid email.', 'school-management-system');
			}
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		if (count($errors) < 1) {
			try {
				$wpdb->query('BEGIN;');

				// Email Carrier.
				$email = $wpdb->get_row($wpdb->prepare('SELECT ID, setting_value FROM %i WHERE school_id = %d AND setting_key = "email"', WLSM_SETTINGS, $school_id));

				$email_data = array(
					'carrier' => $email_carrier,
				);

				if (! $email) {
					$wpdb->insert(
						WLSM_SETTINGS,
						array(
							'setting_key'   => 'email',
							'setting_value' => serialize($email_data),
							'school_id'     => $school_id,
						)
					);
				} else {
					$wpdb->update(
						WLSM_SETTINGS,
						array('setting_value' => serialize($email_data)),
						array('ID'            => $email->ID)
					);
				}

				// WP_Mail.
				$wp_mail = $wpdb->get_row($wpdb->prepare('SELECT ID, setting_value FROM %i WHERE school_id = %d AND setting_key = "wp_mail"', WLSM_SETTINGS, $school_id));

				$wp_mail_data = array(
					'from_name'  => $wp_mail_from_name,
					'from_email' => $wp_mail_from_email,
				);

				if (! $wp_mail) {
					$wpdb->insert(
						WLSM_SETTINGS,
						array(
							'setting_key'   => 'wp_mail',
							'setting_value' => serialize($wp_mail_data),
							'school_id'     => $school_id,
						)
					);
				} else {
					$wpdb->update(
						WLSM_SETTINGS,
						array('setting_value' => serialize($wp_mail_data)),
						array('ID'            => $wp_mail->ID)
					);
				}

				// SMTP.
				$smtp = $wpdb->get_row($wpdb->prepare('SELECT ID, setting_value FROM %i WHERE school_id = %d AND setting_key = "smtp"', WLSM_SETTINGS, $school_id));

				$smtp_data = array(
					'from_name'  => $smtp_from_name,
					'host'       => $smtp_host,
					'username'   => $smtp_username,
					'password'   => $smtp_password,
					'encryption' => $smtp_encryption,
					'port'       => $smtp_port,
				);

				if (! $smtp) {
					$wpdb->insert(
						WLSM_SETTINGS,
						array(
							'setting_key'   => 'smtp',
							'setting_value' => serialize($smtp_data),
							'school_id'     => $school_id,
						)
					);
				} else {
					$smtp_saved_data = unserialize($smtp->setting_value);

					if (empty($smtp_data['password'])) {
						$smtp_data['password'] = $smtp_saved_data['password'];
					}

					$wpdb->update(
						WLSM_SETTINGS,
						array('setting_value' => serialize($smtp_data)),
						array('ID'            => $smtp->ID)
					);
				}

				$buffer = ob_get_clean();
				if (! empty($buffer)) {
					throw new Exception($buffer);
				}

				$wpdb->query('COMMIT;');

				$message = esc_html__('Email settings saved.', 'school-management-system');

				wp_send_json_success(array('message' => $message));
			} catch (Exception $exception) {
				$wpdb->query('ROLLBACK;');
				wp_send_json_error($exception->getMessage());
			}
		}
		wp_send_json_error($errors);
	}

	public static function save_school_email_templates_settings()
	{
		$current_user = WLSM_M_Role::can('manage_settings');

		if (! $current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		try {
			ob_start();
			global $wpdb;

			if (! wp_verify_nonce($_POST['save-school-email-templates-settings'], 'save-school-email-templates-settings')) {
				die();
			}

			$email_student_admission_enable  = isset($_POST['email_student_admission_enable']) ? (bool) ($_POST['email_student_admission_enable']) : 0;
			$email_student_admission_subject = isset($_POST['email_student_admission_subject']) ? sanitize_text_field(stripslashes($_POST['email_student_admission_subject'])) : '';
			$email_student_admission_body    = isset($_POST['email_student_admission_body']) ? wp_kses_post(stripslashes($_POST['email_student_admission_body'])) : '';

			$email_invoice_generated_enable  = isset($_POST['email_invoice_generated_enable']) ? (bool) ($_POST['email_invoice_generated_enable']) : 0;
			$email_invoice_generated_subject = isset($_POST['email_invoice_generated_subject']) ? sanitize_text_field(stripslashes($_POST['email_invoice_generated_subject'])) : '';
			$email_invoice_generated_body    = isset($_POST['email_invoice_generated_body']) ? wp_kses_post(stripslashes($_POST['email_invoice_generated_body'])) : '';

			$email_online_fee_submission_enable  = isset($_POST['email_online_fee_submission_enable']) ? (bool) ($_POST['email_online_fee_submission_enable']) : 0;
			$email_online_fee_submission_subject = isset($_POST['email_online_fee_submission_subject']) ? sanitize_text_field(stripslashes($_POST['email_online_fee_submission_subject'])) : '';
			$email_online_fee_submission_body    = isset($_POST['email_online_fee_submission_body']) ? wp_kses_post(stripslashes($_POST['email_online_fee_submission_body'])) : '';

			$email_offline_fee_submission_enable  = isset($_POST['email_offline_fee_submission_enable']) ? (bool) ($_POST['email_offline_fee_submission_enable']) : 0;
			$email_offline_fee_submission_subject = isset($_POST['email_offline_fee_submission_subject']) ? sanitize_text_field(stripslashes($_POST['email_offline_fee_submission_subject'])) : '';
			$email_offline_fee_submission_body    = isset($_POST['email_offline_fee_submission_body']) ? wp_kses_post(stripslashes($_POST['email_offline_fee_submission_body'])) : '';
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		try {
			$wpdb->query('BEGIN;');

			// Email Student Admission.
			$email_student_admission = $wpdb->get_row($wpdb->prepare('SELECT ID, setting_value FROM %i WHERE school_id = %d AND setting_key = "email_student_admission"', WLSM_SETTINGS, $school_id));

			$email_student_admission_data = array(
				'enable'  => $email_student_admission_enable,
				'subject' => $email_student_admission_subject,
				'body'    => $email_student_admission_body,
			);

			if (! $email_student_admission) {
				$wpdb->insert(
					WLSM_SETTINGS,
					array(
						'setting_key'   => 'email_student_admission',
						'setting_value' => serialize($email_student_admission_data),
						'school_id'     => $school_id,
					)
				);
			} else {
				$wpdb->update(
					WLSM_SETTINGS,
					array('setting_value' => serialize($email_student_admission_data)),
					array('ID'            => $email_student_admission->ID)
				);
			}

			// Email Invoice Generated.
			$email_invoice_generated = $wpdb->get_row($wpdb->prepare('SELECT ID, setting_value FROM %i WHERE school_id = %d AND setting_key = "email_invoice_generated"', WLSM_SETTINGS, $school_id));

			$email_invoice_generated_data = array(
				'enable'  => $email_invoice_generated_enable,
				'subject' => $email_invoice_generated_subject,
				'body'    => $email_invoice_generated_body,
			);

			if (! $email_invoice_generated) {
				$wpdb->insert(
					WLSM_SETTINGS,
					array(
						'setting_key'   => 'email_invoice_generated',
						'setting_value' => serialize($email_invoice_generated_data),
						'school_id'     => $school_id,
					)
				);
			} else {
				$wpdb->update(
					WLSM_SETTINGS,
					array('setting_value' => serialize($email_invoice_generated_data)),
					array('ID'            => $email_invoice_generated->ID)
				);
			}

			// Email Online Fee Submission.
			$email_online_fee_submission = $wpdb->get_row($wpdb->prepare('SELECT ID, setting_value FROM %i WHERE school_id = %d AND setting_key = "email_online_fee_submission"', WLSM_SETTINGS, $school_id));

			$email_online_fee_submission_data = array(
				'enable'  => $email_online_fee_submission_enable,
				'subject' => $email_online_fee_submission_subject,
				'body'    => $email_online_fee_submission_body,
			);

			if (! $email_online_fee_submission) {
				$wpdb->insert(
					WLSM_SETTINGS,
					array(
						'setting_key'   => 'email_online_fee_submission',
						'setting_value' => serialize($email_online_fee_submission_data),
						'school_id'     => $school_id,
					)
				);
			} else {
				$wpdb->update(
					WLSM_SETTINGS,
					array('setting_value' => serialize($email_online_fee_submission_data)),
					array('ID'            => $email_online_fee_submission->ID)
				);
			}

			// Email Offline Fee Submission.
			$email_offline_fee_submission = $wpdb->get_row($wpdb->prepare('SELECT ID, setting_value FROM %i WHERE school_id = %d AND setting_key = "email_offline_fee_submission"', WLSM_SETTINGS, $school_id));

			$email_offline_fee_submission_data = array(
				'enable'  => $email_offline_fee_submission_enable,
				'subject' => $email_offline_fee_submission_subject,
				'body'    => $email_offline_fee_submission_body,
			);

			if (! $email_offline_fee_submission) {
				$wpdb->insert(
					WLSM_SETTINGS,
					array(
						'setting_key'   => 'email_offline_fee_submission',
						'setting_value' => serialize($email_offline_fee_submission_data),
						'school_id'     => $school_id,
					)
				);
			} else {
				$wpdb->update(
					WLSM_SETTINGS,
					array('setting_value' => serialize($email_offline_fee_submission_data)),
					array('ID'            => $email_offline_fee_submission->ID)
				);
			}

			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				throw new Exception($buffer);
			}

			$wpdb->query('COMMIT;');

			$message = esc_html__('Email templates saved.', 'school-management-system');

			wp_send_json_success(array('message' => $message));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}

	public static function save_school_payment_method_settings()
	{
		$current_user = WLSM_M_Role::can('manage_settings');

		if (! $current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		try {
			ob_start();
			global $wpdb;

			if (! wp_verify_nonce($_POST['save-school-payment-method-settings'], 'save-school-payment-method-settings')) {
				die();
			}

			$stripe_enable          = isset($_POST['stripe_enable']) ? (bool) ($_POST['stripe_enable']) : 0;
			$stripe_publishable_key = isset($_POST['stripe_publishable_key']) ? sanitize_text_field($_POST['stripe_publishable_key']) : '';
			$stripe_secret_key      = isset($_POST['stripe_secret_key']) ? sanitize_text_field($_POST['stripe_secret_key']) : '';
		} catch (Exception $exception) {
			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				$response = $buffer;
			} else {
				$response = $exception->getMessage();
			}
			wp_send_json_error($response);
		}

		try {
			$wpdb->query('BEGIN;');

			// Stripe.
			$stripe = $wpdb->get_row($wpdb->prepare('SELECT ID, setting_value FROM %i WHERE school_id = %d AND setting_key = "stripe"', WLSM_SETTINGS, $school_id));

			$stripe_data = array(
				'enable'          => $stripe_enable,
				'publishable_key' => $stripe_publishable_key,
				'secret_key'      => $stripe_secret_key,
			);

			if (! $stripe) {
				$wpdb->insert(
					WLSM_SETTINGS,
					array(
						'setting_key'   => 'stripe',
						'setting_value' => serialize($stripe_data),
						'school_id'     => $school_id,
					)
				);
			} else {
				$wpdb->update(
					WLSM_SETTINGS,
					array('setting_value' => serialize($stripe_data)),
					array('ID'            => $stripe->ID)
				);
			}

			$buffer = ob_get_clean();
			if (! empty($buffer)) {
				throw new Exception($buffer);
			}

			$wpdb->query('COMMIT;');

			$message = esc_html__('Payment settings saved.', 'school-management-system');

			wp_send_json_success(array('message' => $message));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}

	public static function save_school_registration_settings()
	{
		$current_user = WLSM_M_Role::can('manage_settings');

		if (!$current_user) {
			die();
		}

		$school_id = $current_user['school']['id'];

		if (!wp_verify_nonce($_POST['save-school-registration-settings'], 'save-school-registration-settings')) {
			die();
		}

		global $wpdb;

		try {
			$wpdb->query('BEGIN;');

			$form_title            = isset($_POST['registration_form_title']) ? sanitize_text_field($_POST['registration_form_title']) : '';
			$login_user            = isset($_POST['registration_login_user']) ? (bool) ($_POST['registration_login_user']) : '';
			$redirect_url          = isset($_POST['redirect_url']) ? esc_url_raw($_POST['redirect_url']) : '';
			$create_invoice        = isset($_POST['registration_create_invoice']) ? (bool) ($_POST['registration_create_invoice']) : '';
			$auto_admission_number = isset($_POST['registration_auto_admission_number']) ? (bool) ($_POST['registration_auto_admission_number']) : '';
			$auto_roll_number      = isset($_POST['registration_auto_roll_number']) ? (bool) ($_POST['registration_auto_roll_number']) : '';
			$success_message       = isset($_POST['registration_success_message']) ? sanitize_text_field($_POST['registration_success_message']) : '';
			$admin_phone           = isset($_POST['registration_admin_phone']) ? sanitize_text_field($_POST['registration_admin_phone']) : '';
			$admin_email           = isset($_POST['registration_admin_email']) ? sanitize_text_field($_POST['registration_admin_email']) : '';

			// Registraistion form options

			$dob               = isset($_POST['registration_dob']) ? (bool) ($_POST['registration_dob']) : '';
			$religion          = isset($_POST['registration_religion']) ? (bool) ($_POST['registration_religion']) : '';
			$caste             = isset($_POST['registration_caste']) ? (bool) ($_POST['registration_caste']) : '';
			$blood_group       = isset($_POST['registration_blood_group']) ? (bool) ($_POST['registration_blood_group']) : '';
			$phone             = isset($_POST['registration_phone']) ? (bool) ($_POST['registration_phone']) : '';
			$city              = isset($_POST['registration_city']) ? (bool) ($_POST['registration_city']) : '';
			$state             = isset($_POST['registration_state']) ? (bool) ($_POST['registration_state']) : '';
			$country           = isset($_POST['registration_country']) ? (bool) ($_POST['registration_country']) : '';
			$transport         = isset($_POST['registration_transport']) ? (bool) ($_POST['registration_transport']) : '';
			$parent_detail     = isset($_POST['registration_parent_detail']) ? (bool) ($_POST['registration_parent_detail']) : '';
			$parent_occupation = isset($_POST['registration_parent_occupation']) ? (bool) ($_POST['registration_parent_occupation']) : '';
			$parent_login      = isset($_POST['registration_parent_login']) ? (bool) ($_POST['registration_parent_login']) : '';
			$id_number         = isset($_POST['registration_id_number']) ? (bool) ($_POST['registration_id_number']) : '';
			$survey            = isset($_POST['registration_survey']) ? (bool) ($_POST['registration_survey']) : '';
			$fees              = isset($_POST['registration_fees']) ? (bool) ($_POST['registration_fees']) : '';
			$student_aprove    = isset($_POST['student_aprove']) ? (bool) ($_POST['student_aprove']) : '';

			// Settings Registration.
			$settings_registration = $wpdb->get_row($wpdb->prepare('SELECT ID, setting_value FROM %i WHERE school_id = %d AND setting_key = "registration"', WLSM_SETTINGS, $school_id));

			$registration_data = array(
				'form_title'            => $form_title,
				'login_user'            => $login_user,
				'redirect_url'          => $redirect_url,
				'create_invoice'        => $create_invoice,
				'auto_admission_number' => $auto_admission_number,
				'auto_roll_number'      => $auto_roll_number,
				'success_message'       => $success_message,
				'admin_phone'           => $admin_phone,
				'admin_email'           => $admin_email,
				'student_aprove'        => $student_aprove,
				'dob'               => $dob,
				'religion'          => $religion,
				'caste'             => $caste,
				'blood_group'       => $blood_group,
				'phone'             => $phone,
				'city'              => $city,
				'state'             => $state,
				'country'           => $country,
				'transport'         => $transport,
				'parent_detail'     => $parent_detail,
				'parent_occupation' => $parent_occupation,
				'id_number'         => $id_number,
				'survey'            => $survey,
				'fees'              => $fees,
				'parent_login'      => $parent_login
			);

			if (!$settings_registration) {
				$wpdb->insert(
					WLSM_SETTINGS,
					array(
						'setting_key'   => 'registration',
						'setting_value' => serialize($registration_data),
						'school_id'     => $school_id,
					)
				);
			} else {
				$wpdb->update(
					WLSM_SETTINGS,
					array('setting_value' => serialize($registration_data)),
					array('ID'            => $settings_registration->ID)
				);
			}

			$buffer = ob_get_clean();
			if (!empty($buffer)) {
				throw new Exception($buffer);
			}

			$wpdb->query('COMMIT;');

			$message = esc_html__('Registration settings saved.', 'school-management-system');

			wp_send_json_success(array('message' => $message));
		} catch (Exception $exception) {
			$wpdb->query('ROLLBACK;');
			wp_send_json_error($exception->getMessage());
		}
	}
}
