<?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.
 */
/**
 * Prevents direct access to the file.
 *
 * This code checks if the file is being accessed directly by verifying if the
 * ABSPATH constant is defined. If ABSPATH is not defined, it means the file
 * is being accessed directly, and the script will exit to prevent unauthorized access.
 *
 * @package ResultManagementSystem
 * @subpackage MenuFiles/Mark
 */
if (! defined('ABSPATH')) {
        exit;
}
require_once ASTGDRMSYS_PLUGIN_DIR . 'includes/traits/class-astgdrmsys-grade-helper.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_Download_Marksheet
 *
 * Handles the generation and downloading of PDF marksheets for an entire class.
 */
class ASTGDRMSYS_Download_Marksheet
{
        use ASTGDRMSYS_Grade_Helper;
        use ASTGDRMSYS_Exam_Label_Helper;
        use ASTGDRMSYS_Student_Custom_Field_Helper;
        use ASTGDRMSYS_Retake_Helper;

	/**
	 * @var wpdb WordPress database object
	 */
	private $wpdb;

	/**
	 * @var string Database table prefix
	 */
	private $prefix;

	/**
	 * Constructor.
	 */
	public function __construct()
	{
		global $wpdb;
		$this->wpdb   = $wpdb;
		$this->prefix = $wpdb->prefix;

		$this->init();
	}

	/**
	 * Initialize the class hooks and logic.
	 */
	private function init()
	{
		// Handle the form submission for downloading the PDF
		if (isset($_POST['astgdrmsys_download_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['astgdrmsys_download_nonce'])), 'astgdrmsys_download_action')) {
			// $this->handle_download_request();

		} else {
			// Render the main view
			$this->renderView();

			// If class and exam are selected, show students
			if (isset($_GET['classsearch'], $_GET['examname'])) {
				$this->processMarksheet();
			}
		}
	}
	/**
	 * Renders the selection form for class and exam.
	 */
	public function renderView()
	{ ?>
                <div class='container marksheet-filter-container'>
			<div class="premium-page-header">
				<div class="page-header-text">
					<h1><?php esc_html_e('Download Marksheet', 'result-management-system-for-institutions'); ?></h1>
					<p><?php esc_html_e('Download marksheet for all students in a class or a specific exam.', 'result-management-system-for-institutions'); ?></p>
				</div>
			</div>
			<form method='get' action='' id="submitForm" style="background: #ffffff; padding: 25px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.02); border: 1px solid #e2e8f0; margin-bottom: 30px;">
				<input type="hidden" name="page" value="astgdrmsys-download-marksheet">
				<div style="display: flex; flex-wrap: wrap; gap: 20px; align-items: flex-end;">
					<div class="filter-group" style="flex: 1; min-width: 200px;">
						<label style="display: block; font-size: 11px; font-weight: 700; color: #1e293b; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;"><?php esc_html_e('Select Class', 'result-management-system-for-institutions'); ?></label>
						<select class="selectClass premium-input" name="classsearch" required style="width: 100%;">
							<option value="" selected disabled><?php esc_html_e('Select a Class', 'result-management-system-for-institutions'); ?></option>
							<?php
							$classes = $this->fetchClasses();
							foreach ($classes as $class) {
								$selected = (isset($_GET['classsearch']) && $_GET['classsearch'] == $class['id']) ? 'selected' : '';
								echo "<option value='" . esc_attr($class['id']) . "' $selected>" . esc_html($class['class']) . '</option>';
							}
							?>
						</select>
					</div>
					<div class="filter-group" style="flex: 1; min-width: 200px;">
						<label style="display: block; font-size: 11px; font-weight: 700; color: #1e293b; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px;"><?php esc_html_e('Select Exam Name', 'result-management-system-for-institutions'); ?></label>
						<select class="selectExam premium-input" name="examname" required style="width: 100%;">
							<option value=""><?php esc_html_e('Select Class First', 'result-management-system-for-institutions'); ?></option>
						</select>
					</div>
					<div class="filter-actions" style="margin-top: 0;">
						<input type='submit' value='<?php esc_attr_e('View Marksheet', 'result-management-system-for-institutions'); ?>' class='btn btn-primary submit-btn hero-btn' style="height: 42px; margin-bottom: 0;">
					</div>
				</div>
			</form>
		</div> <?php
	}

	/**
	 * Fetch classes
	 *
	 * @return array List of classes
	 */
	public function fetchClasses()
	{
		$class_table = esc_sql($this->prefix . 'astgdrmsys_class');
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		return $this->wpdb->get_results(
			// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
			"SELECT * FROM $class_table",
			ARRAY_A
		);
	}

	/**
	 * Process the marksheet generation based on selected class and exam.
	 */
	public function processMarksheet()
	{
		if (isset($_GET['classsearch'], $_GET['examname']) && ! empty($_GET['classsearch']) && ! empty($_GET['examname'])) {
			$class_id = sanitize_text_field(wp_unslash($_GET['classsearch']));
			$exam_id  = sanitize_text_field(wp_unslash($_GET['examname']));

			// Fetch students for the selected class and exam
			$students = $this->fetchStudents($class_id, $exam_id);

			// Render the marksheet view with the fetched students
			$this->renderMarksheetView($students, $class_id, $exam_id);
		}
	}

	/**
	 * Fetch students based on class and exam.
	 *
	 * @param string $class_id The ID of the class.
	 * @param string $exam_id The ID of the exam.
	 * @return array List of students.
	 */
	public function fetchStudents($classsearch, $examNameSearch, $searchvalue = '')
	{
		global $wpdb;

		$classsearch    = (int) $classsearch;
		$examNameSearch = (int) $examNameSearch;
		$searchvalue    = sanitize_text_field($searchvalue);

		$result_table = esc_sql($this->prefix . 'astgdrmsys_student_result');

		$args        = array($classsearch, $examNameSearch);
		$searchQuery = '';

		if (! empty($searchvalue)) {
			$searchQuery = ' AND (CAST(`sno` AS UNSIGNED) LIKE %s OR `name` LIKE %s)';
			$like_search = '%' . $wpdb->esc_like($searchvalue) . '%';
			$args[]      = $like_search;
			$args[]      = $like_search;
		}

		$sql = "SELECT * FROM {$result_table} WHERE `class` = %d AND `exam_name` = %d {$searchQuery} ORDER BY CAST(`sno` AS UNSIGNED) ASC";

		// Generate a unique cache key based on query arguments.
		$cache_key = 'students_' . md5(maybe_serialize($args));
		$students  = wp_cache_get($cache_key, 'rms');

		if (false === $students) {
			// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
			$students = $wpdb->get_results(
				// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
				$wpdb->prepare($sql, $args),
				ARRAY_A
			);

			wp_cache_set($cache_key, $students, 'rms', 300); // Cache for 5 minutes.
		}

		return $students;
	}

	/**
	 * Fetch class name
	 *
	 * @param int $classId Class ID
	 * @return string Class name
	 */
	public function fetchClassName($classId)
	{
		global $wpdb;
		// sanitize sql
		$class_table = esc_sql($this->prefix . 'astgdrmsys_class');
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		return $wpdb->get_var(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				"SELECT class FROM $class_table WHERE id = %d",
				$classId
			)
		);
	}

	/**
	 * Fetch exam name
	 *
	 * @param int $examId Exam ID
	 * @return string Exam name
	 */
	public function fetchExamName($examId)
	{
		global $wpdb;
		$examname_table = esc_sql($this->prefix . 'astgdrmsys_exam_name');

		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		return $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM $examname_table WHERE id = %d",
				$examId
			),
			ARRAY_A
		);
	}

	/**
	 * Fetch subjects for a given class.
	 *
	 * @param string $class_id Class ID
	 * @return array List of subjects with subcode as key
	 */
	public function fetchSubjectsByClass($class_id)
	{
		$subject_table = esc_sql($this->prefix . 'astgdrmsys_subject');

		$subjects = $this->wpdb->get_results(
			$this->wpdb->prepare(
				"SELECT subcode, subname FROM $subject_table WHERE class_id = %s",
				$class_id
			),
			ARRAY_A
		);

		// Convert to subcode => subject_name map
		$subject_map = [];
		foreach ($subjects as $subject) {
			$subject_map[$subject['subcode']] = $subject['subname'];
		}

		return $subject_map;
	}

        /**
         * Collects all custom field labels present in the selected student result snapshots.
         *
         * @param array<int, array<string, mixed>> $students Student result rows.
         * @return array<int, string>
         */
        private function get_custom_field_labels_for_students(array $students): array
        {
                $labels = [];

                foreach ($students as $student) {
                        $fields = $this->parse_student_custom_fields($student['custom_fields'] ?? '');

                        foreach ($fields as $field) {
                                if (! in_array($field['name'], $labels, true)) {
                                        $labels[] = $field['name'];
                                }
                        }
                }

                return $labels;
        }

        /**
         * Converts custom fields into a label => value map for marksheet table rows.
         *
         * @param mixed $raw_value Stored custom_fields value from student_result.
         * @return array<string, string>
         */
        private function get_student_custom_field_map($raw_value): array
        {
                $map = [];
                foreach ($this->parse_student_custom_fields($raw_value) as $field) {
                        $map[$field['name']] = $field['value'];
                }

                return $map;
        }

        /**
	 * Renders the marksheet view for a given set of students, class, and exam.
	 * This method dynamically generates a marksheet table based on the exam type ('single', 'composite', or 'triple'),
	 *
	 * displaying student marks, grades, and ranks for each subject. It fetches display names for mark columns from
	 * WordPress options, and handles cases where student or subject data is missing.
	 *
	 * Table columns adapt to the exam type:
	 * - 'single': Exam Mark + Grade
	 * - 'composite': Class Mark + Final Mark + Total Mark + Grade
	 * - 'triple': Practical Mark + Class Mark + Final Mark + Total Mark + Grade
	 *
	 * @param array $students   List of students with their details and ranks.
	 * @param int   $class_id   The class ID for which the marksheet is rendered.
	 * @param int   $exam_id    The exam ID for which the marksheet is rendered.
	 *
	 * @return void
	*/
        public function renderMarksheetView($students, $class_id, $exam_id)
        {
                // 1. Fetch essential data and determine the exam type
                $exam_data = $this->fetchExamName($exam_id);
                $subject_overrides = $this->parse_exam_subject_full_marks($exam_data['subject_full_marks'] ?? null);
                $subjects = $this->get_subjects_by_class($class_id, $exam_data['department'] ?? null, $subject_overrides);
                $exam_type = $exam_data['exam_type'] ?? 'single'; // Determine the exam type
                $fail_on_single_subject = ! empty($exam_data['fail_on_single_subject']);
                $show_retake_columns = $this->any_student_has_retake_marks($students);

                $display_names = $this->resolve_exam_mark_labels($exam_data);
		
                if (empty($students) || empty($subjects)) {
                        echo '<div class="notice notice-warning"><p>No student results or subjects found for this class and exam.</p></div>';
                        return;
                }

                list($students, $gpa_cache) = $this->assign_student_ranks(
                        $students,
                        $subjects,
                        $fail_on_single_subject,
                        $exam_type
                );

                $custom_field_labels = $this->get_custom_field_labels_for_students($students);
                ?>
                <div class="container-fluid marksheet-container">
                        <center>
                                <h3>Marksheet for Class: <?php echo esc_html($this->fetchClassName($class_id)); ?></h3>
                                <h4>Exam: <?php echo esc_html($exam_data['exam_name']); ?></h4>
                        </center>
                        <div class="marksheet-column-controls">
                                <div class="marksheet-column-controls__body">
                                        <h5 class="marksheet-column-controls__title">Subject Columns</h5>
                                        <p class="marksheet-column-controls__subtitle">Uncheck a subject to hide its marks from the table below.</p>
                                        <div class="subject-toggle-grid">
                                                <?php foreach ($subjects as $subcode => $subject) :
                                                        $subject_class = 'subject-col-' . sanitize_html_class($subcode);
                                                ?>
                                                        <div class="form-check subject-toggle-grid__item">
                                                                <input
                                                                        class="form-check-input subject-toggle-checkbox"
                                                                        type="checkbox"
                                                                        id="toggle-<?php echo esc_attr($subject_class); ?>"
                                                                        data-target="<?php echo esc_attr($subject_class); ?>"
                                                                        checked
                                                                >
                                                                <label class="form-check-label subject-toggle-label" for="toggle-<?php echo esc_attr($subject_class); ?>">
                                                                        <?php echo esc_html($subject['subname']); ?>
                                                                </label>
                                                        </div>
                                                <?php endforeach; ?>
                                        </div>
                                </div>
                        </div>
                        <div class="marksheet-table-scroll">
                                <div class="marksheet-horizontal-scroll" aria-hidden="true">
                                        <div class="marksheet-horizontal-scroll__spacer"></div>
                                </div>
                                <div class="table-responsive">
                                        <table class="table table-bordered table-striped marksheet-table">
                                        <thead class="thead-dark">
                                                <!-- Headers for Marksheet -->
                                                <tr>
                                                        <th rowspan="2">Roll No.</th>
                                                        <th rowspan="2">Name</th>
                                                        <?php foreach ($custom_field_labels as $custom_label) : ?>
                                                                <th rowspan="2"><?php echo esc_html($custom_label); ?></th>
                                                        <?php endforeach; ?>
                                                        <th rowspan="2">Rank</th>
                                                        <?php foreach ($subjects as $subcode => $subject) :
                                                                $subject_class = 'subject-col-' . sanitize_html_class($subcode);
                                                                // Dynamically calculate the colspan for each subject header
                                                                // based on the number of sub-columns it will have.
                                                                $colspan = 2; // Default for 'single' (Mark + Grade)
                                                                if ($exam_type === 'composite') {
                                                                        $colspan = 4; // Class Mark + Final Mark + Total Mark + Grade
								 } elseif ($exam_type === 'triple') {
									 $colspan = 5; // Practical + Class + Final + Total + Grade
								 }
								 if ($show_retake_columns) {
									 $colspan += 2; // Retake + Final Counted
								 }
								?>
                                                                <th class="marksheet-header subject-column <?php echo esc_attr($subject_class); ?>" colspan="<?php echo esc_attr($colspan); ?>">
                                                                        <?php echo esc_html($subject['subname']); ?>
                                                                </th>
                                                        <?php endforeach; ?>
                                                        <th rowspan="2">Total GPA</th>
                                                        <th rowspan="2">Fourth Subject GPA</th>
                                                        <th rowspan="2">Final GPA</th>
                                                        <th rowspan="2">Final Grade</th>
                                                </tr>
                                                <tr>
                                                        <?php foreach ($subjects as $subcode => $subject) :
                                                                $subject_class = 'subject-col-' . sanitize_html_class($subcode);
                                                        ?>
                                                                <?php if ($exam_type === 'triple') : ?>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>"><?php echo esc_html($display_names['practical_mark_display_name']); ?></th>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>"><?php echo esc_html($display_names['class_mark_display_name']); ?></th>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>"><?php echo esc_html($display_names['final_mark_display_name']); ?></th>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>"><?php echo esc_html($display_names['total_mark_display_name']); ?></th>
                                                                        <?php if ($show_retake_columns) : ?>
                                                                                <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>">Retake</th>
                                                                                <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>">Final Counted</th>
                                                                        <?php endif; ?>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>">Grade</th>

                                                                <?php elseif ($exam_type === 'composite') : ?>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>"><?php echo esc_html($display_names['class_mark_display_name']); ?></th>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>"><?php echo esc_html($display_names['final_mark_display_name']); ?></th>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>"><?php echo esc_html($display_names['total_mark_display_name']); ?></th>
                                                                        <?php if ($show_retake_columns) : ?>
                                                                                <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>">Retake</th>
                                                                                <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>">Final Counted</th>
                                                                        <?php endif; ?>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>">Grade</th>

                                                                <?php else : // 'single' exam type ?>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>"><?php echo esc_html($display_names['single_mark_display_name']); ?></th>
                                                                        <?php if ($show_retake_columns) : ?>
                                                                                <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>">Retake</th>
                                                                                <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>">Final Counted</th>
                                                                        <?php endif; ?>
                                                                        <th class="marksheet-subheader subject-column <?php echo esc_attr($subject_class); ?>">Grade</th>
                                                                <?php endif; ?>
                                                        <?php endforeach; ?>
                                                </tr>

                                        </thead>
					<tbody>
						<?php
						// 3. Loop through students and render marks dynamically
                                                foreach ($students as $student) :
                                                        // Use the corrected helper function
                                                        $marks = $this->get_student_marks($student['sid']);

                                                        if ('composite' === $exam_type) {
                                                                $marks = $this->normalize_composite_marks($marks);
                                                        }
                                                        $marks = $this->apply_retake_marks_to_marks($student['sid'], $marks);

                                                        $gpa_summary = $gpa_cache[$student['sid']] ?? $this->calculate_student_gpa_summary($subjects, $marks, $fail_on_single_subject);
                                                        ?>
                                                        <tr>
                                                                <td><strong><?php echo esc_html($student['sno']); ?></strong></td>
                                                                <td><?php echo esc_html($student['name']); ?></td>
                                                                <?php
                                                                $student_custom_map = $this->get_student_custom_field_map($student['custom_fields'] ?? '');
                                                                foreach ($custom_field_labels as $custom_label) {
                                                                        echo '<td>' . esc_html($student_custom_map[$custom_label] ?? '') . '</td>';
                                                                }
                                                                ?>
                                                                <td><?php echo esc_html($student['rank'] ?? '' ); ?></td>
	
								<?php
								// Loop through the canonical header subjects to maintain column order
                                                                foreach ($subjects as $subcode => $subject) :
                                                                        $subject_class = 'subject-col-' . sanitize_html_class($subcode);
                                                                        $subject_marks = $marks[$subcode] ?? null;
                                                                        // $subject_marks = $marks[0][$subcode] ?? null;

                                                                        if ($subject_marks) {
                                                                                if ($exam_type === 'triple') {
											$practical_mark = $subject_marks['practical'] ?? '-'; 
											$class_mark = $subject_marks['class'] ?? '-';
											$final_mark = $subject_marks['final'] ?? '-';
											$total_mark = $subject_marks['total'] ?? ($practical_mark + $class_mark + $final_mark);
											// Use the final mark to calculate grade
											$grade = $this->calculate_subject_remark($subject, $total_mark);
	
                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($practical_mark) . '</td>';
                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($class_mark) . '</td>';
                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($final_mark) . '</td>';
                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($total_mark) . '</td>';
                                                                                        if ($show_retake_columns) {
                                                                                                echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html(isset($subject_marks['retake_mark']) ? $this->format_retake_mark_value($subject_marks['retake_mark']) : '-') . '</td>';
                                                                                                echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html(isset($subject_marks['final_counted_mark']) ? $this->format_retake_mark_value($subject_marks['final_counted_mark']) : $this->format_retake_mark_value($total_mark)) . '</td>';
                                                                                        }
                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($grade) . '</td>';
                                                                                }
                                                                                elseif ($exam_type === 'composite') {
                                                                                        $first_mark  = $subject_marks['first_assessment'] ?? ($subject_marks['practical'] ?? $subject_marks['class'] ?? '-');
                                                                                        $second_mark = $subject_marks['second_assessment'] ?? ($subject_marks['class'] ?? $subject_marks['final'] ?? '-');

                                                                                        $first_mark_numeric  = is_numeric($first_mark) ? floatval($first_mark) : null;
                                                                                        $second_mark_numeric = is_numeric($second_mark) ? floatval($second_mark) : null;

                                                                                        $total_mark = 0;
                                                                                        $has_numeric = false;

                                                                                        foreach ([$first_mark_numeric, $second_mark_numeric] as $component) {
                                                                                                if (null !== $component) {
                                                                                                        $total_mark += $component;
                                                                                                        $has_numeric = true;
                                                                                                }
                                                                                        }

                                                                                        $total_mark = $has_numeric ? $total_mark : '-';

                                                                                        // Use the remapped total mark to calculate grade
                                                                                        $grade = $this->calculate_subject_remark($subject, ['total' => $has_numeric ? $total_mark : -1]);

                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($first_mark) . '</td>';
                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($second_mark) . '</td>';
                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($total_mark) . '</td>';
                                                                                        if ($show_retake_columns) {
                                                                                                echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html(isset($subject_marks['retake_mark']) ? $this->format_retake_mark_value($subject_marks['retake_mark']) : '-') . '</td>';
                                                                                                echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html(isset($subject_marks['final_counted_mark']) ? $this->format_retake_mark_value($subject_marks['final_counted_mark']) : $this->format_retake_mark_value($total_mark)) . '</td>';
                                                                                        }
                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($grade) . '</td>';
                                                                                } else {
                                                                                        // This is for 'single' exam type
                                                                                        $final_mark = is_array($subject_marks)
                                                                                                        ? ($subject_marks['final'] ?? $subject_marks['total'] ?? '')
                                                                                                        : $subject_marks;
                                                                                        $grade_mark = is_array($subject_marks) && isset($subject_marks['final_counted_mark']) ? $subject_marks['final_counted_mark'] : $final_mark;
                                                                                        $grade = $this->calculate_subject_remark($subject, $grade_mark);

                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($final_mark) . '</td>';
                                                                                        if ($show_retake_columns) {
                                                                                                echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html(isset($subject_marks['retake_mark']) ? $this->format_retake_mark_value($subject_marks['retake_mark']) : '-') . '</td>';
                                                                                                echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html(isset($subject_marks['final_counted_mark']) ? $this->format_retake_mark_value($subject_marks['final_counted_mark']) : $this->format_retake_mark_value($final_mark)) . '</td>';
                                                                                        }
                                                                                        echo '<td class="' . esc_attr('subject-column ' . $subject_class) . '">' . esc_html($grade) . '</td>';
                                                                                }
                                                                        } else {
                                                                                // Student does not have this subject, fill with empty cells
                                                                                $colspan = ($exam_type === 'composite') ? 4 : 2;
                                                                                if ('triple' === $exam_type) {
                                                                                        $colspan = 5;
                                                                                }
                                                                                if ($show_retake_columns) {
                                                                                        $colspan += 2;
                                                                                }
                                                                                echo '<td colspan="' . esc_attr($colspan) . '" class="text-center bg-light subject-column ' . esc_attr($subject_class) . '">-</td>';
                                                                        }
                                                                endforeach;

                                                                $total_gpa_display = number_format_i18n((float) ($gpa_summary['total_gpa'] ?? 0), 2);
                                                                $fourth_subject_gpa_display = number_format_i18n((float) ($gpa_summary['fourth_subject_gpa'] ?? 0), 2);
                                                                $final_gpa_display = number_format_i18n((float) ($gpa_summary['final_gpa'] ?? 0), 2);
                                                                $final_grade_display = $gpa_summary['final_grade'] ?? 'N/A';

                                                                echo '<td>' . esc_html($total_gpa_display) . '</td>';
                                                                echo '<td>' . esc_html($fourth_subject_gpa_display) . '</td>';
                                                                echo '<td>' . esc_html($final_gpa_display) . '</td>';
                                                                echo '<td>' . esc_html($final_grade_display) . '</td>';
                                                                ?>
                                                        </tr>
                                                <?php endforeach; ?>
                                        </tbody>
                                        </table>
                                </div>
                        </div>
                </div><?php
                $this->enqueue_column_toggle_assets();
                $this->astgdrmsys_render_download_buttons();
        }

        /**
         * Prints the inline styles and scripts required for toggling subject columns.
         *
         * @return void
         */
        private function enqueue_column_toggle_assets()
        { ?>
                <style>
                        .marksheet-column-controls {
                                margin: 1.5rem 0;
                                background: #ffffff;
                                border: 1px solid #e1e5ea;
                                border-radius: 8px;
                                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
                        }

                        .marksheet-column-controls__body {
                                padding: 1.25rem 1.5rem;
                        }

                        .marksheet-column-controls__title {
                                font-weight: 600;
                                margin-bottom: 0.25rem;
                        }

                        .marksheet-column-controls__subtitle {
                                margin-bottom: 1rem;
                                color: #6c757d;
                        }

                        .subject-toggle-grid {
                                display: flex;
                                flex-wrap: wrap;
                                gap: 0.75rem 1.5rem;
                        }

                        .subject-toggle-grid__item {
                                min-width: 200px;
                                display: flex;
                                align-items: center;
                                gap: 0.5rem;
                        }

                        .subject-toggle-label {
                                font-weight: 500;
                        }

                        .marksheet-column-controls .form-check {
                                margin: 0;
                        }

                        .marksheet-column-controls .form-check-input {
                                margin: 0;
                        }

                        .marksheet-column-controls .form-check-label {
                                cursor: pointer;
                        }

                        .marksheet-action-buttons {
                                margin: 1.5rem 0;
                        }

                        .subject-column.is-hidden {
                                display: none !important;
                        }

                        .marksheet-table-scroll {
                                position: relative;
                        }

                        .marksheet-horizontal-scroll {
                                overflow-x: auto;
                                overflow-y: hidden;
                                height: 12px;
                                margin-bottom: 0.75rem;
                                position: sticky;
                                top: 0;
                                background: #ffffff;
                                z-index: 5;
                        }

                        .marksheet-horizontal-scroll__spacer {
                                height: 1px;
                        }

                        @media print {
                                body {
                                        margin: 0;
                                        padding: 0;
                                        background: #ffffff;
                                }

                                .marksheet-filter-container,
                                .marksheet-column-controls,
                                .marksheet-action-buttons {
                                        display: none !important;
                                }

                                .marksheet-container {
                                        margin: 0;
                                        padding: 0;
                                }

                                .marksheet-container .table {
                                        width: 100%;
                                }

                                .marksheet-horizontal-scroll {
                                        display: none !important;
                                }
                        }
                </style>
                <script>
                        (function () {
                                const checkboxes = document.querySelectorAll('.subject-toggle-checkbox');
                                let refreshHorizontalScrollbar = null;

                                if (checkboxes.length) {
                                        checkboxes.forEach(function (checkbox) {
                                                checkbox.addEventListener('change', function () {
                                                        const targetClass = this.dataset.target;
                                                        if (!targetClass) {
                                                                return;
                                                        }

                                                        const cells = document.querySelectorAll('.' + targetClass);
                                                        cells.forEach(function (cell) {
                                                                if (checkbox.checked) {
                                                                        cell.classList.remove('is-hidden');
                                                                } else {
                                                                        cell.classList.add('is-hidden');
                                                                }
                                                        });

                                                        if (typeof refreshHorizontalScrollbar === 'function') {
                                                                refreshHorizontalScrollbar();
                                                        }
                                                });
                                        });
                                }

                                const scrollWrapper = document.querySelector('.marksheet-table-scroll');
                                const scrollTopBar = scrollWrapper ? scrollWrapper.querySelector('.marksheet-horizontal-scroll') : null;
                                const scrollTopSpacer = scrollTopBar ? scrollTopBar.querySelector('.marksheet-horizontal-scroll__spacer') : null;
                                const scrollContent = scrollWrapper ? scrollWrapper.querySelector('.table-responsive') : null;

                                if (scrollWrapper && scrollTopBar && scrollTopSpacer && scrollContent) {
                                        const updateSpacerWidth = function () {
                                                const table = scrollContent.querySelector('table');
                                                if (!table) {
                                                        return;
                                                }

                                                scrollTopSpacer.style.width = table.scrollWidth + 'px';
                                        };

                                        refreshHorizontalScrollbar = updateSpacerWidth;

                                        const syncScroll = function (source, target) {
                                                if (target.scrollLeft !== source.scrollLeft) {
                                                        target.scrollLeft = source.scrollLeft;
                                                }
                                        };

                                        scrollTopBar.addEventListener('scroll', function () {
                                                syncScroll(scrollTopBar, scrollContent);
                                        });

                                        scrollContent.addEventListener('scroll', function () {
                                                syncScroll(scrollContent, scrollTopBar);
                                        });

                                        window.addEventListener('resize', updateSpacerWidth);
                                        updateSpacerWidth();
                                }
                        })();
                </script>
        <?php
        }

        /**
         * Attach calculated ranks to each student based on their final GPA.
         *
         * @param array  $students                List of students.
         * @param array  $subjects                Subject definitions for the class.
         * @param bool   $fail_on_single_subject  Whether failing a single subject should fail the exam.
         * @param string $exam_type               Current exam type (single/composite/triple).
         *
         * @return array{0: array, 1: array} Updated students list with ranks and a GPA cache keyed by student ID.
         */
        private function assign_student_ranks($students, $subjects, $fail_on_single_subject, $exam_type)
        {
                if (empty($students) || empty($subjects)) {
                        return [$students, []];
                }

                $ranked_students = $students;
                $gpa_cache       = [];
                $gpa_entries     = [];

                foreach ($ranked_students as $index => $student) {
                        $marks = $this->get_student_marks($student['sid']);

                        if ('composite' === $exam_type) {
                                $marks = $this->normalize_composite_marks($marks);
                        }
                        $marks = $this->apply_retake_marks_to_marks($student['sid'], $marks);

                        $gpa_summary = $this->calculate_student_gpa_summary($subjects, $marks, $fail_on_single_subject);
                        $gpa_cache[$student['sid']] = $gpa_summary;

                        $final_gpa = is_numeric($gpa_summary['final_gpa']) ? (float) $gpa_summary['final_gpa'] : null;

                        $gpa_entries[] = [
                                'index' => $index,
                                'gpa'   => $final_gpa,
                        ];
                }

                usort(
                        $gpa_entries,
                        function ($a, $b) {
                                if (null === $a['gpa'] && null === $b['gpa']) {
                                        return 0;
                                }

                                if (null === $a['gpa']) {
                                        return 1;
                                }

                                if (null === $b['gpa']) {
                                        return -1;
                                }

                                if ($a['gpa'] === $b['gpa']) {
                                        return 0;
                                }

                                return ($a['gpa'] > $b['gpa']) ? -1 : 1;
                        }
                );

                $previous_gpa       = null;
                $current_rank       = 0;
                $processed_with_gpa = 0;

                foreach ($gpa_entries as $entry) {
                        if (null === $entry['gpa']) {
                                $ranked_students[$entry['index']]['rank'] = '';
                                continue;
                        }

                        ++$processed_with_gpa;

                        if (null === $previous_gpa || $entry['gpa'] !== $previous_gpa) {
                                $current_rank = $processed_with_gpa;
                                $previous_gpa = $entry['gpa'];
                        }

                        $ranked_students[$entry['index']]['rank'] = $current_rank;
                }

                return [$ranked_students, $gpa_cache];
        }

        /**
         * Normalize composite exam marks to rely on only the first and second assessments.
         *
         * 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 assessments so downstream grade and GPA calculations stay aligned with
         * the displayed columns.
         *
         * @param array $marks Subject marks keyed by subject code.
         * @return array Remapped marks containing `first_assessment`, `second_assessment`, and
         *               an optional `total`.
         */
        private function normalize_composite_marks(array $marks)
        {
                $normalized = $marks;

                foreach ($marks as $subcode => $subject_mark) {
                        if (in_array($subcode, ['fourth_subject'], true)) {
                                continue;
                        }

                        $normalized[$subcode] = $this->normalize_composite_subject_mark($subject_mark);
                }

                return $normalized;
        }

        /**
         * Normalize a single subject's composite mark to two assessments.
         *
         * @param mixed $subject_mark Raw subject mark data.
         * @return array Normalized subject mark data.
         */
        private function normalize_composite_subject_mark($subject_mark)
        {
                if (! is_array($subject_mark)) {
                        $first_numeric = is_numeric($subject_mark) ? floatval($subject_mark) : null;

                        $normalized = array(
                                'first_assessment'  => $first_numeric ?? $subject_mark,
                                'second_assessment' => '-',
                        );

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

                        return $normalized;
                }

                if (isset($subject_mark['first_assessment']) || isset($subject_mark['second_assessment'])) {
                        $first  = $subject_mark['first_assessment'] ?? null;
                        $second = $subject_mark['second_assessment'] ?? null;

                        $first_numeric  = is_numeric($first) ? floatval($first) : null;
                        $second_numeric = is_numeric($second) ? floatval($second) : null;

                        $total = $this->sum_two_assessments($first_numeric, $second_numeric);

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

                        return $subject_mark;
                }

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

                $first_numeric  = is_numeric($first_raw) ? floatval($first_raw) : null;
                $second_numeric = is_numeric($second_raw) ? floatval($second_raw) : null;

                $normalized = array(
                        'first_assessment'  => $first_numeric ?? ($first_raw ?? '-'),
                        'second_assessment' => $second_numeric ?? ($second_raw ?? '-'),
                );

                $total = $this->sum_two_assessments($first_numeric, $second_numeric);

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

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

                return $normalized;
        }

        /**
         * Safely sum two numeric assessments.
         *
         * @param float|null $first  First numeric value.
         * @param float|null $second Second numeric value.
         * @return float|null Sum when at least one component is numeric, otherwise null.
         */
        private function sum_two_assessments($first, $second)
        {
                $total = 0;
                $has_numeric = false;

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

                return $has_numeric ? $total : null;
        }

        /**
         * 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 two-part 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;
        }

        /**
         * Get subject-wise marks for a student in a given exam.
	 *
	 * @param int $student_id
	 * @param int $exam_id
	 * @return array subcode => marks
	 */
	public function get_student_marks($student_id)
	{
		$marks_table = esc_sql($this->prefix . 'astgdrmsys_mark');

		$serialized_marks = $this->wpdb->get_var(
			$this->wpdb->prepare(
				"SELECT marks FROM $marks_table WHERE sid = %d", // Filter by student AND exam
				$student_id
			)
		);

                if ($serialized_marks) {
                        $marks = maybe_unserialize($serialized_marks);

                        if (! is_array($marks)) {
                                return [];
                        }

                        $normalized_marks = isset($marks[0]) && is_array($marks[0]) ? $marks[0] : $marks;

                        if (isset($marks['fourth_subject']) && ! isset($normalized_marks['fourth_subject'])) {
                                $normalized_marks['fourth_subject'] = $marks['fourth_subject'];
                        }

                        return $normalized_marks;
                }

                return []; // Return an empty array if no marks are found
        }

        /**
         * Calculates GPA summary metrics for a student.
         *
         * Generates the total GPA sum for mandatory subjects, the adjusted GPA contribution
         * from the fourth subject, the final GPA average, and the corresponding letter grade.
         *
         * @param array $subjects      List of class subjects indexed by subject code.
         * @param array $student_marks Marks array returned by get_student_marks().
         * @param bool  $fail_on_single_subject Whether to force an overall fail when any mandatory subject is failed.
         *
         * @return array{
         *     total_gpa: float,
         *     fourth_subject_gpa: float,
         *     final_gpa: float,
         *     final_grade: string
         * }
         */
        private function calculate_student_gpa_summary($subjects, $student_marks, $fail_on_single_subject = false)
        {
                $summary = [
                        'total_gpa'           => 0.0,
                        'fourth_subject_gpa'  => 0.0,
                        'final_gpa'           => 0.0,
                        'final_grade'         => 'N/A',
                ];

                if (empty($subjects) || ! is_array($student_marks)) {
                        return $summary;
                }

                $fourth_subject_code = $student_marks['fourth_subject'] ?? null;
                $mandatory_subject_count = 0;
                $total_gpa_sum = 0.0;
                $fourth_subject_gpa = 0.0;
                $has_failed_mandatory = false;

                foreach ($subjects as $subcode => $subject) {
                        if (! isset($student_marks[$subcode])) {
                                continue;
                        }

                        $subject_mark = $student_marks[$subcode];

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

                        $grade_point = $this->calculate_grade_point($subject, $subject_mark);

                        if (! empty($fourth_subject_code) && (string) $subcode === (string) $fourth_subject_code) {
                                if ($grade_point > 2) {
                                        $fourth_subject_gpa = max(0, $grade_point - 2);
                                }

                                continue;
                        }

                        $mandatory_subject_count++;
                        $subject_gpa = $this->calculate_gpa_without_4($subject, $subject_mark);

                        if ($subject_gpa <= 0 && $grade_point > 0) {
                                $subject_gpa = $grade_point;
                        }

                        $total_gpa_sum += $subject_gpa;

                        if ($grade_point < 1) {
                                $has_failed_mandatory = true;
                        }
                }

                $total_gpa_sum = round($total_gpa_sum, 2);
                $fourth_subject_gpa = round($fourth_subject_gpa, 2);

                if ($mandatory_subject_count > 0) {
                        $final_gpa = min(5, round(($total_gpa_sum + $fourth_subject_gpa) / $mandatory_subject_count, 2));
                } else {
                        $final_gpa = 0.0;
                }

                $summary['total_gpa'] = $total_gpa_sum;
                $summary['fourth_subject_gpa'] = $fourth_subject_gpa;
                $summary['final_gpa'] = $final_gpa;
                $summary['final_grade'] = $this->get_letter_grade_by_gpa($final_gpa);

                if ($fail_on_single_subject && $has_failed_mandatory) {
                        $summary['final_grade'] = 'F';
                }

                return $summary;
        }

	/**
	 * 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()
        { ?>
                <div class="marksheet-action-buttons text-center">
                        <!-- Print button -->
                        <input type="button"
                        id="printMarksheet"
                        class="btn btn-info"
                        value="Print Marksheet">
                        <!-- Download button -->
                        <input type="button"
                        id="downloadMarksheet"
                        class="btn btn-success"
                        value="Download Marksheet">
                </div> <?php
        }
}
// Initialize the class to make it run
$astgdrmsys_download_marksheet = new ASTGDRMSYS_Download_Marksheet();
