<?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-exam-label-helper.php';

/**
 * Class ASTGDRMSYS_Marks_Manager
 *
 * Handles mark operations for the Result Management System for Institutions
 */
class ASTGDRMSYS_Marks_Manager
{
        use ASTGDRMSYS_Exam_Label_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();
        }

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

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

                return $has_numeric ? $total : null;
        }

        /**
         * Normalize a composite subject mark to first/second assessments.
         *
         * @param mixed $subject_mark Subject mark payload.
         * @return array<string, mixed> Normalized 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  = $subject_mark['class'] ?? $subject_mark['practical'] ?? $subject_mark['final'] ?? null;
                $second_raw = $subject_mark['final'] ?? $subject_mark['class'] ?? $subject_mark['practical'] ?? 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;
        }

	/**
	 * Initialize the class
	 */
	private function init()
	{
		$this->displayMessage();

		// First, check if we're trying to add marks for a specific student
		if (isset($_GET['addid'])) {
			// Verify nonce when accessing student-specific actions
			if (! isset($_GET['astgdrmsys_mark_nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['astgdrmsys_mark_nonce'])), 'astgdrmsys_mark_action')) {
				wp_die(esc_html__('Invalid or expired request.', 'result-management-system-for-institutions'));
			}

			$stuid = absint(wp_unslash($_GET['addid']));
			$this->renderAddMarksForm($stuid);
		} else {
			// Render the main view (no nonce needed for initial view)
			$this->renderView();

			// If class and exam are selected, show students
			if (isset($_GET['classsearch'], $_GET['examname'])) {
				$this->handleClassExamSearch();
			}
		}
	}

	/**
	 * Display success message
	 */
	public function displayMessage()
	{
		// phpcs:ignore WordPress.Security.NonceVerification
		if (isset($_GET['insert']) && $_GET['insert'] === 'true') {
			echo "
            <div id='message_div' class='alert alert-success custom-message'>
               <div class='message-content'>
                  <center>Marks have been added Successfully.</center>
                  <button class='close-message' aria-label='Close'>&times;</button>
                  </div>
            </div>";
		}
	}

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

	/**
	 * Fetch students
	 *
	 * @param int $classsearch Class ID
	 * @param int $examNameSearch Exam ID
	 * @param string $searchvalue Search term
	 * @return array List of students
	 */
	public function fetchStudents($classsearch, $examNameSearch, $searchvalue = '')
	{
		global $wpdb;

		$classsearch    = (int) $classsearch;
		$examNameSearch = (int) $examNameSearch;
		$searchvalue    = sanitize_text_field($searchvalue);
		$exam_ids       = $this->getEquivalentExamIds($classsearch, $examNameSearch);

		$result_table     = esc_sql($this->prefix . 'astgdrmsys_student_result');
		$students_table   = esc_sql($this->prefix . 'astgdrmsys_students');
		$enrollment_table = esc_sql($this->prefix . 'astgdrmsys_student_enrollments');

		$exam_placeholders = implode(',', array_fill(0, count($exam_ids), '%d'));
		$args              = array_merge(array($classsearch), $exam_ids);
		$searchQuery       = '';

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

		$sql = "SELECT sr.*
			FROM {$result_table} sr
			LEFT JOIN {$students_table} st ON sr.student_id = st.id
			LEFT JOIN {$enrollment_table} se ON sr.enrollment_id = se.id
			WHERE sr.`class` = %d
				AND sr.`exam_name` IN ({$exam_placeholders})
				AND (sr.student_id IS NULL OR st.id IS NULL OR st.status = 'active')
				AND (sr.enrollment_id IS NULL OR se.id IS NULL OR se.status = 'active')
				{$searchQuery}
			ORDER BY CAST(sr.`sno` AS UNSIGNED) ASC";

		// Generate a unique cache key based on query arguments.
		$cache_key = 'students_active_' . 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);
		}

		return $students;
	}

	/**
	 * Fetch all internal exam IDs that represent the same class exam label.
	 *
	 * @param int $class_id Class ID.
	 * @param int $exam_id Selected exam ID.
	 * @return array<int> Equivalent exam IDs.
	 */
	private function getEquivalentExamIds($class_id, $exam_id)
	{
		$class_id = (int) $class_id;
		$exam_id  = (int) $exam_id;

		if ($class_id <= 0 || $exam_id <= 0) {
			return array($exam_id);
		}

		$exam_table = esc_sql($this->prefix . 'astgdrmsys_exam_name');
		$exam       = $this->wpdb->get_row(
			$this->wpdb->prepare(
				"SELECT exam_name, exam_year_id, COALESCE(exam_category, 'general_exam') AS exam_category FROM {$exam_table} WHERE id = %d",
				$exam_id
			),
			ARRAY_A
		);

		if (empty($exam)) {
			return array($exam_id);
		}

		$ids = $this->wpdb->get_col(
			$this->wpdb->prepare(
				"SELECT id FROM {$exam_table}
				WHERE class_id = %d
					AND exam_name = %s
					AND exam_year_id = %d
					AND COALESCE(exam_category, 'general_exam') = %s",
				$class_id,
				$exam['exam_name'],
				(int) $exam['exam_year_id'],
				$exam['exam_category']
			)
		);

		$ids = array_values(array_filter(array_map('absint', (array) $ids)));

		return ! empty($ids) ? $ids : array($exam_id);
	}

	/**
	 * Fetch exam name
	 *
	 * @param int $examId Exam ID
	 * @return string Exam name
	 */
	public function fetchExamName($examId)
	{
		global $wpdb;
		// sanitize sql
		$examname_table = esc_sql($this->prefix . 'astgdrmsys_exam_name');
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery
		return $wpdb->get_var(
			$wpdb->prepare(
				// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
				"SELECT exam_name FROM $examname_table WHERE id = %d",
				$examId
			)
		);
	}

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

	/**
	 * Render main view
	 */
	public function renderView()
	{ ?>
		<div class='container astgdrmsys-premium-wrap'>
			<div class="premium-page-header">
				<div class="page-header-text">
					<h1><?php esc_html_e( 'Manage Marks', 'result-management-system-for-institutions' ); ?></h1>
					<p>View, add, or update student marks for any exam.</p>
				</div>
			</div>
			<form method='get' action='' id="submitForm" class="premium-filter-form">
				<input type="hidden" name="page" value="astgdrmsys-add-mark">
				
				<div class="filter-group">
					<label>Select Class :</label>
					<select class="selectClass premium-input" name="classsearch" required>
						<option value="" selected disabled>Select a Class</option>
						<?php
						$classes = $this->fetchClasses();
						foreach ($classes as $class) {
							echo "<option value='" . esc_html($class['id']) . "'>" . esc_html($class['class']) . '</option>';
						}
						?>
					</select>
				</div>
				
				<div class="filter-group">
					<label>Select Exam Name:</label>
					<select class="selectExam premium-input" name="examname" required>
						<option value="">Select Class First</option>
					</select>
				</div>
				
				<div class="filter-actions" style="margin-bottom: 5px;">
					<button type='submit' class='hero-btn primary-btn submit-btn'>See Students</button>
				</div>
			</form>
		</div> <?php
	}
	
	/**
	 * Handle class and exam search
	 */
	public function handleClassExamSearch()
	{
		// Check if the form was submitted
		if (isset($_POST['searchsubmit'])) {
			// Verify nonce for security
			if (! isset($_POST['astgdrmsys_search_nonce']) || ! wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['astgdrmsys_search_nonce'])), 'astgdrmsys_search_action')) {
				wp_die(esc_html__('Invalid search request.', 'result-management-system-for-institutions'));
			}
		}
		// sanitize and validate inputs
		$classsearch    = isset($_GET['classsearch']) ? absint(wp_unslash($_GET['classsearch'])) : 0;
		$examnameFull   = isset($_GET['examname']) ? sanitize_text_field(wp_unslash($_GET['examname'])) : '';
		$examnameParts  = explode(',', $examnameFull);
		$examNameSearch = isset($examnameParts[0]) ? absint($examnameParts[0]) : 0;
		// fetch class and exam names
		$classname = $this->fetchClassName($classsearch);
		$examname  = $this->fetchExamName($examNameSearch);

		$searchvalue = isset($_POST['searchsubmit'], $_POST['search']) ? sanitize_text_field(wp_unslash($_POST['search'])) : '';
		// fetch students based on class, exam, and search value
		$students = $this->fetchStudents($classsearch, $examNameSearch, $searchvalue);
		$this->renderStudentTable($students, $classname, $examname);
	}
	
	/**
	 * Render student table
	 *
	 * @param array  $students List of students
	 * @param string $classname Class name
	 * @param string $examname Exam name
	 */
	public function renderStudentTable($students, $classname, $examname)
	{  ?>
		<div class='students-container astgdrmsys-premium-wrap m-4 px-1'>
			<center>
				<h3 class='heading'>Students in <span class="span-text"><?php echo esc_html($classname); ?></span> for Exam <span class="span-text"><?php echo esc_html($examname); ?></span></h3>
			</center>
			<div class='formcontrol'>
				<form action='' method='post'>
					<?php wp_nonce_field('astgdrmsys_search_action', 'astgdrmsys_search_nonce'); ?>
					<input type='text' class="field searchfield" name='search' placeholder='Search By Name or Roll Number' aria-label="Search" required>
					<input type='submit' class="btn btn-primary field" value="Search" name='searchsubmit'>
				</form>
			</div>
			<table class="styled-table">
				<?php if (empty($students)) { ?>
					<div class="no-records-found alert alert-warning">
						<p>No students found matching your search criteria.</p>
						<ul>
							<li>Check the class and exam name selection</li>
							<li>Verify the search term (name or roll number)</li>
							<li>Ensure students exist in the selected class and exam</li>
						</ul>
					</div>
				<?php } else { ?>
					<thead class="thead-dark">
						<tr>
							<th class="student-id-header" scope="col">Id</th>
							<th scope="col">Roll No</th>
							<th scope="col">Student Name</th>
							<th scope="col">Class</th>
							<th scope="col">Exam Name</th>
							<th scope="col">Action</th>
						</tr>
					</thead>

					<tbody>
						<?php
						foreach ($students as $index => $student) {
							$className = $this->fetchClassName($student['class']);
							$examName  = $this->fetchExamName($student['exam_name']);
						?>
							<tr>
								<th class="student-id-header"><?php echo esc_html($index + 1); ?></th>
								<td><strong><?php echo esc_html($student['sno']); ?></strong></td>
								<td><?php echo esc_html($student['name']); ?></td>
								<td><?php echo esc_html($className); ?></td>
								<td><?php echo esc_html($examName); ?></td>
								<td>
									<a class="btn btn-primary" href="<?php echo esc_url(wp_nonce_url(admin_url('admin.php?page=astgdrmsys-add-mark&addid=' . esc_html($student['sid']) . '&classsearch=' . esc_html($student['class']) . '&examname=' . esc_html($student['exam_name'])), 'astgdrmsys_mark_action', 'astgdrmsys_mark_nonce')); ?>">Add Marks</a>
								</td>
							</tr>
					<?php
						}
					}
					?>
					</tbody>
			</table>
		</div> <?php
	}
	
	/**
	 * Render add marks form
	 *
	 * @param int $stuid Student ID
	 */
        public function renderAddMarksForm($stuid)
        {
		global $wpdb;

		// Sanitize student id
		$stuid = filter_var($stuid, FILTER_SANITIZE_NUMBER_INT);

		// Tables with prefix
                $result_table = esc_sql($this->prefix . 'astgdrmsys_student_result');
                $exam_table   = esc_sql($this->prefix . 'astgdrmsys_exam_name');
                $marks_table = esc_sql($this->prefix . 'astgdrmsys_mark');

		// Get student result row
		$addstudent = $wpdb->get_row(
			$wpdb->prepare("SELECT * FROM $result_table WHERE `sid` = %d", $stuid),
			ARRAY_A
		);

		if (! $addstudent) {
			echo "<div class='error'>Student not found!</div>";
			return;
		}

		// ==================== NEW: DECODE CUSTOM FIELDS ====================
		$custom_fields_array = [];
		if (!empty($addstudent['custom_fields'])) {
			// Decode the JSON string into a PHP associative array
			$custom_fields_array = json_decode($addstudent['custom_fields'], true);
		}
		// =================================================================

		// Fetch exam_type by matching exam_name (which is id in exam_name table)
		$exam_name_id = $addstudent['exam_name'];

                $exam_row = $wpdb->get_row(
                        $wpdb->prepare(
                                "SELECT exam_type, subject_full_marks, first_assessment_display_name, second_assessment_display_name, third_assessment_display_name, total_display_name FROM $exam_table WHERE id = %d",
                                $exam_name_id
                        ),
                        ARRAY_A
                );

                if (! $exam_row) {
                        $exam_row = array(
                                'exam_type' => 'single',
                        );
                }

                $exam_type = $exam_row['exam_type'] ?? '';

                // Fallback exam_type to 'single' if not found or empty
                if (empty($exam_type)) {
                        $exam_type = 'single';
                }

                $class_id      = isset($addstudent['class']) ? absint($addstudent['class']) : 0;
                $department_id = isset($addstudent['department']) ? absint($addstudent['department']) : 0;

                $subjects = $this->get_exam_subjects_for_student($exam_row, $class_id, $department_id);

		// Get marks for student
		$marks = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT * FROM $marks_table WHERE `sid` = %d",
				$stuid
			),
			ARRAY_A
		);
		// Unserialize marks if exist
		if ($marks && isset($marks['marks'])) {
				$mark = maybe_unserialize($marks['marks']);
				$mark = is_array($mark) ? $mark : array();
		} else {
				$mark = array(); // Default empty
		}

		$current_fourth = $mark['fourth_subject'] ?? '';
		unset($mark['fourth_subject']);

		$btnLabel = empty($mark) ? 'Add Mark' : 'Update Mark';
		
                $display_names = $this->resolve_exam_mark_labels($exam_row);
		?>
		<div class='container astgdrmsys-premium-wrap'>
			<center>
				<h3 class='heading'>Add Marks for <?php echo esc_html($addstudent['name']); ?></h3>
			</center>
			<form method='post' action=''>
				<?php wp_nonce_field('astgdrmsys_mark_action', 'astgdrmsys_mark_nonce'); ?>
				<!-- ==================== NEW: DISPLAY CUSTOM FIELDS FOR EDITING ==================== -->
				<?php if (!empty($custom_fields_array)) : ?>
					<div class="custom-fields-section">
						<h4>Custom Student Information</h4>
						<table class="styled-table">
							<?php foreach ($custom_fields_array as $index => $field) : ?>
								<tr>
									<td>
										<!-- Field Name Input -->
										<label for="custom_field_name_<?php echo esc_attr($index); ?>">Field Name</label>
										<input type="text"
											id="custom_field_name_<?php echo esc_attr($index); ?>"
											name="custom_fields[<?php echo esc_attr($index); ?>][name]"
											value="<?php echo esc_attr($field['name']); ?>"
											class="form-control">
									</td>
									<td>
										<!-- Field Value Input -->
										<label for="custom_field_value_<?php echo esc_attr($index); ?>">Field Value</label>
										<input type="text"
											id="custom_field_value_<?php echo esc_attr($index); ?>"
											name="custom_fields[<?php echo esc_attr($index); ?>][value]"
											value="<?php echo esc_attr($field['value']); ?>"
											class="form-control">
									</td>
								</tr>
							<?php endforeach; ?>
						</table>
						<hr>
					</div>
				<?php endif; ?>
				<!-- ============================================================================== -->
				<h4>Exam Marks</h4>
				<table class='styled-table mark-table'>
					<thead class="thead-dark">
						<tr>
							<th>Subjects</th>
							<th>Total Mark</th>
							
							<?php // Dynamic headers based on exam type and settings
							if ($exam_type === 'triple') : ?>
								<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>
							<?php elseif ($exam_type === 'composite') : ?>
								<th><?php echo esc_html($display_names['class_mark_display_name']); ?></th>
								<th><?php echo esc_html($display_names['final_mark_display_name']); ?></th>
							<?php else : // 'single' exam type ?>
								<th><?php echo esc_html($display_names['single_mark_display_name']); ?></th>
							<?php endif; ?>
							    <th>Total Obtained</th>
								<th>Not Taken</th>
                                <th>Fourth Subject</th>
						</tr>
					</thead>
					<?php
					$column_count = ($exam_type === 'triple') ? 8 : (($exam_type === 'composite') ? 7 : 6);
					foreach ($subjects as $subject) {
						$id = $subject['subcode'];
						// Initialize all possible marks
						$practical_exam_mark = '';
						$class_exam_mark     = '';
						$final_exam_mark     = '';
						$total_obtained      = '';
						$is_not_taken        = false;
						
                                                // Check if marks exist for this subject
                                                if (isset($mark[$id])) {
                                                        if ($exam_type === 'composite') {
                                                                $normalized_mark   = $this->normalize_composite_subject_mark($mark[$id]);
                                                                $class_exam_mark   = is_numeric($normalized_mark['first_assessment'] ?? null) ? $normalized_mark['first_assessment'] : '';
                                                                $final_exam_mark   = is_numeric($normalized_mark['second_assessment'] ?? null) ? $normalized_mark['second_assessment'] : '';
                                                                $total_obtained    = $normalized_mark['total'] ?? $this->sum_two_assessments($class_exam_mark, $final_exam_mark);
                                                                $is_not_taken      = ! empty($normalized_mark['not_taken']);
                                                        } elseif (is_array($mark[$id])) {
                                                                $practical_exam_mark = $mark[$id]['practical'] ?? '';
                                                                $class_exam_mark     = $mark[$id]['class']     ?? '';
                                                                $final_exam_mark     = $mark[$id]['final']     ?? '';
                                                                $is_not_taken        = ! empty($mark[$id]['not_taken']);
                                                                // Calculate total based on exam type
                                                                if ($exam_type === 'triple') {
                                                                        $total_obtained = (is_numeric($class_exam_mark) && is_numeric($final_exam_mark) && is_numeric($practical_exam_mark))
                                                                        ? ($class_exam_mark + $final_exam_mark + $practical_exam_mark)
                                                                        : '';
                                                                } else { // single
                                                                        $total_obtained = is_numeric($final_exam_mark) ? $final_exam_mark : '';
                                                                }

                                                                if ($is_not_taken) {
                                                                        $practical_exam_mark = '';
                                                                        $class_exam_mark     = '';
                                                                        $final_exam_mark     = '';
                                                                        $total_obtained      = '';
                                                                }
                                                        } elseif ($exam_type === 'single' && is_numeric($mark[$id])) {
                                                                $final_exam_mark = $mark[$id];
                                                                $total_obtained  = $final_exam_mark;
                                                        }
                                                }
						?>
						<tr>
							<td class="subject-cell">
    <strong><?php echo esc_html($subject['subname']); ?></strong><br>
    <small>(<?php echo esc_html($subject['subcode']); ?>)</small>
</td>
							<td><strong><?php echo esc_html($subject['total']); ?></strong></td>
							<?php if ($exam_type === 'triple') : ?>
								<td>
									<input class="practical-mark" type="number" min="0"
									name="<?php echo esc_attr($id); ?>[practical]"
									value="<?php echo esc_attr($practical_exam_mark); ?>"
									placeholder="<?php echo esc_attr($display_names['practical_mark_display_name']); ?>"
									<?php echo $is_not_taken ? 'disabled' : ''; ?>>
								</td>
							<?php endif; ?>
							
							<?php if (in_array($exam_type, ['composite', 'triple'], true)) : ?>
								<td>
									<input class="class-mark" type="number" min="0"
									name="<?php echo esc_attr($id); ?>[class]"
									value="<?php echo esc_attr($class_exam_mark); ?>"
									placeholder="<?php echo esc_attr($display_names['class_mark_display_name']); ?>"
									<?php echo $is_not_taken ? 'disabled' : ''; ?>>
								</td>
							<?php endif; ?>
							
							<td>
								<input class="final-mark" type="number" min="0"
								name="<?php echo esc_attr($id); ?>[final]"
								value="<?php echo esc_attr($final_exam_mark); ?>"
								placeholder="<?php echo esc_attr($display_names['final_mark_display_name']); ?>"
								<?php echo $is_not_taken ? 'disabled' : ''; ?>>
							</td>
							<td>                               
								<input class='total-mark form-control bg-light' type='number'
								value='<?php echo esc_attr($total_obtained); ?>' readonly<?php echo $is_not_taken ? ' disabled' : ''; ?> />
							</td>
							<td>
								<input type="checkbox" class="not-taken-checkbox" name="<?php echo esc_attr($id); ?>[not_taken]" value="1" <?php checked($is_not_taken); ?>>
							</td>
							<td>
								<input type="radio" name="fourth_subject" value="<?php echo esc_attr($id); ?>" <?php checked($current_fourth === $id); ?><?php echo $is_not_taken ? ' disabled' : ''; ?>>
							</td>
						</tr>
                        <?php
					} ?>
					<tr>
						<td><strong>Remark</strong></td>
						<td colspan="<?php echo $column_count - 1; ?>">
							<input type='text' name='remark' placeholder="Enter Remark" value="<?php echo isset($addstudent['remark']) ? esc_html($addstudent['remark']) : ''; ?>">
						</td>
					</tr>
					<tr>
						<td colspan="<?php echo $column_count; ?>">
							<div class="button-div">
								<input type="submit" name="input_submit" value="<?php echo esc_attr($btnLabel); ?>" class="btn btn-primary">
								<button class="btn btn-danger" type="button" onclick="window.history.back();">Cancel</button>
							</div>
						</td>
					</tr>
				</table>
			</form>
		</div><?php
		// Start processing Marks
                $this->processMarkSubmission($stuid, $subjects, $marks, $exam_type);
        }
	
	/**
	 * Process mark submission
	 *
	 * @param int   $stuid Student ID
	 * @param array $subjects List of subjects
	 * @param array $marks Existing marks
	 */
        public function processMarkSubmission($stuid, $subjects, $marks, $exam_type)
        {
                if (isset($_POST['input_submit'], $_POST['astgdrmsys_mark_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['astgdrmsys_mark_nonce'])), 'astgdrmsys_mark_action')) {
			// PROCESS AND SAVE CUSTOM FIELDS
			$sanitized_custom_fields = [];
			if (isset($_POST['custom_fields']) && is_array($_POST['custom_fields'])) {
				foreach ($_POST['custom_fields'] as $field_data) {
					// Only save the field if both name and value are not empty
					if (!empty($field_data['name']) && !empty($field_data['value'])) {
						$sanitized_custom_fields[] = [
							'name'  => sanitize_text_field(wp_unslash($field_data['name'])),
							'value' => sanitize_text_field(wp_unslash($field_data['value'])),
						];
					}
				}
			}

			// Convert the sanitized array back to JSON
			$custom_fields_json = wp_json_encode($sanitized_custom_fields);

			// Update the student_result table with the new custom fields JSON
			$this->wpdb->update(
				$this->prefix . 'astgdrmsys_student_result',
				['custom_fields' => $custom_fields_json], // Data to update
				['sid' => $stuid],                         // WHERE clause
				['%s'],                                    // Data format
				['%d']                                     // WHERE format
			);

			// initialize marks data
			$marksData = array();

                        foreach ($subjects as $subject) {
                                if (empty($subject['subcode'])) {
                                        continue;
                                }

                                $subcode = $subject['subcode'];
                                $input   = $_POST[$subcode] ?? [];

                                $is_not_taken   = isset($input['not_taken']) && (int) $input['not_taken'] === 1;
                                $practical_mark = isset($input['practical']) ? intval(wp_unslash($input['practical'])) : 0;
                                $class_mark     = isset($input['class']) ? intval(wp_unslash($input['class'])) : 0;
                                $final_mark     = isset($input['final']) ? intval(wp_unslash($input['final'])) : 0;

                                if ($is_not_taken) {
                                        $practical_mark = 0;
                                        $class_mark     = 0;
                                        $final_mark     = 0;
                                }

                                if ('triple' === $exam_type) {
                                        $total_mark = $practical_mark + $class_mark + $final_mark;

                                        $marksData[$subcode] = array(
                                                'practical' => $practical_mark,
                                                'class'     => $class_mark,
                                                'final'     => $final_mark,
                                                'total'     => $total_mark,
                                        );
                                } elseif ('composite' === $exam_type) {
                                        $total_mark = $this->sum_two_assessments($class_mark, $final_mark);

                                        $marksData[$subcode] = array(
                                                'first_assessment'  => $class_mark,
                                                'second_assessment' => $final_mark,
                                                'class'             => $class_mark,
                                                'final'             => $final_mark,
                                        );

                                        if (null !== $total_mark) {
                                                $marksData[$subcode]['total'] = $total_mark;
                                        }
                                } else {
                                        $marksData[$subcode] = array(
                                                'final' => $final_mark,
                                                'total' => $final_mark,
                                        );
                                }

                                if ($is_not_taken) {
									$marksData[$subcode]['not_taken'] = 1;
                                }
                        }

                        $remark          = isset($_POST['remark']) ? sanitize_text_field(wp_unslash($_POST['remark'])) : '';
                        $fourth_subject  = isset($_POST['fourth_subject']) ? sanitize_text_field(wp_unslash($_POST['fourth_subject'])) : '';

                        if ($fourth_subject !== '' && (! isset($marksData[$fourth_subject]['not_taken']) || empty($marksData[$fourth_subject]['not_taken']))) {
                                        $marksData['fourth_subject'] = $fourth_subject;
                        }

			$serializedMarks = serialize($marksData);

			try {
				$table_name = $this->wpdb->prefix . 'astgdrmsys_mark';

				if (empty($marks)) {
					$this->wpdb->insert(
						$table_name,
						array(
							'sid'    => $stuid,
							'marks'  => $serializedMarks,
							'remark' => $remark,
						),
						array('%d', '%s', '%s')
					);
				} else {
					$this->wpdb->update(
						$table_name,
						array(
							'marks'  => $serializedMarks,
							'remark' => $remark,
						),
						array('sid' => $stuid),
						array('%s', '%s'),
						array('%d')
					);
				}

				wp_safe_redirect(admin_url('admin.php?page=astgdrmsys-add-mark&insert=true'));
				exit;
			} catch (Exception $e) {
				do_action('astgdrmsys_log_error', 'Exception in mark insertion: ' . $e->getMessage());
				wp_die('An error occurred while saving marks.');
                        }
                }
        }

        /**
         * Retrieves subjects for the provided class and department combination.
         *
         * @param int $class_id      Class identifier.
         * @param int $department_id Department identifier.
         *
         * @return array<int, array<string, mixed>>
         */
        private function fetch_subjects_by_class_and_department($class_id, $department_id)
        {
                $subject_table = esc_sql($this->prefix . 'astgdrmsys_subject');

                $class_id      = absint($class_id);
                $department_id = absint($department_id);

                $subjects = $this->wpdb->get_results(
                        $this->wpdb->prepare(
                                "SELECT * FROM $subject_table WHERE `class` = %d AND `department` = %d",
                                $class_id,
                                $department_id
                        ),
                        ARRAY_A
                );

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

                return $subjects;
        }

        /**
         * Parses stored exam subject information into a normalized structure.
         *
         * @param mixed $raw_value Raw subject data from the exam record.
         *
         * @return array<int, array<string, mixed>>
         */
        private function parse_exam_subject_entries($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();
                }

                $subjects = array();

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

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

                        $id = isset($entry['id']) ? intval($entry['id']) : 0;

                        $code = '';
                        if (! empty($entry['code'])) {
                                $code = sanitize_text_field((string) $entry['code']);
                        } elseif (! empty($entry['subcode'])) {
                                $code = sanitize_text_field((string) $entry['subcode']);
                        } elseif ($id > 0) {
                                $code = (string) $id;
                        }

                        $name = '';
                        if (isset($entry['name'])) {
                                $name = sanitize_text_field((string) $entry['name']);
                        } elseif (isset($entry['subname'])) {
                                $name = sanitize_text_field((string) $entry['subname']);
                        }

                        $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']);
                        }

                        $subjects[] = array(
                                'id'      => $id,
                                'subcode' => $code,
                                'subname' => $name,
                                'total'   => $full_mark,
                        );
                }

                return $subjects;
        }

        /**
         * Builds the subject list for the add mark workflow, honoring exam-specific totals.
         *
         * @param array|null $exam_row      Exam record containing subject metadata.
         * @param int        $class_id      Student class identifier.
         * @param int        $department_id Student department identifier.
         *
         * @return array<int, array<string, mixed>>
         */
        private function get_exam_subjects_for_student($exam_row, $class_id, $department_id)
        {
                $subject_entries = array();
                $override_totals = array();

                if (is_array($exam_row) && ! empty($exam_row['subject_full_marks'])) {
                        $subject_entries = $this->parse_exam_subject_entries($exam_row['subject_full_marks']);
                }

                if (empty($subject_entries)) {
                        return $this->fetch_subjects_by_class_and_department($class_id, $department_id);
                }

                foreach ($subject_entries as $entry) {
                        $total = $entry['total'] ?? null;

                        if ($total === null || $total === '') {
                                continue;
                        }

                        $total_value = is_numeric($total) ? floatval($total) : $total;

                        $code = isset($entry['subcode']) ? trim((string) $entry['subcode']) : '';
                        if ($code !== '') {
                                $override_totals['code:' . $code] = $total_value;
                        }

                        $id = isset($entry['id']) ? (int) $entry['id'] : 0;

                        if ($id > 0) {
                                $override_totals['id:' . $id] = $total_value;
                        }
                }

                $fallback_subjects = $this->fetch_subjects_by_class_and_department($class_id, $department_id);

                if (empty($fallback_subjects)) {
                        return array_values(array_filter(
                                $subject_entries,
                                static function ($subject) {
                                        return isset($subject['subcode']) && $subject['subcode'] !== '';
                                }
                        ));
                }

                $fallback_by_code = array();
                $fallback_by_id   = array();

                foreach ($fallback_subjects as $subject) {
                        $code = isset($subject['subcode']) ? trim((string) $subject['subcode']) : '';

                        if ($code !== '') {
                                $fallback_by_code[$code] = $subject;
                        }

                        if (isset($subject['id'])) {
                                $fallback_by_id[(int) $subject['id']] = $subject;
                        }
                }

                foreach ($subject_entries as &$subject) {
                        $code = isset($subject['subcode']) ? trim((string) $subject['subcode']) : '';
                        $id   = isset($subject['id']) ? (int) $subject['id'] : 0;

                        if ($code === '' && $id > 0 && isset($fallback_by_id[$id]['subcode'])) {
                                $code               = trim((string) $fallback_by_id[$id]['subcode']);
                                $subject['subcode'] = $code;
                        }

                        $fallback = null;
                        if ($code !== '' && isset($fallback_by_code[$code])) {
                                $fallback = $fallback_by_code[$code];
                        } elseif ($id > 0 && isset($fallback_by_id[$id])) {
                                $fallback = $fallback_by_id[$id];
                        }

                        $fallback_total = null;
                        if ($fallback) {
                                if ((empty($subject['subname']) || ! is_string($subject['subname'])) && isset($fallback['subname'])) {
                                        $subject['subname'] = $fallback['subname'];
                                }

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

                        $code_key       = ($code !== '') ? 'code:' . $code : null;
                        $id_key         = ($id > 0) ? 'id:' . $id : null;
                        $has_override   = ($code_key && isset($override_totals[$code_key])) || ($id_key && isset($override_totals[$id_key]));
                        $override_total = null;

                        if ($code_key && isset($override_totals[$code_key])) {
                                $override_total = $override_totals[$code_key];
                        } elseif ($id_key && isset($override_totals[$id_key])) {
                                $override_total = $override_totals[$id_key];
                        }

                        if ($override_total !== null && $override_total !== '') {
                                $subject['total'] = $override_total;
                        } elseif ((! isset($subject['total']) || $subject['total'] === null || $subject['total'] === '') && $fallback_total !== null) {
                                $subject['total'] = $fallback_total;
                        } elseif ($fallback_total !== null && ! $has_override && is_numeric($subject['total']) && is_numeric($fallback_total) && floatval($subject['total']) <= 0) {
                                $subject['total'] = $fallback_total;
                        }

                        if (! isset($subject['total']) || $subject['total'] === null) {
                                $subject['total'] = '';
                        }
                }

                unset($subject);

                return array_values(array_filter(
                        $subject_entries,
                        static function ($subject) {
                                return isset($subject['subcode']) && $subject['subcode'] !== '';
                        }
                ));
        }

}

// Initialize the class
$ASTGDRMSYS_Marks_Manager = new ASTGDRMSYS_Marks_Manager();
