<?php

/**
 * Result Management System for Institutions
 *
 * Copyright (C) 2025 ASTGD
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/**
 * This file is part of the Result Management System for Institutions plugin.
 *
 * It ensures that the file is not accessed directly by checking if the
 * ABSPATH constant is defined. If ABSPATH is not defined, the script exits.
 *
 * @package ResultManagementSystem
 * @subpackage Menu_Files/Show_Result
 * @since 1.0.0
 */
if (! defined('ABSPATH')) {
	exit;
}

require_once ASTGDRMSYS_PLUGIN_DIR . 'includes/traits/class-astgdrmsys-grade-helper.php';
require_once ASTGDRMSYS_PLUGIN_DIR . 'includes/phpqrcode/phpqrcode.php';
require_once ASTGDRMSYS_PLUGIN_DIR . 'includes/traits/class-astgdrmsys-exam-label-helper.php';
require_once ASTGDRMSYS_PLUGIN_DIR . 'includes/traits/class-astgdrmsys-student-custom-field-helper.php';
require_once ASTGDRMSYS_PLUGIN_DIR . 'includes/traits/class-astgdrmsys-retake-helper.php';

/**
 * Class ASTGDRMSYS_Show_Result
 *
 * This class is responsible for managing the results in the RMS (Result Management System) plugin.
 * It is part of the "Result Management System" plugin and is located in the "show-result" menu files.
 *
 * @package ResultManagementSystem
 * @subpackage Menu_Files/Show_Result
 * @since 1.0.0
 */

class ASTGDRMSYS_Show_Result
{
	use ASTGDRMSYS_Grade_Helper;
	use ASTGDRMSYS_Exam_Label_Helper;
	use ASTGDRMSYS_Student_Custom_Field_Helper;
	use ASTGDRMSYS_Retake_Helper;

	private $wpdb;
	private $prefix;
	private $atts = [];
        private $view_settings    = array();

	/**
	 * Constructor for the class.
	 *
	 * This method initializes the class and sets up any necessary properties or methods.
	 */
	public function __construct($atts = [])
	{
		global $wpdb;
		$this->wpdb   = $wpdb;
		$this->prefix = $wpdb->prefix;
		$this->atts = $atts;
		// View Settings
		$this->load_view_settings($wpdb);
	}

	/**
	 * Loads the view settings for the result management plugin.
	 *
	 * This function is responsible for loading and configuring the settings
	 * required to display the results in the plugin's interface.
	 *
	 * @return void
	 */
        private function load_view_settings($wpdb)
        {
                $cache_key           = 'view_settings';
                $this->view_settings = wp_cache_get($cache_key);

		if ($this->view_settings === false) {
			$sql = "SELECT * FROM `{$this->prefix}astgdrmsys_view_settings`";

			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery
			$this->view_settings = $wpdb->get_results($sql, ARRAY_A);

			wp_cache_set($cache_key, $this->view_settings);
                }
        }

        /**
         * Determines if the shortcode requires both roll and registration numbers.
         */
        private function requires_registration_number(): bool
        {
                return ! empty($this->atts['require_registration']) && $this->atts['require_registration'] !== '0';
        }

	/**
	 * Renders the result management interface.
	 *
	 * This function is responsible for generating and displaying the result
	 * management interface within the plugin. It handles the necessary logic
	 * to retrieve and present the results to the user.
	 *
	 * @return void
	 */
	public function astgdrmsys_showresult_render()
	{
		if (isset($_GET['data'])) {
			$decrypted = $this->decrypt_query($_GET['data']);
			parse_str($decrypted, $params);

			// Simulate POST values from decrypted data
			$_POST['sclass']      = isset($params['class']) ? $params['class'] : '';
			$_POST['sdepartment'] = isset($params['department']) ? $params['department'] : '';
			$_POST['ssection']    = isset($params['section']) ? $params['section'] : '';
			$_POST['s_exam_name'] = isset($params['exam_name']) && isset($params['exam_year'])
				? $params['exam_name'] . ',' . $params['exam_year']	: '';
			$_POST['sno']         = isset($params['roll']) ? $params['roll'] : '';
			$_POST['username']    = isset($params['username']) ? $params['username'] : wp_get_current_user()->user_login;

			// Optionally disable nonce check for direct links
			$_POST['_wpnonce']    = wp_create_nonce('astgdrmsys_exam_search');
			$_POST['submit'] = '1';

			$this->process_result_request();
			return;
		}
		$this->astgdrmsys_render_search_form();
		$this->process_result_request();
		
	}

	private function encrypt_query($queryString)
	{
		$key = ASTGDRMSYS_SECRET_KEY;
		$iv = ASTGDRMSYS_SECRET_IV;
		$encrypted = openssl_encrypt($queryString, ASTGDRMSYS_ENCRYPT_METHOD, $key, 0, $iv);
		return urlencode(base64_encode($encrypted));
	}

	private function decrypt_query($encryptedQuery)
	{
		$key = ASTGDRMSYS_SECRET_KEY;
		$iv = ASTGDRMSYS_SECRET_IV;
		$decoded = base64_decode(urldecode($encryptedQuery));
		return openssl_decrypt($decoded, ASTGDRMSYS_ENCRYPT_METHOD, $key, 0, $iv);
	}
	/**
	 * Renders the search form for the result management.
	 *
	 * This function generates and displays the search form used to search for results.
	 *
	 * @return void
	 */
	public function astgdrmsys_render_search_form()
	{
		$exam_context  = $this->get_selected_exam_context();
		$restrict_user = $this->fetch_restrict_user($exam_context['exam_id']);

		// Check if user restriction is enabled and user is not logged in
		if ($restrict_user && ! is_user_logged_in()) {
			// Display a friendly message instead of redirecting
			wp_die(
				sprintf(
					__(
						'<h2 style="color:#d63638;">Access Restricted</h2>
						<p>You must be logged in to view this page.</p>
						<p><a href="%s" class="button button-primary">Click here to log in</a></p>'
					),
					esc_url(wp_login_url(get_permalink()))
				)
			);
		}
		
		// Fetch classes and search method
                $classes       = $this->get_classes();
                $search_method = $this->get_search_method();
                $requires_registration = $this->requires_registration_number();
		
		// Display options for the result marksheet
		$display_options      = $this->astgdrmsys_disply_options();
		$display_class_name   = !empty($display_options['class']) ? $display_options['class'] : 'Class';
                $display_roll         = !empty($display_options['roll']) ? $display_options['roll'] : 'Roll No';
                $display_regno        = !empty($display_options['regno']) ? $display_options['regno'] : 'Registration No';
                $display_dept_name    = !empty($display_options['dept']) ? $display_options['dept'] : 'Department';
                $display_section_name = !empty($display_options['section']) ? $display_options['section'] : 'Section';
                $display_exam_name    = !empty($display_options['exam_name']) ? $display_options['exam_name'] : 'Exam Name'; ?>

		<center>
			<form action="<?php echo esc_url(add_query_arg(array(), isset($_SERVER['REQUEST_URI']) ? sanitize_url(wp_unslash($_SERVER['REQUEST_URI'])) : '') . '#print'); ?>" method="post">
				<?php wp_nonce_field('student_result_form', 'student_result_nonce'); ?>
				<?php wp_nonce_field('astgdrmsys_exam_search'); ?>
				<table class='table searchtable' id='table'>
					<!-- Class Dropdown OR Hidden Input -->
                                        <?php if (!empty($this->atts['sclass'])) : ?>
                                                <input type="hidden" class="selectClass" name="sclass" value="<?php echo esc_attr($this->atts['sclass']); ?>">
					<?php else : ?>
						<tr>
							<td><strong>Select <?php echo esc_html($display_class_name); ?></strong></td>
							<td>
								<center>
									<select name="sclass" class="selectClass" required>
										<option value="" selected disabled>Select <?php echo esc_html($display_class_name); ?></option>
										<?php foreach ($classes as $class) : ?>
											<option value="<?php echo esc_attr($class['id']); ?>">
												<?php echo esc_html($class['class']); ?>
											</option>
										<?php endforeach; ?>
									</select>
								</center>
							</td>
						</tr>
					<?php endif; ?>

					<!-- Username -->
					<input type="text" name="username" value="<?php echo esc_attr(wp_get_current_user()->user_login); ?>" hidden>

					<!-- Department Dropdown OR Hidden Input -->
                                        <?php if (!empty($this->atts['sdepartment'])) : ?>
                                                <input type="hidden" class="selectDepartment" data-include-published="1" name="sdepartment" value="<?php echo esc_attr($this->atts['sdepartment']); ?>">
					<?php else : ?>
						<tr>
							<td><strong>Select <?php echo esc_html($display_dept_name); ?></strong></td>
							<td>
								<center>
                                                                        <select name="sdepartment" class="selectDepartment" data-include-published="1" required>
										<option value="" selected disabled>Select <?php echo esc_html($display_class_name); ?> First</option>
									</select>
								</center>
							</td>
						</tr>
					<?php endif; ?>

					<!-- Section Dropdown -->

                                        <?php if (!empty($this->atts['ssection'])) : ?>
                                                <input type="hidden" class="selectSection" name="ssection" value="<?php echo esc_attr($this->atts['ssection']); ?>">
					<?php else : ?>
						<tr>
							<td><strong>Select <?php echo esc_html($display_section_name); ?></strong></td>
							<td>
								<center>
									<select name="ssection" class="selectSection" required>
										<option value="" selected disabled>Select <?php echo esc_html($display_dept_name); ?> First</option>
									</select>
								</center>
							</td>
						</tr>
					<?php endif; ?>

					<!-- Exam Name Dropdown -->

                                        <?php if (!empty($this->atts['s_exam_name'])) : ?>
                                                <input type="hidden" class="selectExam" name="s_exam_name" value="<?php echo esc_attr($this->atts['s_exam_name']); ?>">
					<?php else : ?>
						<tr>
							<td><strong>Select <?php echo esc_html($display_exam_name); ?></strong></td>
							<td>
								<center>
									<select name="s_exam_name" class="selectExam" required>
										<option value="" selected disabled>Select <?php echo esc_html($display_class_name); ?> First</option>
									</select>
								</center>
							</td>
						</tr>
					<?php endif; ?>

                                        <!-- Identifier Input (Roll/Registration Number) -->
                                        <tr>
                                                <td>
                                                        <?php
                                                        if ($requires_registration) {
                                                                echo '<strong>' . esc_html($display_roll) . '</strong>';
                                                        } elseif ($search_method['method'] === 'Roll Number') {
                                                                echo '<strong>' . esc_html($display_roll) . '</strong>';
                                                        } elseif ($search_method['method'] === 'Reg Number') {
                                                                echo '<strong>' . esc_html($display_regno) . '</strong>';
                                                        } else {
                                                                echo '<strong>' . esc_html($search_method['label']) . '</strong>';
                                                        }
                                                        ?>
                                                </td>
                                                <td>
                                                        <?php
                                                        $placeholder_label = $requires_registration ? $display_roll
                                                                : ($search_method['method'] === 'Roll Number' ? $display_roll
                                                                : ($search_method['method'] === 'Reg Number' ? $display_regno : $search_method['label']));
                                                        ?>
                                                        <input type="text"
                                                                name="sno"
                                                                placeholder="<?php echo esc_attr('Type Your ' . $placeholder_label); ?>"
                                                                required>
                                                </td>
                                        </tr>

                                        <?php if ($requires_registration) : ?>
                                        <tr>
                                                <td><strong><?php echo esc_html($display_regno); ?></strong></td>
                                                <td>
                                                        <input type="text"
                                                                name="regno"
                                                                placeholder="<?php echo esc_attr('Type Your ' . $display_regno); ?>"
                                                                required>
                                                </td>
                                        </tr>
                                        <?php endif; ?>

					<!-- Submit Button -->
					<tr>
						<td></td>
						<td>
							<center>
								<input type="submit" class="btn btn-primary" name="submit" value="Submit">
							</center>
						</td>
					</tr>
				</table>
			</form>
		</center>
	<?php
	}

	/**
	 * Retrieves the list of classes.
	 *
	 * This function fetches and returns an array of classes.
	 *
	 * @return array The list of classes.
	 */
	private function get_classes()
	{
		$sql = "SELECT * FROM `{$this->prefix}astgdrmsys_class`";
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		return $this->wpdb->get_results($sql, ARRAY_A);
	}

	/**
	 * Retrieves the search method.
	 *
	 * This function is responsible for determining and returning the search method
	 * used in the result management system.
	 *
	 * @return string The search method.
	 */
	private function get_search_method()
	{
		$method = $this->view_settings[0]['methodfield'];
		return array(
			'method' => $method,
			'label'  => $method === 'Roll Number'
				? $this->view_settings[0]['rollfield']
				: $this->view_settings[0]['regnofield'],
		);
	}
	
	/**
	 * Retrieves the selected exam context from the current request.
	 *
	 * @return array{
	 *     exam_id:int,
	 *     exam_year:int
	 * }
	 */
	private function get_selected_exam_context()
	{
		static $context = null;
	
		if ($context !== null) {
			return $context;
		}
		
		$exam_id   = 0;
		$exam_year = 0;

		if (isset($_POST['s_exam_name'])) {
			$exam_raw = sanitize_text_field(wp_unslash($_POST['s_exam_name']));
			
			if (strpos($exam_raw, ',') !== false) {
				$parts     = array_map('trim', explode(',', $exam_raw));
				$exam_id   = absint($parts[0] ?? 0);
				$exam_year = absint($parts[1] ?? 0);
			} else {
				$exam_id = absint($exam_raw);
			}
		}

		if ($exam_id && ! $exam_year) {
			global $wpdb;
			
			$exam_year = (int) $wpdb->get_var(
				$wpdb->prepare(
					"SELECT exam_year_id FROM `{$wpdb->prefix}astgdrmsys_exam_name` WHERE id = %d",
					$exam_id
				)
			);
		}
		
		$context = array(
			'exam_id'   => $exam_id,
			'exam_year' => $exam_year,
		);
		
		return $context;
	}
	
	/**
	 * Processes the result request.
	 *
	 * This function handles the logic for processing the result request.
	 * It is a private method and should not be accessed directly.
	 *
	 * @return void
	*/
	private function process_result_request()
	{
		// Ensure the form is submitted
		if (! isset($_POST['submit'])) {
			return;
		}

		// Verify the nonce
		// $nonce = isset($_POST['student_result_nonce']) ? sanitize_text_field(wp_unslash($_POST['student_result_nonce'])) : '';
		// try {
		// 	if (wp_verify_nonce($nonce, 'student_result_form')) {
		// 		// Regenerate a new nonce and URL
		// 		// $new_nonce = wp_create_nonce('my_plugin_nonce');
		// 		// $new_url = add_query_arg('nonce', $new_nonce, 'https://example.com/my-plugin-url');
		// 		// Update the user's session or database with the new nonce and URL
		// 	} else {
		// 		wp_safe_redirect(add_query_arg(array(), get_permalink()));
		// 		exit;
		// 	}
		// } catch (\Throwable $th) {
		// 	// throw $th;
		// 	wp_safe_redirect(add_query_arg(array(), get_permalink()));
		// 	exit;
		// }
		
		$exam_context = $this->get_selected_exam_context();

		// Fetch user restriction
		$user_restriction = $this->fetch_restrict_user($exam_context['exam_id']);

		// Check user restrictions
		if ($user_restriction && ! is_user_logged_in()) {
			echo '<center><h2>Access Denied</h2></center>';
			return;
		}

		// fetch check user result
		$check_user_result = $this->check_user_result($exam_context['exam_id']);

		// Fetch result data
		$result_data = $this->fetch_result_data($check_user_result);

		// Check if results exist
		if (! isset($result_data) || ! is_array($result_data) || empty($result_data)) {
			// Check if the user is logged in
			if ($check_user_result && ! is_user_logged_in()) {
				echo '<center><h3>You do not have access to view this result.</h3></center>';
				return;
			}
			// Check if the user is allowed to view the result
			if ($check_user_result) {
				echo '<center><h2>You are not allowed to view this result</h2></center>';
				return;
			}
			// Check if the result is published
			if (! isset($result_data['exam_name']['result_publish_status']) || $result_data['exam_name']['result_publish_status'] == 0) {
				echo '<center><h2>Result not published yet</h2></center>';
				return;
			}
			// Show when result is not found
			echo '<center><h2>No Result Found</h2></center>';
			return;
		}

		// Render the result marksheet and download buttons
		$this->astgdrmsys_render_result_marksheet($result_data);
		$this->astgdrmsys_render_download_buttons();
	}

        private function fetch_result_data($check_user_result)
        {
                global $wpdb;
		// Handle exam name and year
		$s_exam_name = 0;
		$s_exam_year = 0;
		
		if (
			isset($_POST['s_exam_name'], $_POST['_wpnonce']) &&
			wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_wpnonce'])), 'astgdrmsys_exam_search')
			) {
				$exam_context = $this->get_selected_exam_context();
				$s_exam_name  = $exam_context['exam_id'];
				$s_exam_year  = $exam_context['exam_year'];
			}
			
		$cache_key       = 'exam_publish_status_' . $s_exam_name;
		$resultPublished = wp_cache_get($cache_key);

		if ($resultPublished === false) {
			$query = $wpdb->prepare(
				"SELECT result_publish_status FROM `{$wpdb->prefix}astgdrmsys_exam_name` WHERE id = %d",
				$s_exam_name
			);

			// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery
			$resultPublished = $wpdb->get_var($query);

			wp_cache_set($cache_key, $resultPublished);
		}

		// Check if it is published
		if (! $resultPublished == 1) {
			return;
		}

		// Sanitize and validate input parameters
		$id          = isset($_POST['sno']) ? sanitize_text_field(wp_unslash($_POST['sno'])) : '';
		$sclass      = isset($_POST['sclass']) ? absint(wp_unslash($_POST['sclass'])) : 0;
		$sdepartment = isset($_POST['sdepartment']) ? absint(wp_unslash($_POST['sdepartment'])) : 0;
		$ssection	= isset($_POST['ssection']) ? sanitize_text_field(wp_unslash($_POST['ssection'])) : '';
		$current_user_regno = isset($_POST['username']) ? sanitize_text_field(wp_unslash($_POST['username'])) : '';
		$registration_number = $this->requires_registration_number() && isset($_POST['regno'])
			? sanitize_text_field(wp_unslash($_POST['regno']))
			: '';
		if ($this->requires_registration_number() && $registration_number === '') {
			return null;
		}

		//encrypt url
		$query = http_build_query([
			'class' => $sclass,
			'department' => $sdepartment,
			'section' => $ssection,
			'exam_name' => $s_exam_name,
			'exam_year' => $s_exam_year,
			'roll' => $id,
		]);
		$encrypted = $this->encrypt_query($query);
		// Use this in the URL
		$request_uri  = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '/';
		$current_path = strtok($request_uri, '?');
		$home_parts   = wp_parse_url(home_url());
		$current_url  = ! empty($home_parts['host'])
			? esc_url_raw(($home_parts['scheme'] ?? (is_ssl() ? 'https' : 'http')) . '://' . $home_parts['host'] . (isset($home_parts['port']) ? ':' . $home_parts['port'] : '') . ($current_path ?: '/'))
			: home_url('/');
		$url = $current_url . "?data=" . $encrypted;

		// Determine search key based on view settings
		$search_method = $this->view_settings[0];
		// $searchsqlkey = $search_method['methodfield'] == 'Roll Number' ? 'sno' : 'regno';
		$searchsqlkey = $this->requires_registration_number() ? 'sno'
			: (in_array($search_method['methodfield'], array('Roll Number', 'Reg Number')) ? 'sno' : 'regno'); // Validate column name

		// Prepare additional SQL conditions
		$andsql              = '';
		$additional_settings = $this->view_settings[1] ?? array();
		if ($additional_settings['methodfield'] == 'true') {
			if ($check_user_result) {
				$andsql = $this->wpdb->prepare(
					'AND `class` = %d AND `regno` = %s AND `section` = %d AND `department` = %d AND `exam_name` = %d AND `exam_year` = %d',
					$sclass,
					$current_user_regno,
					$ssection,
					$sdepartment,
					$s_exam_name,
					$s_exam_year
				);
			} else {
				$andsql = $this->wpdb->prepare(
					'AND `class` = %d AND `section` = %d AND `department` = %d AND `exam_name` = %d AND `exam_year` = %d',
					$sclass,
					$ssection,
					$sdepartment,
					$s_exam_name,
					$s_exam_year
				);
			}
		}

		// Fetch result data
		$result_data = array(
			'school_info'   => $this->fetch_school_info(),
			'view_settings' => $this->view_settings,
			'classes'       => $this->get_classes(),
			'exam_years'    => $this->get_exam_years(),
		);

		// Fetch student result
		$cache_key                     = 'student_result_' . md5($searchsqlkey . $id . $andsql . $current_user_regno . $registration_number);
		$result_data['student_result'] = wp_cache_get($cache_key);

		if ($result_data['student_result'] === false) {
			// sanitize table
			$result_table = esc_sql($this->prefix . 'astgdrmsys_student_result');
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			$base_conditions = array("`{$searchsqlkey}` = %s");
			$prepare_params  = array($id);

			if ($this->requires_registration_number()) {
				$base_conditions[] = '`regno` = %s';
				$prepare_params[]  = $registration_number;
			}

			$where_sql = implode(' AND ', $base_conditions) . ' ' . $andsql;

			$result_data['student_result'] = $wpdb->get_row(
				$wpdb->prepare(
					// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
					"SELECT * FROM $result_table WHERE {$where_sql}",
					$prepare_params
				),
				ARRAY_A
			);

			wp_cache_set($cache_key, $result_data['student_result']);
		}
		
		if (empty($result_data['student_result'])) {
			return null;
		}
		// Fetch exam data and limit subjects by department
		$exam_table = $this->prefix . 'astgdrmsys_exam_name';
		$exam_data  = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM $exam_table WHERE id = %d",
				$result_data['student_result']['exam_name']
			),
			ARRAY_A
		);
		
		// Fetch additional data
		$result_data['student_result']['class_details'] = $this->astgdrmsys_fetch_class_details($result_data['student_result']['class']);
                $exam_subject_overrides = $this->parse_exam_subject_full_marks($exam_data['subject_full_marks'] ?? null);
                $subject_info = $this->fetch_student_subjects(
                        $result_data['student_result'],
                        $exam_data['department'] ?? null,
                        $exam_subject_overrides,
                        $exam_data['exam_type'] ?? null
                );
                $result_data['student_result']['subjects'] = $subject_info['subjects'];
                $result_data['student_result']['fourth_subject_code'] = $subject_info['fourth_subject_code'];
                $result_data['exam_name'] = $exam_data;
                $result_data['code_url'] = $url; // Add the encrypted URL to the result data
                return $result_data;
        }

        /**
         * Normalizes the stored subject full marks for an exam into lookup arrays.
         *
         * @param mixed $raw_value Raw subject data from the exam record.
         * @return array<string|int, float> Map of subject identifiers to their custom full marks.
         */
        private function parse_exam_subject_full_marks($raw_value)
        {
                if (empty($raw_value)) {
                        return array();
                }

                if (is_string($raw_value)) {
                        $decoded = json_decode($raw_value, true);

                        if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                                $raw_value = $decoded;
                        } else {
                                $maybe_unserialized = maybe_unserialize($raw_value);

                                if (is_array($maybe_unserialized)) {
                                        $raw_value = $maybe_unserialized;
                                }
                        }
                } elseif (is_object($raw_value)) {
                        $raw_value = (array) $raw_value;
                }

                if (! is_array($raw_value)) {
                        return array();
                }

                $overrides = array();

                foreach ($raw_value as $entry) {
                        if (is_object($entry)) {
                                $entry = (array) $entry;
                        }

                        if (! is_array($entry)) {
                                continue;
                        }

                        $full_mark = null;

                        if (isset($entry['full_mark']) && $entry['full_mark'] !== '') {
                                $full_mark = floatval($entry['full_mark']);
                        } elseif (isset($entry['total']) && $entry['total'] !== '') {
                                $full_mark = floatval($entry['total']);
                        }

                        if ($full_mark === null) {
                                continue;
                        }

                        if (! empty($entry['code'])) {
                                $overrides[trim((string) $entry['code'])] = $full_mark;
                        } elseif (! empty($entry['subcode'])) {
                                $overrides[trim((string) $entry['subcode'])] = $full_mark;
                        }

                        if (! empty($entry['id'])) {
                                $overrides[(int) $entry['id']] = $full_mark;
                        }
                }

                return $overrides;
        }

	/**
	 * Fetches information about the school.
	 *
	 * This function retrieves various details about the school, such as its name,
	 * address, contact information, and other relevant data.
	 *
	 * @return array An associative array containing school information.
	 */
	private function fetch_school_info()
	{
		$sql = "SELECT * FROM `{$this->prefix}astgdrmsys_school_info`";
		// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
		$results = $this->wpdb->get_results($sql, ARRAY_A);
		return ! empty($results) ? $results[0] : array();
	}

	/**
	 * Checks the result for the current user.
	 *
	 * This function verifies and retrieves the result data for the user.
	 *
	 * @return void
	 */
	private function check_user_result($exam_id = 0)
	{
		$settings = $this->get_exam_access_settings($exam_id);
		return ! empty($settings['check_user_result']);
	}
		
	/**
	 * Fetches and restricts user access based on certain criteria.
	 *
	 * This function is responsible for retrieving user information and applying
	 * restrictions to ensure that only authorized users can access certain features
	 * or data.
	 *
	 * @return bool
	 */
	private function fetch_restrict_user($exam_id = 0)
	{
		$settings = $this->get_exam_access_settings($exam_id);
		return ! empty($settings['restrict_user']);
	}
	/**
	 * Retrieves the per-exam access settings for the provided exam identifier.
	 *
	 * @param int $exam_id Exam identifier.
	 * @return array{restrict_user:int,check_user_result:int}
	 */
	private function get_exam_access_settings($exam_id)
	{
		static $cached_settings = array();
		
		if (! $exam_id) {
			return array(
				'restrict_user'     => 0,
				'check_user_result' => 0,
			);
		}
		
		if (! isset($cached_settings[$exam_id])) {
			$exam_table = esc_sql($this->prefix . 'astgdrmsys_exam_name');
			$row        = $this->wpdb->get_row(
				$this->wpdb->prepare(
					"SELECT restrict_non_logged_in, check_only_own_result FROM $exam_table WHERE id = %d",
					$exam_id
				),
				ARRAY_A
			);
			
			if ($this->wpdb->last_error) {
					do_action('astgdrmsys_log_error', 'Database error: ' . $this->wpdb->last_error);
			}
			
			if (empty($row)) {
				$cached_settings[$exam_id] = array(
					'restrict_user'     => 0,
					'check_user_result' => 0,
				);
			} else {
				$cached_settings[$exam_id] = array(
					'restrict_user'     => (int) ($row['restrict_non_logged_in'] ?? 0),
					'check_user_result' => (int) ($row['check_only_own_result'] ?? 0),
				);
			}
		}
		
		return $cached_settings[$exam_id];
	}
	
	/**
	 * Retrieves all exam names from the database.
	 *
	 * This function executes a SQL query to select all records from the
	 * `astgdrmsys_exam_name` table and returns the results as an associative array.
	 *
	 * @return array An associative array of exam names.
	 */
	private function get_exam_names()
	{
		global $wpdb;
		// sanitize table name
		$exam_table = esc_sql($this->prefix . 'astgdrmsys_exam_name');
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
		return $wpdb->get_results(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				"SELECT * FROM $exam_table"
			),
			ARRAY_A
		);
	}

	/**
	 * Retrieves the list of exam years.
	 *
	 * This function fetches and returns an array of years for which exams have been conducted.
	 *
	 * @return array An array of exam years.
	 */
	private function get_exam_years()
	{
		global $wpdb;
		// sanitize table name
		$examyear_table = esc_sql($this->prefix . 'astgdrmsys_exam_year');
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
		return $wpdb->get_results(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				"SELECT * FROM $examyear_table"
			),
			ARRAY_A
		);
	}

	/**
	 * Fetches the details of a class based on the provided class ID.
	 *
	 * @param int $class_id The ID of the class to fetch details for.
	 * @return array An associative array containing the class details.
	 */
	private function astgdrmsys_fetch_class_details($class_id)
	{
		global $wpdb;
		// sanitize table name
		$class_table = esc_sql($this->prefix . 'astgdrmsys_class');

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery
		$results = $wpdb->get_results(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared    
				"SELECT * FROM $class_table WHERE `id` = %d",
				$class_id
			),
			ARRAY_A
		);

		// Check for database errors
		return ! empty($results) ? $results[0] : array();
	}
	
	/**
	 * Fetches the subjects for a given student's result.
	 *
         * @param array       $student_result   An associative array containing the student's result data.
         * @param string      $department_ids  Optional comma-separated department IDs to filter subjects.
         * @param array       $subject_overrides Custom subject totals keyed by code or ID.
         * @param string|null $exam_type        The exam type (e.g., 'composite').
         * @return array An array of subjects associated with the student's result.
         */
        protected function fetch_student_subjects($student_result, $department_ids = null, $subject_overrides = array(), $exam_type = null)
        {
                global $wpdb;

                $marks_table = esc_sql($this->prefix . 'astgdrmsys_mark');
		$marks_result = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT * FROM $marks_table WHERE `sid` = %d",
				$student_result['sid']
			),
			ARRAY_A
		);
		
                $marks_data = ! empty($marks_result) ? maybe_unserialize($marks_result[0]['marks']) : array();
                $subjects   = array();
                $fourth_subject_code = null;
                $available_subjects  = $this->get_subjects_by_class($student_result['class'], $department_ids);
                $subject_overrides   = is_array($subject_overrides) ? $subject_overrides : array();
                $is_composite_exam   = ($exam_type === 'composite');
                $retake_marks        = $this->get_retake_marks_by_sid($student_result['sid']);

                if (is_array($marks_data)) {
                        $fourth_subject_code = isset($marks_data['fourth_subject']) ? trim((string) $marks_data['fourth_subject']) : (isset($marks_data[0]['fourth_subject']) ? trim((string) $marks_data[0]['fourth_subject']) : null);
                        $marks               = isset($marks_data[0]) ? $marks_data[0] : $marks_data;
                        unset($marks['fourth_subject']);
                        $seen_subjects       = array();
			
                        foreach ($marks as $subcode => $mark) {
                                if ($subcode == 'insub') {
                                        continue;
                                }

                                $normalized_subcode = trim((string) $subcode);
                                $seen_subjects[$normalized_subcode] = true;
                                $subject            = $available_subjects[$normalized_subcode] ?? ($available_subjects[$subcode] ?? null);

                                if ($subject) {
                                        if (is_array($mark) && ! empty($mark['not_taken'])) {
                                                continue;
                                        }

                                        if ($is_composite_exam) {
                                                $mark = $this->normalize_composite_subject_mark($mark);
                                        }

                                        if (is_array($mark) && ! isset($mark['total'])) {
                                                $mark['total'] = 0;
                                                foreach (['practical', 'class', 'final'] as $part) {
                                                        if (isset($mark[$part]) && is_numeric($mark[$part])) {
                                                                $mark['total'] += floatval($mark[$part]);
                                                        }
                                                }
                                        }
					
                                        $override_total = null;

                                        if (isset($subject_overrides[$normalized_subcode])) {
                                                $override_total = floatval($subject_overrides[$normalized_subcode]);
                                        } elseif (isset($subject_overrides[$subcode])) {
                                                $override_total = floatval($subject_overrides[$subcode]);
                                        } elseif (! empty($subject['id']) && isset($subject_overrides[(int) $subject['id']])) {
                                                $override_total = floatval($subject_overrides[(int) $subject['id']]);
                                        }

                                        if ($override_total !== null) {
                                                $subject['total'] = $override_total;
                                        } elseif (! isset($subject['total']) && isset($subject['full_mark'])) {
                                                $subject['total'] = floatval($subject['full_mark']);
                                        }

                                        $is_fourth   = (trim((string) $subcode) === $fourth_subject_code);
                                        $subject_row = array(
                                                'subject'     => $subject,
                                                'mark'        => $mark,
                                                'remark'      => $this->calculate_subject_remark($subject, $mark),
                                                'grade_point' => $this->calculate_grade_point($subject, $mark),
						'is_fourth'   => $is_fourth,
					);

                                        if (isset($retake_marks[$normalized_subcode])) {
                                                $subject_row = $this->apply_retake_to_subject_row($subject_row, $retake_marks[$normalized_subcode]);
                                        }
					
					if (! $is_fourth) {
						$subject_row['gpa_without_4'] = $this->calculate_gpa_without_4($subject, $subject_row['mark']);
					}
					
					$subjects[] = $subject_row;
				}
			}

                        foreach ($retake_marks as $retake_subcode => $retake) {
                                $normalized_subcode = trim((string) $retake_subcode);

                                if (isset($seen_subjects[$normalized_subcode])) {
                                        continue;
                                }

                                $subject = $available_subjects[$normalized_subcode] ?? null;
                                if (! $subject) {
                                        continue;
                                }

                                $is_fourth   = ($normalized_subcode === $fourth_subject_code);
                                $subject_row = array(
                                        'subject'   => $subject,
                                        'mark'      => null,
                                        'is_fourth' => $is_fourth,
                                );
                                $subject_row = $this->apply_retake_to_subject_row($subject_row, $retake);

                                if (! $is_fourth) {
                                        $subject_row['gpa_without_4'] = $this->calculate_gpa_without_4($subject, $subject_row['mark']);
                                }

                                $subjects[] = $subject_row;
                        }
		}
		
                return array(
                        'subjects'            => $subjects,
                        'fourth_subject_code' => $fourth_subject_code,
                );
        }

        /**
         * Normalize composite exam marks to rely on class and final components.
         *
         * Composite exams may have been stored with three components (practical, class, final)
         * when they previously used the triple exam format. This helper remaps each subject's
         * marks to two components so downstream grade and GPA calculations stay aligned with
         * the displayed columns.
         *
         * @param mixed $subject_mark Raw subject mark data.
         * @return array Normalized subject mark data keyed by 'class', 'final', and optionally 'total'.
         */
        private function normalize_composite_subject_mark($subject_mark)
        {
                if (! is_array($subject_mark)) {
                        $class_numeric = is_numeric($subject_mark) ? floatval($subject_mark) : null;

                        $normalized = array(
                                'class' => $class_numeric ?? $subject_mark,
                                'final' => '-',
                        );

                        if (null !== $class_numeric) {
                                $normalized['total'] = $class_numeric;
                        }

                        return $normalized;
                }

                if (isset($subject_mark['class']) || isset($subject_mark['final'])) {
                        $class_numeric = isset($subject_mark['class']) && is_numeric($subject_mark['class']) ? floatval($subject_mark['class']) : null;
                        $final_numeric = isset($subject_mark['final']) && is_numeric($subject_mark['final']) ? floatval($subject_mark['final']) : null;

                        if (! isset($subject_mark['total'])) {
                                $total = 0;
                                $has_numeric = false;

                                foreach ([$class_numeric, $final_numeric] as $component) {
                                        if (null !== $component) {
                                                $total       += $component;
                                                $has_numeric  = true;
                                        }
                                }

                                if ($has_numeric) {
                                        $subject_mark['total'] = $total;
                                }
                        }

                        return $subject_mark;
                }

                $class_raw = $this->choose_composite_component(
                        $subject_mark['class'] ?? null,
                        $subject_mark['practical'] ?? null
                );
                $final_raw = $this->choose_composite_component(
                        $subject_mark['final'] ?? null,
                        $subject_mark['class'] ?? null
                );

                $class_numeric = is_numeric($class_raw) ? floatval($class_raw) : null;
                $final_numeric = is_numeric($final_raw) ? floatval($final_raw) : null;

                $normalized = array(
                        'class' => $class_numeric ?? ($class_raw ?? '-'),
                        'final' => $final_numeric ?? ($final_raw ?? '-'),
                );

                $total = 0;
                $has_numeric = false;

                foreach ([$class_numeric, $final_numeric] as $component) {
                        if (null !== $component) {
                                $total       += $component;
                                $has_numeric  = true;
                        }
                }

                if ($has_numeric) {
                        $normalized['total'] = $total;
                }

                if (isset($subject_mark['not_taken'])) {
                        $normalized['not_taken'] = $subject_mark['not_taken'];
                }

                return $normalized;
        }

        /**
         * Choose the primary component unless it is clearly empty, falling back when possible.
         *
         * Legacy triple-exam rows may store placeholder zeros or blanks for components that no
         * longer apply. To avoid showing those placeholders after remapping to composite exams,
         * prefer the fallback value when the primary is empty/blank or a zero placeholder and
         * the fallback contains data.
         *
         * @param mixed      $primary  Preferred component value.
         * @param mixed|null $fallback Alternative component value.
         *
         * @return mixed Selected component value (may be null).
         */
        private function choose_composite_component($primary, $fallback)
        {
                $primary_is_empty = null === $primary || '' === $primary || '-' === $primary;
                $primary_is_zero_placeholder = is_numeric($primary) && 0 === floatval($primary);

                $fallback_has_value = null !== $fallback && '' !== $fallback && '-' !== $fallback;

                if (($primary_is_empty || $primary_is_zero_placeholder) && $fallback_has_value) {
                        return $fallback;
                }

                return $primary;
        }

        private function astgdrmsys_disply_options()
        {
		// Display options for the result marksheet
		$display_name = get_option('astgdrmsys_display_name', 'true');
		$display_class_name   = !empty($display_name['class_display_name'])   ? $display_name['class_display_name']   : 'Class';
		$display_dept_name    = !empty($display_name['dept_display_name'])    ? $display_name['dept_display_name']    : 'Department';
		$display_section_name = !empty($display_name['section_display_name']) ? $display_name['section_display_name'] : 'Section';
		$display_exam_name    = !empty($display_name['exam_name_display_name']) ? $display_name['exam_name_display_name'] : 'Exam Name';
		$display_exam_year    = !empty($display_name['exam_year_display_name']) ? $display_name['exam_year_display_name'] : 'Exam Year';
		$display_roll         = !empty($display_name['roll_display_name'])    ? $display_name['roll_display_name']    : 'Roll No';
		$display_regno        = !empty($display_name['regno_display_name'])   ? $display_name['regno_display_name']   : 'Registration No';

		return array(
			'class' => $display_class_name,
			'roll'  => $display_roll,
			'regno' => $display_regno,
			'dept'  => $display_dept_name,
			'section' => $display_section_name,
			'exam_name' => $display_exam_name,
			'exam_year' => $display_exam_year,
		);
	}

	/**
	 * Renders the result marksheet.
	 *
	 * @param array $result_data An associative array containing the result data to be displayed in the marksheet.
	 */
	private function astgdrmsys_render_result_marksheet($result_data)
	{
		global $wpdb;
		// Extract data to update result marksheet
		$result_text = $result_data['exam_name'];
		
		// Extract data for easier access
		$school_info   = $result_data['school_info'];
		$student       = $result_data['student_result'];
		$view_settings = $result_data['view_settings'];
		
		// Display options for the result marksheet
		$display_options = $this->astgdrmsys_disply_options();
		$display_class_name   = $display_options['class'];
		$display_roll         = $display_options['roll'];
		$display_regno        = $display_options['regno'];

		$subjects            = $student['subjects'];
		$fourth_subject_code = $student['fourth_subject_code'] ?? null;

		$exam_name = $student['exam_name']; // This should be the exam ID

		// get exam type and custom fields
		$exam_type     = $result_text['exam_type'];
		$fail_on_single_subject = (bool) $result_text['fail_on_single_subject'];
		$exam_custom_fields = $result_text ?? [];
		$student_custom_fields = $this->parse_student_custom_fields($student['custom_fields'] ?? '');

		// upload directory for logo
		$upload_dir = wp_upload_dir();
		$logo_path  = $school_info['logo']; // Full system path or relative upload path

		// Case 1: If logo is a file system path, convert to relative
		if (strpos($logo_path, $upload_dir['basedir']) !== false) {
			$relative_path = str_replace($upload_dir['basedir'], '', $logo_path);
		} else {
			// Assume it's already a relative path or filename
			$relative_path = '/' . ltrim($logo_path, '/\\');
		}

		$logo_url = esc_url($upload_dir['baseurl'] . $relative_path);

		// Inside the function or template where you display the result:
		$include_qr_code = ! empty( $result_text['include_qr_code'] );
		$qrFileName      = '';
		$qrFileUrl       = '';
		if ( $include_qr_code && empty( $upload_dir['error'] ) ) {
			$qrTempDir = trailingslashit( $upload_dir['basedir'] ) . 'astgdrmsys-qrcodes/';
			$qrBaseUrl = trailingslashit( $upload_dir['baseurl'] ) . 'astgdrmsys-qrcodes/';

			if ( wp_mkdir_p( $qrTempDir ) && wp_is_writable( $qrTempDir ) ) {
				$qrFileName = 'qr_' . md5( (string) $result_data['code_url'] ) . '.png';
				$qrFilePath = $qrTempDir . $qrFileName;
				$qrFileUrl  = $qrBaseUrl . $qrFileName;

				QRcode::png(
					$result_data['code_url'],
					$qrFilePath,
					QR_ECLEVEL_L,
					5,
					2
				);
			}
		}

		// Calculate totals and percentages
		$totals = $this->calculate_result_totals($subjects, $fourth_subject_code, $exam_name);
		?>
		<!-- Result Marksheet -->
		<div class='print' id='print' name='print'>
			<!-- Institute Logo -->
			<?php
			// If logo is in media library, use wp_get_attachment_image
			$attachment_id = attachment_url_to_postid($logo_url);
			if ($attachment_id) {
				// Output the logo using wp_get_attachment_image if attachment ID is found
				echo wp_get_attachment_image(
					$attachment_id,
					'medium',
					false,
					array(
						'class'     => 'watermark',
						'alt'       => esc_attr__('School Logo', 'result-management-system-for-institutions'),
						'draggable' => 'false',
					)
				);
			} else {
				// Fallback to using img tag if attachment is not found (PHPCS:ignore warning)
				// phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage
				echo '<img class="watermark" draggable="false" src="' . esc_url($logo_url) . '" alt="' . esc_attr__('School Logo', 'result-management-system-for-institutions') . '">';
			}
			?>
			<div class="result-container">
				<div class="result-header">
					<!-- Institute Header -->
					<center>
						<?php
						// If logo is in media library, use wp_get_attachment_image
						if ($attachment_id) {
							echo wp_get_attachment_image(
								$attachment_id,
								'medium',
								false,
								array(
									'class' => 'logo-img',
									'alt'   => esc_attr__('School Logo', 'result-management-system-for-institutions'),
								)
							);
						} else {
							// Fallback to using img tag if attachment is not found (PHPCS:ignore warning)
							// phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage
							echo '<img src="' . esc_url($logo_url) . '" alt="' . esc_attr__('School Logo', 'result-management-system-for-institutions') . '" class="logo-img">';
						}
						?>
						<h1><?php echo esc_html($school_info['sc_name']); ?></h1>
						<div id="address">
							<?php echo esc_html($school_info['vill'] . ', ' . $school_info['ps'] . ', ' . $school_info['dist'] . ' (' . $school_info['state'] . ')'); ?>
						</div>
						<div id="result-title">
							<?php echo esc_html($result_text['result_title']); ?>
						</div>
						<div id="result-subtitle">
							<?php echo esc_html($result_text['result_subtitle']); ?>
						</div>
						<?php if ( $include_qr_code && ! empty( $qrFileUrl ) ) : ?>
						<div id="code-url" class="result-qrcode">
								<img src="<?php echo esc_url( $qrFileUrl ); ?>" alt="<?php esc_attr_e( 'Result QR Code', 'result-management-system-for-institutions' ); ?>">
						</div>
						<?php endif; ?>
					</center>
				</div>
				<?php
				// Define view settings and checks
				$rowcheck = array(
					'namefield'   => $view_settings['namefield'] ?? 'false',
					'genderfield' => $view_settings['genderfield'] ?? 'false',
					'fnamefield'  => $view_settings['fnamefield'] ?? 'false',
					'mnamefield'  => $view_settings['mnamefield'] ?? 'false',
					'classfield'  => $view_settings['classfield'] ?? 'false',
					'rollfield'   => $view_settings['rollfield'] ?? 'false',
					'regnofield'  => $view_settings['regnofield'] ?? 'false',
				);

				$rowview = array(
					'namefield'   => 'Student Name',
					'genderfield' => 'Gender',
					'fnamefield'  => 'Father\'s Name',
					'mnamefield'  => 'Mother\'s Name',
					'classfield'  => $display_class_name,
					'rollfield'   => $display_roll,
					'regnofield'  => $display_regno,
				);

				$displayable_custom_fields = $student_custom_fields;
				// Display exam-specific custom fields (up to 3)
				for ($i = 1; $i <= 3; $i++) {
					$name_key = 'custom_field_name' . $i;
					$value_key = 'custom_field_value' . $i;
				
					if (!empty($exam_custom_fields[$name_key]) && !empty($exam_custom_fields[$value_key])) {
						$displayable_custom_fields[] = [
							'name'  => $exam_custom_fields[$name_key],
							'value' => $exam_custom_fields[$value_key],
						];
					}
				}

                                $display_names = $this->resolve_exam_mark_labels($result_text);
                                $show_retake_columns = false;
                                foreach ($subjects as $subject_item) {
                                        if (! empty($subject_item['retake']) || (is_array($subject_item['mark'] ?? null) && isset($subject_item['mark']['retake_mark']))) {
                                                $show_retake_columns = true;
                                                break;
                                        }
                                }
				?>

				<!-- Student Details Table -->
				<table border='1' class="table-style">
					<?php
					$gender = ! empty($student['gender']) ? $student['gender'] : 'OTHERS';
					$student_image_url = ! empty($student['student_image']) ? esc_url($student['student_image']) : '';
					if ($gender === 'MALE') {
						$default_avatar_url = ASTGDRMSYS_PLUGIN_URL . 'images/default-student-image/default-male-student.png';
					} elseif ($gender === 'FEMALE') {
						$default_avatar_url = ASTGDRMSYS_PLUGIN_URL . 'images/default-student-image/default-female-student.png';
					} else {
						$default_avatar_url = ASTGDRMSYS_PLUGIN_URL . 'images/default-student-image/default-other-student.png';
					}
					$final_image_url = $student_image_url ? $student_image_url : $default_avatar_url;
					// Name and Gender Row
					if ($rowcheck['namefield'] == 'false' || $rowcheck['genderfield'] == 'false') :
					?>
						<tr>
						<tr>
							<td rowspan="2" class="image-cell">
								<img src="<?php echo $final_image_url; ?>" alt="Student Image" class="student-image" draggable="false" />
							</td>

							<?php if ($rowcheck['namefield'] == 'false') : ?>
								<td <?php echo $rowcheck['genderfield'] != 'false' ? 'colspan="2"' : ''; ?>>
									<strong><?php echo esc_html($rowview['namefield']); ?></strong>
								</td>
								<td <?php echo $rowcheck['genderfield'] != 'false' ? 'colspan="2"' : ''; ?>>
									<?php echo esc_html($student['name']); ?>
								</td>
							<?php endif; ?>

							<?php if ($rowcheck['genderfield'] == 'false') : ?>
								<td <?php echo $rowcheck['namefield'] != 'false' ? 'colspan="2"' : ''; ?>>
									<strong><?php echo esc_html($rowview['genderfield']); ?></strong>
								</td>
								<td <?php echo $rowcheck['namefield'] != 'false' ? 'colspan="2"' : ''; ?>>
									<?php echo esc_html($student['gender']); ?>
								</td>
							<?php endif; ?>
						</tr>
					<?php endif;

					// Father's Name and Mother's Name Row
					if ($rowcheck['fnamefield'] == 'false' || $rowcheck['mnamefield'] == 'false') :
					?>
						<tr>
							<?php if ($rowcheck['fnamefield'] == 'false') : ?>
								<td <?php echo $rowcheck['mnamefield'] != 'false' ? 'colspan="2"' : ''; ?>>
									<strong><?php echo esc_html($rowview['fnamefield']); ?></strong>
								</td>
								<td <?php $rowcheck['mnamefield'] != 'false' ? 'colspan="2"' : ''; ?>>
									<?php echo esc_html($student['fname']); ?>
								</td>
							<?php endif; ?>

							<?php if ($rowcheck['mnamefield'] == 'false') : ?>
								<td <?php $rowcheck['fnamefield'] != 'false' ? 'colspan="2"' : ''; ?>>
									<strong><?php echo esc_html($rowview['mnamefield']); ?></strong>
								</td>
								<td <?php echo $rowcheck['fnamefield'] != 'false' ? 'colspan="2"' : ''; ?>>
									<?php echo esc_html($student['mname']); ?>
								</td>
							<?php endif; ?>
						</tr>
					<?php
					endif;

					// Class and Roll No Row
					if ($rowcheck['classfield'] == 'false' || $rowcheck['rollfield'] == 'false') :
					?>
						<tr>
							<?php if ($rowcheck['classfield'] == 'false') : ?>
								<td <?php echo $rowcheck['rollfield'] != 'false' ? 'colspan="2"' : ''; ?>>
									<strong><?php echo esc_html($rowview['classfield']); ?></strong>
								</td>
								<td
									<?php
									$rowcheck['rollfield'] != 'false' ? 'colspan="2"' : '';
									$colStdClass = $this->astgdrmsys_fetch_class_details($student['class']); // fetching class column
									?>>
									<?php echo esc_html($colStdClass['class'] ?? ''); ?>
								</td>
							<?php
							endif;
							if ($rowcheck['rollfield'] == 'false') :
							?>
								<td <?php echo $rowcheck['rollfield'] != 'false' ? 'colspan="2"' : ''; ?>>
									<strong><?php echo esc_html($rowview['rollfield']); ?></strong>
								</td>
								<td <?php echo $rowcheck['classfield'] == 'false' ? 'colspan="2"' : ''; ?>>
									<?php echo esc_html($student['sno'] ?? ''); ?>
								</td>
							<?php endif; ?>
						</tr>
					<?php
					endif;
					// Registration Number Row
					if ($rowcheck['regnofield'] == 'false') :
					?>
						<tr>
							<th colspan="2">
								<?php echo esc_html($rowview['regnofield']); ?>
							</th>
							<td <?php echo $rowcheck['regnofield'] == 'false' ? 'colspan="3"' : 'colspan="2"'; ?>>
								<?php echo esc_html($student['regno'] ?? ''); ?>
							</td>
						</tr>
					<?php endif;

					// Display custom fields if available
					if (!empty($displayable_custom_fields)) : ?>
						<?php foreach ($displayable_custom_fields as $field) : ?>
							<tr>
								<td colspan="2">
									<strong><?php echo esc_html($field['name']); ?></strong>
								</td>
								<td colspan="3">
									<?php echo esc_html($field['value']); ?>
								</td>
							</tr>
						<?php endforeach; ?>
					<?php endif; ?>
				</table>
				<!-- Subjects Marks Table -->
				<table border='1' class="table-style">
					<thead>
						<tr>
							<?php if ($exam_type === 'triple') : ?>
								<th>Subject</th>
								<th><?php echo esc_html($display_names['practical_mark_display_name']); ?></th> 
								<th><?php echo esc_html($display_names['class_mark_display_name']); ?></th>     
								<th><?php echo esc_html($display_names['final_mark_display_name']); ?></th>     
								<th><?php echo esc_html($display_names['total_mark_display_name']); ?></th>     
								<?php if ($show_retake_columns) : ?>
									<th>Retake</th>
									<th>Final Counted</th>
								<?php endif; ?>
								<th>Letter Grade</th>
								<th>Grade Point</th> 
								<th>Final GPA</th>   
        					<?php elseif ($exam_type === 'composite') : ?>
								<th>Subject</th>
								<th><?php echo esc_html($display_names['class_mark_display_name']); ?></th>     
								<th><?php echo esc_html($display_names['final_mark_display_name']); ?></th>     
								<th><?php echo esc_html($display_names['total_mark_display_name']); ?></th>     
								<?php if ($show_retake_columns) : ?>
									<th>Retake</th>
									<th>Final Counted</th>
								<?php endif; ?>
								<th>Letter Grade</th>
								<th>Grade Point</th> 
								<th>Final GPA</th>   
        					<?php else : ?>
								<th>Subject</th>
								<th><?php echo esc_html($display_names['single_mark_display_name']); ?></th>      
								<?php if ($show_retake_columns) : ?>
									<th>Retake</th>
									<th>Final Counted</th>
								<?php endif; ?>
								<th>Grade</th>       
								<th>Grade Point</th> 
								<th colspan="2">GPA</th>
								<?php endif; ?>
						</tr>
					</thead>
					<tbody>
						<?php
						// Separate subjects for easier processing
						$main_subjects  = array();
						$fourth_subject = null;
						foreach ($subjects as $subject_item) {
							$subcode   = $subject_item['subject']['subcode'] ?? null;
							$is_fourth = !empty($subject_item['is_fourth']) || ($fourth_subject_code !== null && $subcode === $fourth_subject_code);
							
							if ($is_fourth) {
								$fourth_subject = $subject_item;
							} else {
								$main_subjects[] = $subject_item;
							}
						}

						// Calculate GPA and other metrics
                                                $total_gpa        = 0;
                                                $is_failed        = false;
                                                $subject_count    = count($main_subjects); // Count only main subjects

                                                foreach ($main_subjects as $subject_item) {
                                                        $total_gpa += $subject_item['gpa_without_4'] ?? 0;
							$subject_mark = $subject_item['mark'] ?? array();

							if (is_array($subject_mark) && ! empty($subject_mark['not_taken'])) {
								continue;
							}

                                                        if ($subject_item['grade_point'] < 1) {
                                                                $is_failed = true;
                                                        }
                                                }

                                                if ($fourth_subject) {
                                                        $fourth_subject_grade_point = $fourth_subject['grade_point'] ?? 0;
                                                        $fourthsubjectgpa          = max(0, $fourth_subject_grade_point - 2);
                                                } else {
                                                        $fourthsubjectgpa = 0;
                                                }

                                                // Calculate final GPAs
                                                $average_gpa   = $subject_count > 0 ? round($total_gpa / $subject_count, 2) : 0;
                                                $finaltotalgpa = $subject_count > 0 ? min(5, round(($total_gpa + $fourthsubjectgpa) / $subject_count, 2)) : 0;
                                                $final_letter_grade = $this->get_letter_grade_by_gpa($finaltotalgpa);

                                                if ($fail_on_single_subject && $is_failed) {
                                                        $final_letter_grade = 'F';
                                                        $finaltotalgpa      = 0;
                                                }
						$gpaboxtaken   = false;

						// Render Main Subjects
						foreach ($main_subjects as $subject) :
						?>
							<tr>
								<td><?php echo esc_html($subject['subject']['subname'] . ' - ' . $subject['subject']['subcode']); ?></td>
								<?php if ($exam_type === 'triple') : ?>
									<td><?php echo esc_html($subject['mark']['practical'] ?? '-'); ?></td>
									<td><?php echo esc_html($subject['mark']['class'] ?? '-'); ?></td>
									<td><?php echo esc_html($subject['mark']['final'] ?? '-'); ?></td>
									<td><?php echo esc_html($subject['mark']['total'] ?? (($subject['mark']['practical'] ?? 0) + ($subject['mark']['class'] ?? 0) + ($subject['mark']['final'] ?? 0))); ?></td>
									<?php if ($show_retake_columns) : ?>
										<td><?php echo esc_html(isset($subject['mark']['retake_mark']) ? $this->format_retake_mark_value($subject['mark']['retake_mark']) : '-'); ?></td>
										<td><?php echo esc_html(isset($subject['mark']['final_counted_mark']) ? $this->format_retake_mark_value($subject['mark']['final_counted_mark']) : $this->format_retake_mark_value($subject['mark']['total'] ?? '-')); ?></td>
									<?php endif; ?>
								<?php elseif ($exam_type === 'composite') : ?>
									<td><?php echo esc_html($subject['mark']['class'] ?? '-'); ?></td>
									<td><?php echo esc_html($subject['mark']['final'] ?? '-'); ?></td>
									<td><?php echo esc_html($subject['mark']['total'] ?? (($subject['mark']['class'] ?? 0) + ($subject['mark']['final'] ?? 0))); ?></td>
									<?php if ($show_retake_columns) : ?>
										<td><?php echo esc_html(isset($subject['mark']['retake_mark']) ? $this->format_retake_mark_value($subject['mark']['retake_mark']) : '-'); ?></td>
										<td><?php echo esc_html(isset($subject['mark']['final_counted_mark']) ? $this->format_retake_mark_value($subject['mark']['final_counted_mark']) : $this->format_retake_mark_value($subject['mark']['total'] ?? '-')); ?></td>
									<?php endif; ?>
								<?php else : ?>
									<td>
										<?php
										if (is_array($subject['mark']) && isset($subject['mark']['final'])) {
											echo esc_html(is_array($subject['mark']['final']) 
											? implode(', ', $subject['mark']['final']) 
											: $subject['mark']['final']);
										} else {
											echo esc_html($subject['mark'] ?? '-');
										}
										?>
									</td>
									<?php if ($show_retake_columns) : ?>
										<td><?php echo esc_html(is_array($subject['mark']) && isset($subject['mark']['retake_mark']) ? $this->format_retake_mark_value($subject['mark']['retake_mark']) : '-'); ?></td>
										<td><?php echo esc_html(is_array($subject['mark']) && isset($subject['mark']['final_counted_mark']) ? $this->format_retake_mark_value($subject['mark']['final_counted_mark']) : $this->format_retake_mark_value(is_array($subject['mark']) ? ($subject['mark']['total'] ?? '-') : $subject['mark'])); ?></td>
									<?php endif; ?>
								<?php endif; ?>
								<td><?php echo esc_html($subject['remark']); ?></td>
								<td><?php echo esc_html($subject['grade_point']); ?></td>
								<?php
								if (! $gpaboxtaken && $subject_count > 0) {
									$colspan = ($exam_type === 'triple') ? 4 : (($exam_type === 'composite') ? 3 : 2);
									echo '<td colspan="' . esc_attr($colspan) . '" rowspan="' . esc_attr($subject_count) . '"><span class="finalgpa">' . esc_html($finaltotalgpa) . '</span></td>';
									$gpaboxtaken = true;
								}
								?>
							</tr>
						<?php
						endforeach;

						// Render Average GPA row (after main subjects)
						$average_gpa_colspan = ($exam_type === 'triple') ? 5 : (($exam_type === 'composite') ? 4 : 3);
						if ($show_retake_columns) {
							$average_gpa_colspan += 2;
						}
						?>
						<tr>
							<td colspan="<?php echo $average_gpa_colspan; ?>" class="text-right"><strong>Average GPA (Excl. Optional):</strong></td>
							<td class="average-gpa" colspan="<?php echo $average_gpa_colspan; ?>"><?php echo esc_html($average_gpa); ?></td>
						</tr>
						<?php
						// Render the Fourth (Optional) Subject, if it exists
						if ($fourth_subject) :
							$additional_subject_colspan = ($exam_type === 'triple') ? 8 : (($exam_type === 'composite') ? 7 : 6);
							if ($show_retake_columns) {
								$additional_subject_colspan += 2;
							}
						?>
						<tr>
							<td colspan="<?php echo $additional_subject_colspan; ?>">
								<p class="additional-subject-label">Additional Subject:</p>
							</td>
						</tr>
						<tr>
							<td colspan="<?php echo $additional_subject_colspan ?>" class="added_gpa"><em>Added to GPA</em></td>
						</tr>
						<tr class="fourth-sub-row">
							<td><?php echo esc_html($fourth_subject['subject']['subname'] . ' - ' . $fourth_subject['subject']['subcode']); ?></td>
							<?php if ($exam_type === 'triple') : ?>
								<td><?php echo esc_html($fourth_subject['mark']['practical'] ?? '-'); ?></td>
								<td><?php echo esc_html($fourth_subject['mark']['class'] ?? '-'); ?></td>
								<td><?php echo esc_html($fourth_subject['mark']['final'] ?? '-'); ?></td>
								<td><strong><?php echo esc_html($fourth_subject['mark']['total'] ?? '-'); ?></strong></td>
								<?php if ($show_retake_columns) : ?>
									<td><?php echo esc_html(isset($fourth_subject['mark']['retake_mark']) ? $this->format_retake_mark_value($fourth_subject['mark']['retake_mark']) : '-'); ?></td>
									<td><?php echo esc_html(isset($fourth_subject['mark']['final_counted_mark']) ? $this->format_retake_mark_value($fourth_subject['mark']['final_counted_mark']) : $this->format_retake_mark_value($fourth_subject['mark']['total'] ?? '-')); ?></td>
								<?php endif; ?>
								<?php elseif ($exam_type === 'composite') : ?>
								<td><?php echo esc_html($fourth_subject['mark']['class'] ?? '-'); ?></td>
								<td><?php echo esc_html($fourth_subject['mark']['final'] ?? '-'); ?></td>
								<td><strong><?php echo esc_html($fourth_subject['mark']['total'] ?? '-'); ?></strong></td>
								<?php if ($show_retake_columns) : ?>
									<td><?php echo esc_html(isset($fourth_subject['mark']['retake_mark']) ? $this->format_retake_mark_value($fourth_subject['mark']['retake_mark']) : '-'); ?></td>
									<td><?php echo esc_html(isset($fourth_subject['mark']['final_counted_mark']) ? $this->format_retake_mark_value($fourth_subject['mark']['final_counted_mark']) : $this->format_retake_mark_value($fourth_subject['mark']['total'] ?? '-')); ?></td>
								<?php endif; ?>
								<?php else : // single ?>
								<td><strong><?php echo esc_html($fourth_subject['mark']['total'] ?? ($fourth_subject['mark'] ?? '-')); ?></strong></td>
								<?php if ($show_retake_columns) : ?>
									<td><?php echo esc_html(is_array($fourth_subject['mark']) && isset($fourth_subject['mark']['retake_mark']) ? $this->format_retake_mark_value($fourth_subject['mark']['retake_mark']) : '-'); ?></td>
									<td><?php echo esc_html(is_array($fourth_subject['mark']) && isset($fourth_subject['mark']['final_counted_mark']) ? $this->format_retake_mark_value($fourth_subject['mark']['final_counted_mark']) : $this->format_retake_mark_value(is_array($fourth_subject['mark']) ? ($fourth_subject['mark']['total'] ?? '-') : $fourth_subject['mark'])); ?></td>
								<?php endif; ?>
							<?php endif; ?>
                                                                <td><?php echo esc_html($fourth_subject['remark']); ?></td>
                                                                <td><?php echo esc_html($fourth_subject['grade_point']); ?></td>
                                                                <td><?php echo esc_html($fourthsubjectgpa); ?></td>
                                                </tr>
						<?php
						endif;
						// Render the final Total row
						// Dynamically calculate colspan for the final total marks
						$total_marks_colspan = ($exam_type === 'triple') ? 4 : (($exam_type === 'composite') ? 3 : 2);
						if ($show_retake_columns) {
							$total_marks_colspan += 2;
						}
						?>
						<tr>
							<th>Total</th>
							<th colspan="<?php echo $total_marks_colspan; ?>"><?php echo esc_html($totals['obtained_marks']); ?></th>
							<th colspan="3"><?php echo esc_html($totals['total_remark']); ?></th>
						</tr>
					</tbody>
				</table>
				<!-- Result Summary -->
				<table class="table-style">
					<tr>
						<th>Percentage</th>
						<td class="result-td"><?php echo number_format($totals['percentage'], 2); ?>%</td>
						<th>Result</th>
						<td class="result-td">
							<?php
							// If fail on single subject is enabled, override the final result to 'Failed' if any subject is failed
							if ($fail_on_single_subject) {
								echo $is_failed ? 'Failed' : esc_html($final_letter_grade);
							} else {
								echo esc_html($final_letter_grade);
							}
							?>
						</td>
						<th>Rank</th>
						<td class="result-td"><?php echo esc_html($student['rank'] ?? '' ); ?></td>
					</tr>
				</table>
				
			<div class="signature-area">
				<?php if ( ! empty( $result_text['office_seal'] ) ) : ?>
					<div class="signature-box office-seal">
						<img src="<?php echo esc_url( $result_text['office_seal'] ); ?>" alt="office seal">
						<span class="signature-caption">Institute Seal</span>
					</div>
				<?php endif; ?>
				<?php if ( ! empty( $result_text['principal_signature'] ) ) : ?>
					<div class="signature-box principal-signature">
						<img src="<?php echo esc_url( $result_text['principal_signature'] ); ?>" alt="principal signature">
						<span class="signature-caption">Authorized Signature</span>
					</div>
				<?php endif; ?>
			</div>
			<div id="result-footer">
				<?php echo esc_html($result_text['result_footer_text']); ?>
			</div>
			</div>
			
		</div>
	<?php
	}

	/**
	 * Calculate the total results for the given subjects.
	 *
	 * @param array $subjects An array of subjects to calculate the totals for.
	 * @return array The calculated totals for each subject.
	*/
	private function calculate_result_totals($subjects, $fourth_subject_code = null, $exam_id = null)
	{
		// Initialize variables
		$main_subjects_obtained = 0; // Total marks from main subjects ONLY, for percentage calculation and display
		$main_subjects_total    = 0; // The denominator for the percentage (max marks of main subjects)
		$total_min_marks        = 0; // Total minimum passing marks for main subjects
	
		foreach ($subjects as $subject) {
			// This part correctly gets the 'total' obtained mark for a subject
			$obtained_mark = 0;
			if (isset($subject['mark'])) {
				$obtained_mark = is_array($subject['mark']) ? floatval($subject['mark']['total'] ?? 0) : floatval($subject['mark']);
			}
			
			$subcode    = $subject['subject']['subcode'] ?? null;
			$is_optional = !empty($subject['is_fourth']) || ($fourth_subject_code !== null && $subcode === $fourth_subject_code);
			
			// Only include the subject if it's NOT a fourth/optional subject.
			if (! $is_optional) {
				$main_subjects_obtained += $obtained_mark;
				$main_subjects_total    += floatval($subject['subject']['total']);
				$total_min_marks        += floatval($subject['subject']['minmark']);
			}
		}
		
		// Now, calculate the percentage using ONLY the main subject totals.
		$percentage = $main_subjects_total > 0 ? ($main_subjects_obtained * 100 / $main_subjects_total) : 0;
		
		// Determine total remark and final result based on the correct percentage
		$total_remark = $this->get_total_remark($percentage, $exam_id);
		
		return array(
			'obtained_marks'  => $main_subjects_obtained, // Total for compulsory subjects only
			'total_marks'     => $main_subjects_total,
			'total_min_marks' => $total_min_marks,
			'percentage'      => $percentage,
			'total_remark'    => $total_remark,
		);
	}

	/**
	 * Renders the download buttons for the result management interface.
	 *
	 * This function generates and displays the buttons that allow users to download
	 * results in various formats.
	 *
	 * @return void
	 */
	private function astgdrmsys_render_download_buttons()
	{ ?>
		<center>
			<input type="button"
				id="printResult"
				class="btn btn-info"
				value="Print Marksheet">
			<input type="button"
				id="downloadResult"
				class="btn btn-info"
				value="Download Marksheet">
		</center>
	<?php
	}
}

/**
 *
 * This script initializes and renders the ASTGDRMSYS_Show_Result.
 *
 * @package ResultManagementSystemResultManagement
 * @subpackage ASTGDRMSYS_Show_Result
 *
 * @class ASTGDRMSYS_Show_Result
 * @method render() Renders the result manager interface.
 */
$show_result = new ASTGDRMSYS_Show_Result($atts);
$show_result->astgdrmsys_showresult_render(); // Render the result manager interface

// ShortCode Modifer
if (!empty($atts['sclass']) || !empty($atts['sdepartment']) || !empty($atts['ssection']) || !empty($atts['s_exam_name'])) : ?>
	<script>
		jQuery(document).ready(function($) {
			const classId = <?php echo json_encode($atts['sclass'] ?? ''); ?>;
			const deptId = <?php echo json_encode($atts['sdepartment'] ?? ''); ?>;
			const sectionId = <?php echo json_encode($atts['ssection'] ?? ''); ?>;
			const examNameId = <?php echo json_encode($atts['s_exam_name'] ?? ''); ?>;

			if (classId) {
				// For <select> or <input> field with .selectClass class
				$('.selectClass').val(classId);
				setTimeout(() => {
					$('.selectClass').trigger('change');
					$('input.selectClass').trigger('change');
				}, 500);
			}

			if (deptId) {
				// Wait until class triggers department population
				setTimeout(() => {
					$('.selectDepartment').val(deptId).trigger('change');
					$('input.selectDepartment').trigger('change');
				}, 1000);
			}

			if (sectionId) {
				setTimeout(() => {
					$('.selectSection').val(sectionId).trigger('change');
					$('input.selectSection').trigger('change');
				}, 1500);
			}

			if (examNameId) {
				setTimeout(() => {
					$('.selectExam').val(examNameId).trigger('change');
					$('input.selectExam').trigger('change');
				}, 2000);
			}
		});
	</script>
<?php endif;
