<?php

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

/**
 * This file is part of the RMS (Result Management System for Institutions) plugin.
 *
 * It ensures that the file is not accessed directly by checking if the ABSPATH constant is defined.
 * If ABSPATH is not defined, the script exits to prevent unauthorized access.
 */
if (!defined('ABSPATH')) {
    exit;
}

require_once ASTGDRMSYS_PLUGIN_DIR . 'includes/traits/class-astgdrmsys-exam-label-helper.php';

// Include WordPress database access
global $wpdb;

/**
 * Class ASTGDRMSYS_Insert_Result
 *
 * This class handles the inserting marks from a CSV file.
 *
 */
class ASTGDRMSYS_Insert_Result
{
    use ASTGDRMSYS_Exam_Label_Helper;
    private $wpdb;
    private $prefix;

    /**
     * Constructor for the class.
     *
     * @param wpdb $wpdb WordPress database access abstraction object.
     */
    public function __construct($wpdb)
    {
        $this->wpdb = $wpdb;
        $this->prefix = $wpdb->prefix;
    }

    /**
     * Display success messages based on query parameters.
     *
     * This function checks the URL query parameters to determine if a success message
     * should be displayed for either insertion or update operations. If the 'insert'
     * parameter is set to 'true', it displays a message indicating that marks were
     * inserted successfully. If the 'update' parameter is set to 'true', it displays
     * a message indicating that marks were updated successfully.
     *
     * @return void
     */
    function show_message(): void
    {
        // Check for insertion success
        if (isset($_GET['insert']) && $_GET['insert'] === 'true') {
            $this->display_message('Marks Inserted Successfully', 'success');
        }

        // Check for update success
        if (isset($_GET['update']) && $_GET['update'] === 'true') {
            $this->display_message('Marks Updated Successfully', 'success');
        }
    }

    /**
     * Displays the form for updating marks via CSV.
     *
     * @return void
     */
    function display_mark_update(): void
    {
        if (current_user_can('administrator')) {
            $this->astgdrmsys_display_insert_form();
        } else {
            ?>
            <div class="alert alert-danger display-mark-update-div">
                <h2>Unauthorized Access</h2>
                <p>Sorry, you are not authorized to access this page.</p>
            </div>
        <?php
        }
    }

    /**
     * Displays a message with a specified type.
     *
     * @param string $message The message to be displayed.
     * @param string $type The type of message, default is 'success'. Possible values are 'success', 'error', 'warning', etc.
     *
     * @return void
     */
    function display_message($message, $type = 'success'): void
    {
        $class = ($type === 'success') ? 'alert-success' : 'alert-danger';
        echo "
        <div id='message_div' class='alert {$class}' style='position: relative;'>
            <center>{$message}</center>
            <button class='close-message' onclick='this.parentElement.style.display=\"none\";'>&times;</button>
        </div>";
    }

    /**
     * Retrieve the exam type for a given exam ID.
     */
    private function get_exam_profile($exam_id): array
    {
        $exam_table = $this->prefix . 'astgdrmsys_exam_name';
        $exam = $this->wpdb->get_row(
            $this->wpdb->prepare(
                "SELECT exam_type, first_assessment_display_name, second_assessment_display_name, third_assessment_display_name, total_display_name FROM $exam_table WHERE id = %d",
                $exam_id
            ),
            ARRAY_A
        );

        if (!$exam) {
            $exam = [
                'exam_type' => 'single',
            ];
        }

        $exam['display_names'] = $this->resolve_exam_mark_labels($exam);

        return $exam;
    }

    private function get_exam_type($exam_id): string
    {
        $profile = $this->get_exam_profile($exam_id);
        $exam_type = $profile['exam_type'] ?? '';

        return !empty($exam_type) ? $exam_type : 'single';
    }

    /**
     * Normalize a composite subject mark so the first and second assessments are
     * aligned with the rendered columns.
     */
    private function normalize_composite_subject_mark($subject_mark): array
    {
        if (!is_array($subject_mark)) {
            $first_numeric = is_numeric($subject_mark) ? floatval($subject_mark) : null;

            $normalized = [
                '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 = [
            '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.
     */
    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;
    }

    /**
     * Insert or update marks for a student and subject combination.
     */
    private function update_student_mark_record(int $student_id, string $subject_code, $new_mark_for_subject): bool
    {
        $table_name = $this->prefix . 'astgdrmsys_mark';

        // Clear any previous DB error before processing this record to avoid stale errors causing false negatives.
        $this->wpdb->last_error = '';

        $marks_row = $this->wpdb->get_row(
            $this->wpdb->prepare(
                "SELECT * FROM `{$this->prefix}astgdrmsys_mark` WHERE sid = %d",
                $student_id
            ),
            ARRAY_A
        );

        $unserializedMarksData = [];
        if (!empty($marks_row['marks'])) {
            $unserialized = maybe_unserialize($marks_row['marks']);
            if (is_array($unserialized)) {
                $unserializedMarksData = $unserialized;
            }
        }

        $unserializedMarksData[$subject_code] = $new_mark_for_subject;
        $serializedMarksData = serialize($unserializedMarksData);

        if (empty($marks_row)) {
            $result = $this->wpdb->insert(
                $table_name,
                [
                    'sid'   => $student_id,
                    'marks' => $serializedMarksData
                ],
                ['%d', '%s']
            );
        } else {
            $result = $this->wpdb->update(
                $table_name,
                ['marks' => $serializedMarksData],
                ['sid' => $student_id],
                ['%s'],
                ['%d']
            );
        }

        if ($result === false || !empty($this->wpdb->last_error)) {
            $error_message = sprintf(
                'Failed to update marks for student %d subject %s: %s | Query: %s',
                $student_id,
                $subject_code,
                $this->wpdb->last_error,
                $this->wpdb->last_query
            );

            error_log($error_message);
            return false;
        }

        return true;
    }

    /**
     * Displays the form for updating marks via CSV.
     *
     * This function renders the form that allows users to upload a CSV file
     * for updating marks in the system.
     *
     * @return void
     */
    public function astgdrmsys_display_insert_form()
    {
        // Process submissions that require an immediate redirect before any HTML is sent.
        if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['submit_student_mark'])) {
            if ($this->handleStudentMark()) {
                return;
            }
        }

        if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['download_csv_template'])) {
            $this->handleDownloadTemplate();
            return;
        }

        $show_student_list_after_upload = false;
        if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['upload_marks_csv'])) {
            $show_student_list_after_upload = $this->handleUploadMarksCsv();
        }

        ?>
        <div class='container astgdrmsys-premium-wrap'>
            <div class="premium-page-header">
                <div class="page-header-text">
                    <h1>Insert Students Marks</h1>
                    <p>Select the appropriate Class, Exam Name & Subject and student records accordingly.</p>
                </div>
            </div><br>

            <form method='POST' action='' id="insertResultForm" class="premium-filter-form">
                <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
                        $sqlclass = "SELECT * FROM `{$this->prefix}astgdrmsys_class`";
                        $classes = $this->wpdb->get_results($sqlclass, ARRAY_A);
                        foreach ($classes as $class) {
                        ?>
                            <option value="<?php echo esc_html($class['id']); ?>"><?php echo esc_html($class['class']); ?></option>
                        <?php
                        }
                        ?>
                    </select>
                </div>
                
                <div class="filter-group">
                    <label>Select Department :</label>
                    <select class="selectDepartment premium-input" name="departmentsearch" required>
                        <option value="" selected disabled>Select Class First</option>
                    </select>
                </div>
                
                <div class="filter-group">
                    <label>Select Exam Name:</label>
                    <select class="selectExam premium-input" name="examsearch" required>
                        <option value="" selected disabled>Select Department First</option>
                    </select>
                </div>
                
                <div class="filter-group">
                    <label>Select Subject:</label>
                    <select class="selectSubjectCode premium-input" name="subjectcode" required>
                        <option value="" selected disabled>Select Subject</option>
                    </select>
                </div>
                
                <div class="filter-actions" style="margin-bottom: 5px;">
                    <input class="hero-btn primary-btn" style="border:none;" type="submit" name="submit-load-students" value="Load Students" id="insertSubmitBtn">
                </div>
            </form>
        </div>
        <?php
        // Handle form submissions
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            if (isset($_POST['submit-load-students']) || $show_student_list_after_upload) {
                $this->handleLoadStudents();
            }
        }
    }

    /**
     * Retrieves a list of students based on the provided class name, exam name, exam year, and optional filters.
     *
     * @param string      $classname     The name of the class.
     * @param string      $examname      The name of the exam.
     * @param int         $examyear      The year of the exam.
     * @param int|null    $department_id Optional department identifier to constrain the results.
     * @param string|null $subject_code  Optional subject code used to remove students who marked the subject as not taken.
     *
     * @return array An array containing the list of students.
     */
    private function getstudentlist($classname, $examname, $examyear, $department_id = null, $subject_code = null): array
    {
        $query = "SELECT sid, sno, regno, name, class, section, department, exam_name, exam_year FROM {$this->prefix}astgdrmsys_student_result WHERE class = %d AND exam_name = %d AND exam_year = %d";
        $params = [$classname, $examname, $examyear];

        if ($department_id !== null && $department_id !== 0) {
            $department_table = $this->prefix . 'astgdrmsys_department';
            $department_name = $this->wpdb->get_var(
                $this->wpdb->prepare("SELECT department FROM {$department_table} WHERE id = %d", $department_id)
            );

            if (!empty($department_name)) {
                $query .= ' AND (department = %s OR department = %d)';
                $params[] = $department_name;
                $params[] = (int) $department_id;
            } else {
                $query .= ' AND department = %d';
                $params[] = (int) $department_id;
            }
        }

        $prepared_query = $this->wpdb->prepare($query, $params);
        $students       = $this->wpdb->get_results($prepared_query, ARRAY_A);

        $subject_code = is_string($subject_code) ? trim($subject_code) : '';
        if ($subject_code === '' || empty($students)) {
            return $students;
        }

        $student_ids = [];
        foreach ($students as $student_row) {
            if (isset($student_row['sid'])) {
                $student_ids[] = (int) $student_row['sid'];
            }
        }
        $student_ids = array_filter($student_ids);
        if (empty($student_ids)) {
            return $students;
        }

        $marks_table   = $this->prefix . 'astgdrmsys_mark';
        $placeholders  = implode(',', array_fill(0, count($student_ids), '%d'));
        $marks_query   = $this->wpdb->prepare("SELECT sid, marks FROM {$marks_table} WHERE sid IN ($placeholders)", $student_ids);
        $marks_results = $this->wpdb->get_results($marks_query, ARRAY_A);

        if (empty($marks_results)) {
            return $students;
        }

        $marks_map = [];
        foreach ($marks_results as $row) {
            $marks_map[(int) $row['sid']] = $row['marks'];
        }

        $filtered_students = [];
        foreach ($students as $student) {
            $sid = isset($student['sid']) ? (int) $student['sid'] : 0;

            if ($sid === 0 || !isset($marks_map[$sid])) {
                $filtered_students[] = $student;
                continue;
            }

            $marks_data = maybe_unserialize($marks_map[$sid]);
            if (!is_array($marks_data)) {
                $filtered_students[] = $student;
                continue;
            }

            if (isset($marks_data[0]) && is_array($marks_data[0]) && isset($marks_data[0][$subject_code])) {
                $subject_marks = $marks_data[0][$subject_code];
            } else {
                $subject_marks = $marks_data[$subject_code] ?? null;
            }

            if (!is_array($subject_marks) || empty($subject_marks['not_taken'])) {
                $filtered_students[] = $student;
            }
        }

        return $filtered_students;
    }

    /**
     * Handles the loading of students.
     *
     * This function is responsible for loading student data.
     *
     * @return void
     */
    private function handleLoadStudents(): void
    {
        $classname = sanitize_text_field($_POST['classsearch']);
        $department_id = isset($_POST['departmentsearch']) ? absint($_POST['departmentsearch']) : 0;
        if ($department_id <= 0) {
            $this->display_message(__('Please select a department before loading students.'), 'error');
            return;
        }

        // store exam name and year in with comma
        $examnamewithyear = sanitize_text_field($_POST['examsearch']);
        // split exam name and year
        list($examname, $examyear) = explode(',', $examnamewithyear);
        // store subject id and subject code with comma
        $subject_id_with_subcode = sanitize_text_field($_POST['subjectsearch']);
        // split subject id and subject code
        list($subject_id, $subject_code) = explode(',', $subject_id_with_subcode);

        $exam_profile = $this->get_exam_profile((int) $examname);
        $exam_type = $exam_profile['exam_type'] ?? 'single';

        // Fetch students based on class and exam
        $studentList = $this->getstudentlist($classname, $examname, $examyear, $department_id, $subject_code);
        $display_names = $exam_profile['display_names'];
        ?>
        <div class="student-list m-4 px-1">
            <div class="csv-section border rounded p-3 bg-light">
                <div class="csv-instructions mb-3">
                    <h5 class="mb-2">CSV Upload / Download</h5>
                    <p class="text-muted mb-0">
                        Use this section to <strong>download</strong> a marks template for your exam, 
                        fill it with student marks, and then <strong>upload</strong> it back once complete. 
                        Please make sure the uploaded file matches the downloaded format exactly.
                    </p>
                </div>
                
                <div class="d-flex flex-wrap align-items-center gap-3">
                    <!-- Download CSV Template -->
                    <div class="download-template p-2 border rounded bg-white flex-grow-1 text-center">
                        <h6 class="text-success mb-2">Download Template</h6>
                        <p class="small text-muted mb-2">
                            Download a blank CSV template for this class and exam.
                        </p>
                        <form action="" method="post">
                            <input type="hidden" name="classname" value="<?php echo esc_attr($classname); ?>">
                            <input type="hidden" name="department_id" value="<?php echo esc_attr($department_id); ?>">
                            <input type="hidden" name="examname" value="<?php echo esc_attr($examname); ?>">
                            <input type="hidden" name="examyear" value="<?php echo esc_attr($examyear); ?>">
                            <input type="hidden" name="subject_code" value="<?php echo esc_attr($subject_code); ?>">
                            <input type="hidden" name="subject_id" value="<?php echo esc_attr($subject_id); ?>">
                            <button type="submit" name="download_csv_template" class="btn btn-success">
                                <i class="dashicons dashicons-download"></i> Download CSV Template
                            </button>
                        </form>
                    </div>
            
                    <!-- Upload Filled CSV -->
                    <div class="upload-template p-2 border rounded bg-white flex-grow-1 text-center">
                        <h6 class="text-secondary mb-2">Upload Completed CSV</h6>
                        <p class="small text-muted mb-2">
                            Once you’ve filled the marks in the template, upload the completed CSV file here.
                        </p>
                        <form action="" method="post" enctype="multipart/form-data" class="d-inline-flex flex-column align-items-center gap-2">
                            <input type="hidden" name="classname" value="<?php echo esc_attr($classname); ?>">
                            <input type="hidden" name="department_id" value="<?php echo esc_attr($department_id); ?>">
                            <input type="hidden" name="examname" value="<?php echo esc_attr($examname); ?>">
                            <input type="hidden" name="examyear" value="<?php echo esc_attr($examyear); ?>">
                            <input type="hidden" name="subject_code" value="<?php echo esc_attr($subject_code); ?>">
                            <input type="hidden" name="subject_id" value="<?php echo esc_attr($subject_id); ?>">
                            <input type="file" name="marks_csv" accept=".csv" class="form-control-file" required>
                            <button type="submit" name="upload_marks_csv" class="btn btn-secondary">
                                <i class="dashicons dashicons-upload"></i> Upload Marks from CSV
                            </button>
                        </form>
                    </div>
                </div>
            </div>


            <form action="" method="post">
                <input type="hidden" name="classname" value="<?php echo esc_attr($classname); ?>">
                <input type="hidden" name="department_id" value="<?php echo esc_attr($department_id); ?>">
                <input type="hidden" name="examname" value="<?php echo esc_attr($examname); ?>">
                <input type="hidden" name="examyear" value="<?php echo esc_attr($examyear); ?>">
                <input type="hidden" name="subject_code" value="<?php echo esc_attr($subject_code); ?>">
                <input type="hidden" name="subject_id" value="<?php echo esc_attr($subject_id); ?>">
            <div class="premium-page-header" style="margin-top:20px;">
                <div class="page-header-text">
                    <h1>Student List for Subject Code: <?php echo esc_html($subject_code); ?></h1>
                </div>
            </div>
            <div class="astgdrmsys-premium-wrap" style="padding-bottom:0; min-height:auto;">
                <table class="styled-table" id="student_list_table">
                    <thead class="thead-dark">
                        <tr>
                            <td><?php echo esc_html($display_names['roll_display_name']); ?></td>
                            <td><?php echo esc_html($display_names['regno_display_name']); ?></td>
                            <td>Name</td>
                            <?php if ($exam_type === 'triple') : ?>
                                <td><?php echo esc_html($display_names['practical_mark_display_name']); ?></td>
                                <td><?php echo esc_html($display_names['class_mark_display_name']); ?></td>
                                <td><?php echo esc_html($display_names['final_mark_display_name']); ?></td>
                                <td><?php echo esc_html($display_names['total_mark_display_name']); ?></td>
                            <?php elseif ($exam_type === 'composite') : ?>
                                <td><?php echo esc_html($display_names['class_mark_display_name']); ?></td>
                                <td><?php echo esc_html($display_names['final_mark_display_name']); ?></td>
                                <td><?php echo esc_html($display_names['total_mark_display_name']); ?></td>
                            <?php else : ?>
                                <td><?php echo esc_html($display_names['single_mark_display_name']); ?></td>
                            <?php endif; ?>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($studentList as $student) {
                            // This query is correct for your database design.
                            $marks_row = $this->wpdb->get_row(
                                $this->wpdb->prepare(
                                    "SELECT * FROM `{$this->prefix}astgdrmsys_mark` WHERE sid = %d",
                                    $student['sid']
                                ),
                                ARRAY_A
                            );

                            // Initialize all variables to 0 to handle cases where no marks exist yet.
                            $practical_mark = $class_mark = $final_mark = $total_mark = $subject_mark = 0;

                            // Check if a row was found and if the 'marks' column is not empty.
                            if (!empty($marks_row) && !empty($marks_row['marks'])) {
                                $marks_value = $marks_row['marks'];
                                $studentmark_array = maybe_unserialize($marks_value);

                                // If the stored value isn't serialized, attempt a JSON decode and fall back to an empty array.
                                if (!is_array($studentmark_array)) {
                                    $json_marks = json_decode($marks_value, true);
                                    $studentmark_array = is_array($json_marks) ? $json_marks : [];
                                }
                                // print_r($studentmark_array);

                                // Make sure the unserialized data is an array before proceeding.
                                if (is_array($studentmark_array)) {
                                    // Check if the marks for the specific subject exist in the array
                                    if (isset($studentmark_array[$subject_code])) {
                                        $mark_data = $studentmark_array[$subject_code];

                                        if ($exam_type === 'triple') {
                                            $practical_mark = $mark_data['practical'] ?? 0;
                                            $class_mark = $mark_data['class'] ?? 0;
                                            $final_mark = $mark_data['final'] ?? 0;
                                            $total_mark = $practical_mark + $class_mark + $final_mark;
                                        } elseif ($exam_type === 'composite') {
                                            $normalized_mark = $this->normalize_composite_subject_mark($mark_data);
                                            $class_mark = is_numeric($normalized_mark['first_assessment'] ?? null) ? floatval($normalized_mark['first_assessment']) : 0;
                                            $final_mark = is_numeric($normalized_mark['second_assessment'] ?? null) ? floatval($normalized_mark['second_assessment']) : 0;
                                            $total_mark = $normalized_mark['total'] ?? $this->sum_two_assessments($class_mark, $final_mark);
                                        } else {
                                            // For 'single' type, the value might not be in an array.
                                            if (is_array($mark_data)) {
                                                // Case 1: The mark is stored as an array, e.g., ['final' => 8]
                                                $subject_mark = $mark_data['final'] ?? 0;
                                            } else {
                                                // Case 2: The mark is stored as a direct value, e.g., 8
                                                $subject_mark = $mark_data;
                                            }
                                        }
                                    }
                                }
                            }
                        ?>
                            <tr>
                                <td><?php echo esc_html($student['sno']); ?></td>
                                <td><?php echo esc_html($student['regno']); ?></td>
                                <td><?php echo esc_html($student['name']); ?></td>
                                <?php // This rendering logic MUST match the processing logic above.
                                if ($exam_type === 'triple') : ?>
                                    <td><input class="practical-mark" type="number" name="practical_mark[<?php echo esc_attr($student['sid']); ?>]" value="<?php echo esc_attr($practical_mark); ?>"></td>
                                    <td><input class="class-mark" type="number" name="class_mark[<?php echo esc_attr($student['sid']); ?>]" value="<?php echo esc_attr($class_mark); ?>"></td>
                                    <td><input class="final-mark" type="number" name="final_mark[<?php echo esc_attr($student['sid']); ?>]" value="<?php echo esc_attr($final_mark); ?>"></td>
                                    <td><input class='total-mark form-control bg-light' type='number' value='<?php echo esc_attr($total_mark); ?>' readonly /></td>
                                <?php elseif ($exam_type === 'composite') : ?>
                                    <td><input class="class-mark" type="number" name="class_mark[<?php echo esc_attr($student['sid']); ?>]" value="<?php echo esc_attr($class_mark); ?>"></td>
                                    <td><input class="final-mark" type="number" name="final_mark[<?php echo esc_attr($student['sid']); ?>]" value="<?php echo esc_attr($final_mark); ?>"></td>
                                    <td><input class='total-mark form-control bg-light' type='number' value='<?php echo esc_attr($total_mark); ?>' readonly /></td>
                                <?php else : ?>
                                    <td><input type="number" name="subject_mark[<?php echo esc_attr($student['sid']); ?>]" value="<?php echo esc_attr($subject_mark); ?>"></td>
                                <?php endif; ?>
                            </tr>
                        <?php } ?>
                    </tbody>
                </table>
                <div class="submit-button-div">
                    <input type='submit' name='submit_student_mark' value='Save Mark' class='btn btn-primary'>
                </div>
            </form>
        </div><?php
    }
    
    /**
     * Handle the processing of posted student marks.
     *
     * Validates the request, normalizes the posted marks based on the exam type,
     * updates each student's record, and redirects on success. Returns false and
     * displays an error message when validation fails or any database write
     * error occurs.
     */
    private function handleStudentMark(): bool
    {
        $classname = sanitize_text_field($_POST['classname']);
        $examname = sanitize_text_field($_POST['examname']);
        $examyear = sanitize_text_field($_POST['examyear']);
        $department_id = isset($_POST['department_id']) ? absint($_POST['department_id']) : 0;

        if ($department_id <= 0) {
            $this->display_message(__('Missing department information for this request.'), 'error');
            return false;
        }
        $subcode = sanitize_text_field($_POST['subject_code']);

        // Fetch students based on class and exam
        $studentList = $this->getstudentlist($classname, $examname, $examyear, $department_id, $subcode);

        if (empty($studentList)) {
            $this->display_message(__('No students were found for this request. Please reload the list and try again.'), 'error');
            return false;
        }

        // We need to know the exam type to process the correct POST variables
        $exam_type = $this->get_exam_type($examname);

        $has_error = false;
        $error_details = [];

        foreach ($studentList as $student) {
            $student_id = $student['sid'];

            // NEW DYNAMIC MARK PROCESSING LOGIC
            $new_mark_for_subject = [];

            if ($exam_type === 'triple') {
                // Get all three marks for this student from their respective POST arrays
                $practical_mark = isset($_POST['practical_mark'][$student_id]) ? intval($_POST['practical_mark'][$student_id]) : 0;
                $class_mark     = isset($_POST['class_mark'][$student_id]) ? intval($_POST['class_mark'][$student_id]) : 0;
                $final_mark     = isset($_POST['final_mark'][$student_id]) ? intval($_POST['final_mark'][$student_id]) : 0;

                // Build the array for this subject
                $new_mark_for_subject = [
                    'practical' => $practical_mark,
                    'class'     => $class_mark,
                    'final'     => $final_mark,
                ];
            } elseif ($exam_type === 'composite') {
                // Get the two marks for this student
                $class_mark = isset($_POST['class_mark'][$student_id]) ? intval($_POST['class_mark'][$student_id]) : 0;
                $final_mark = isset($_POST['final_mark'][$student_id]) ? intval($_POST['final_mark'][$student_id]) : 0;

                $total = $this->sum_two_assessments($class_mark, $final_mark);

                // Build the array for this subject
                $new_mark_for_subject = [
                    'first_assessment'  => $class_mark,
                    'second_assessment' => $final_mark,
                    'class'             => $class_mark,
                    'final'             => $final_mark,
                ];

                if (null !== $total) {
                    $new_mark_for_subject['total'] = $total;
                }
            } else { // 'single'
                // Get single mark for this student
                $subject_mark = isset($_POST['subject_mark'][$student_id]) ? intval($_POST['subject_mark'][$student_id]) : 0;

                // store the numeric value
                $new_mark_for_subject = $subject_mark;
            }

            if (!$this->update_student_mark_record($student_id, $subcode, $new_mark_for_subject)) {
                $has_error = true;
                $error_details[] = sprintf(__('Student ID %d failed to save'), $student_id);
            }
        } // End of foreach loop

        if ($has_error) {
            $error_text = __('Failed to update one or more student marks. Please try again.');

            if (!empty($this->wpdb->last_error)) {
                $error_text .= ' ' . sprintf(__('Database error: %s'), esc_html($this->wpdb->last_error));
            } elseif (!empty($error_details)) {
                $error_text .= ' ' . esc_html(implode(', ', $error_details));
            }

            $this->display_message($error_text, 'error');
            return false;
        }
        // safely redirect with success message
        wp_redirect(admin_url('admin.php?page=astgdrmsys-add-mark&mark_tab=insert_result&update=true'));
        exit;
    }
    
    /**
     * Handle downloading of the CSV template for mark entry.
     */
    private function handleDownloadTemplate(): void
    {
        $classname = sanitize_text_field($_POST['classname'] ?? '');
        $department_id = isset($_POST['department_id']) ? absint($_POST['department_id']) : 0;
        $examname = sanitize_text_field($_POST['examname'] ?? '');
        $examyear = sanitize_text_field($_POST['examyear'] ?? '');
        $subject_code = sanitize_text_field($_POST['subject_code'] ?? '');

        if ($classname === '' || $department_id <= 0 || $examname === '' || $examyear === '' || $subject_code === '') {
            $this->display_message(__('Unable to generate CSV template due to missing information.'), 'error');
            return;
        }

        $exam_profile = $this->get_exam_profile((int) $examname);
        $exam_type = $exam_profile['exam_type'] ?? 'single';
        $studentList = $this->getstudentlist($classname, $examname, $examyear, $department_id, $subject_code);

        if (headers_sent()) {
            $this->display_message(__('Cannot download CSV because headers have already been sent.'), 'error');
            return;
        }

        // Ensure nothing has been sent before emitting headers for the CSV download.
        if (ob_get_level() > 0) {
            while (ob_get_level() > 0) {
                ob_end_clean();
            }
        }

        $filename = sprintf('marks-template-%s-%s-%s.csv', $subject_code, $examname, $examyear);

        nocache_headers();
        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename=' . $filename);

        $output = fopen('php://output', 'w');
        if ($output === false) {
            $this->display_message(__('Failed to create CSV output.'), 'error');
            return;
        }

        $labels = $exam_profile['display_names'];
        
        // Base CSV columns
        $headers = [
            'Student ID',
            $labels['roll_display_name'],
            $labels['regno_display_name'],
            'Student Name'
        ];
        
        // Exam-type specific columns
        if ($exam_type === 'triple') {
            $headers = array_merge($headers, [
                $labels['practical_mark_display_name'],
                $labels['class_mark_display_name'],
                $labels['final_mark_display_name']
            ]);
        } elseif ($exam_type === 'composite') {
            $headers = array_merge($headers, [
                $labels['class_mark_display_name'],
                $labels['final_mark_display_name']
            ]);
        } else {
            $headers[] = $labels['single_mark_display_name'];
        }
        
        // Output the CSV headers
        fputcsv($output, $headers);


        foreach ($studentList as $student) {
            $row = [
                $student['sid'],
                $student['sno'],
                $student['regno'],
                $student['name'],
            ];

            // Fetch current marks to prefill the template
            $marks_row = $this->wpdb->get_row(
                $this->wpdb->prepare(
                    "SELECT marks FROM `{$this->prefix}astgdrmsys_mark` WHERE sid = %d",
                    $student['sid']
                ),
                ARRAY_A
            );

            $existing_mark = 0;
            if (!empty($marks_row['marks'])) {
                $marks_data = maybe_unserialize($marks_row['marks']);
                if (is_array($marks_data) && isset($marks_data[$subject_code])) {
                    $existing_mark = $marks_data[$subject_code];
                }
            }

            if ($exam_type === 'triple') {
                $row[] = is_array($existing_mark) ? intval($existing_mark['practical'] ?? 0) : 0;
                $row[] = is_array($existing_mark) ? intval($existing_mark['class'] ?? 0) : 0;
                $row[] = is_array($existing_mark) ? intval($existing_mark['final'] ?? 0) : 0;
            } elseif ($exam_type === 'composite') {
                $normalized_mark = $this->normalize_composite_subject_mark($existing_mark);
                $row[] = intval(is_array($normalized_mark) ? ($normalized_mark['first_assessment'] ?? 0) : 0);
                $row[] = intval(is_array($normalized_mark) ? ($normalized_mark['second_assessment'] ?? 0) : 0);
            } else {
                if (is_array($existing_mark)) {
                    $row[] = intval($existing_mark['final'] ?? 0);
                } else {
                    $row[] = intval($existing_mark);
                }
            }

            fputcsv($output, $row);
        }

        fclose($output);
        exit;
    }
    
    /**
     * Handle uploading marks from a CSV file.
     *
     * @return bool Whether the student list should be displayed again after processing.
    */
    private function handleUploadMarksCsv(): bool
    {
        $classname     = sanitize_text_field($_POST['classname'] ?? '');
        $department_id = isset($_POST['department_id']) ? absint($_POST['department_id']) : 0;
        $examname      = sanitize_text_field($_POST['examname'] ?? '');
        $examyear      = sanitize_text_field($_POST['examyear'] ?? '');
        $subject_code  = sanitize_text_field($_POST['subject_code'] ?? '');
    
        if ($classname === '' || $department_id <= 0 || $examname === '' || $examyear === '' || $subject_code === '') {
            $this->display_message(__('Unable to process CSV due to missing information.'), 'error');
            return true;
        }
    
        if (empty($_FILES['marks_csv']) || $_FILES['marks_csv']['error'] !== UPLOAD_ERR_OK) {
            $this->display_message(__('Please upload a valid CSV file.'), 'error');
            return true;
        }
    
        $file_tmp = $_FILES['marks_csv']['tmp_name'];
        $handle   = fopen($file_tmp, 'r');
        if ($handle === false) {
            $this->display_message(__('Unable to open the uploaded CSV file.'), 'error');
            return true;
        }
    
        $exam_profile = $this->get_exam_profile((int) $examname);
        $exam_type = $exam_profile['exam_type'] ?? 'single';
        $labels = $exam_profile['display_names'];
    
        // ✅ Build expected headers dynamically (exactly like CSV download)
        $expected_headers = [
            'Student ID',
            $labels['roll_display_name'],
            $labels['regno_display_name'],
            'Student Name',
        ];
    
        if ($exam_type === 'triple') {
            $expected_headers = array_merge($expected_headers, [
                $labels['practical_mark_display_name'],
                $labels['class_mark_display_name'],
                $labels['final_mark_display_name'],
            ]);
        } elseif ($exam_type === 'composite') {
            $expected_headers = array_merge($expected_headers, [
                $labels['class_mark_display_name'],
                $labels['final_mark_display_name'],
            ]);
        } else {
            $expected_headers[] = $labels['single_mark_display_name'];
        }
    
        // ✅ Read first row from uploaded CSV
        $headers = fgetcsv($handle);
        if ($headers === false) {
            fclose($handle);
            $this->display_message(__('The uploaded CSV file is empty.'), 'error');
            return true;
        }
    
        // Trim whitespace for safety
        $headers = array_map('trim', $headers);
    
        // ✅ Validate headers match
        if ($headers !== $expected_headers) {
            fclose($handle);
            $this->display_message(__('The CSV headers do not match the expected format. Please use the provided CSV template.'), 'error');
            return true;
        }
    
        // ✅ Build student list map
        $studentList = $this->getstudentlist($classname, $examname, $examyear, $department_id, $subject_code);
        $valid_students = [];
        foreach ($studentList as $student) {
            $valid_students[(int) $student['sid']] = $student;
        }
    
        if (empty($valid_students)) {
            fclose($handle);
            $this->display_message(__('No students found for the selected filters.'), 'error');
            return true;
        }
    
        // ✅ Read each CSV row
        while (($row = fgetcsv($handle)) !== false) {
            if (empty(array_filter($row, static fn($v) => $v !== null && $v !== ''))) {
                continue; // skip empty lines
            }
    
            $row_data = array_combine($headers, $row);
            if ($row_data === false) {
                continue;
            }
    
            $student_id = intval($row_data['Student ID'] ?? 0);
            if (!isset($valid_students[$student_id])) {
                continue;
            }
    
            // ✅ Match display-name keys to internal logic
            if ($exam_type === 'triple') {
                $new_mark_for_subject = [
                    'practical' => intval($row_data[$labels['practical_mark_display_name']] ?? 0),
                    'class'     => intval($row_data[$labels['class_mark_display_name']] ?? 0),
                    'final'     => intval($row_data[$labels['final_mark_display_name']] ?? 0),
                ];
            } elseif ($exam_type === 'composite') {
                $first_assessment  = intval($row_data[$labels['class_mark_display_name']] ?? 0);
                $second_assessment = intval($row_data[$labels['final_mark_display_name']] ?? 0);
                $total = $this->sum_two_assessments($first_assessment, $second_assessment);

                $new_mark_for_subject = [
                    'first_assessment'  => $first_assessment,
                    'second_assessment' => $second_assessment,
                    'class'             => $first_assessment,
                    'final'             => $second_assessment,
                ];

                if (null !== $total) {
                    $new_mark_for_subject['total'] = $total;
                }
            } else {
                $new_mark_for_subject = intval($row_data[$labels['single_mark_display_name']] ?? 0);
            }
    
            $this->update_student_mark_record($student_id, $subject_code, $new_mark_for_subject);
        }
    
        fclose($handle);
    
        wp_safe_redirect(admin_url('admin.php?page=astgdrmsys-add-mark&mark_tab=insert_result&update=true'));
        exit;
    }
    
}
/**
 * Creates a new instance of the ASTGDRMSYS_Insert_Result class and displays the form.
 *
 * This code initializes the ASTGDRMSYS_Insert_Result class with the global $wpdb object
 * and calls the astgdrmsys_display_insert_form method to render the form for inserting results.
 *
 * @param wpdb $wpdb The WordPress database access abstraction object.
 */
$insertResult = new ASTGDRMSYS_Insert_Result($wpdb);
$insertResult->show_message();
// Display the form (access control is handled inside the method)
$insertResult->display_mark_update();
?>
