<?php
// submit_exam.php
// Handles MCQ and Theory submissions separately for mixed exams.
// - MCQ: auto-grade, save mcq_score, compute total only if theory_score already present.
// - Theory: save answers, set status pending; DO NOT overwrite mcq_score.
// - Prevent reattempts per part (mcq/theory).

error_reporting(E_ALL);
ini_set('display_errors', 1);

chdir('../');
session_start();
require_once('public/connection.php');

if (!isset($_SESSION['student_id']) || !isset($_POST['exam_id'])) {
    header("Location: ../index.php");
    exit;
}

$student_id   = (int) $_SESSION['student_id'];
$exam_id      = (int) $_POST['exam_id'];
$answers      = $_POST['answers'] ?? []; // answers[qid] => value
$postedQtype  = isset($_POST['Qtype']) ? strtolower(trim($_POST['Qtype'])) : '';
$submitted_at = date("Y-m-d H:i:s");
$started_at   = $_SESSION['exam_started_at'] ?? null;

if ($exam_id <= 0) {
    $_SESSION['msg'] = "Invalid exam selected.";
    $_SESSION['type'] = "danger";
    header("Location: dashboard.php");
    exit;
}

/**
 * Helper: safely bind parameters to a mysqli_stmt
 * - Builds a types string automatically (i = int, d = double, s = string)
 * - Treats NULL as string 's' (mysqli accepts NULLs bound as parameters)
 * - Converts numeric strings to numeric types for accurate binding
 *
 * Usage:
 *    bind_params_safe($stmt, [ $a, $b, $c ]);
 */
function bind_params_safe(mysqli_stmt $stmt, array $values) {
    // Build type string and normalize values
    $types = '';
    $normalized = [];

    foreach ($values as $v) {
        // Nulls -> treat as string (mysqli accepts nulls)
        if ($v === null) {
            $types .= 's';
            $normalized[] = null;
            continue;
        }

        // Integers
        if (is_int($v)) {
            $types .= 'i';
            $normalized[] = $v;
            continue;
        }

        // Floats
        if (is_float($v)) {
            $types .= 'd';
            $normalized[] = $v;
            continue;
        }

        // Numeric strings -> prefer numeric binding (d if contains decimal, i if integer-like)
        if (is_string($v) && is_numeric($v)) {
            // Detect integer-like (no decimal point or exponent)
            if (ctype_digit($v)) {
                $types .= 'i';
                $normalized[] = (int)$v;
            } else {
                $types .= 'd';
                $normalized[] = (float)$v;
            }
            continue;
        }

        // Everything else -> string
        $types .= 's';
        $normalized[] = (string)$v;
    }

    // Prepare arguments for bind_param: first types string, then values by reference
    $bindParams = [];
    $bindParams[] = $types;
    foreach ($normalized as $k => $nv) {
        // must pass variables by reference
        $bindParams[] = &$normalized[$k];
    }

    // call_user_func_array requires the parameters as an array of references
    return call_user_func_array([$stmt, 'bind_param'], $bindParams);
}

// Load exam record
$examStmt = $conn->prepare("SELECT * FROM exams WHERE id = ?");
$examStmt->bind_param("i", $exam_id);
$examStmt->execute();
$exam = $examStmt->get_result()->fetch_assoc();
$examStmt->close();

if (!$exam) {
    $_SESSION['msg'] = "Exam not found.";
    $_SESSION['type'] = "danger";
    header("Location: dashboard.php");
    exit;
}

$exam_group_id   = (int)($exam['exam_group_id'] ?? 0);
$exam_type_label = strtolower(trim($exam['exam_type'] ?? 'exam'));
$class           = $conn->real_escape_string($exam['class'] ?? '');
$term            = $conn->real_escape_string($exam['term'] ?? '');
$session_yr      = $conn->real_escape_string($exam['session'] ?? '');
$subject_id      = (int)($exam['subject_id'] ?? 0);

// convert subject_id to text (exam_group_results stores subject text)
$subject = '';
if ($subject_id > 0) {
    $sStmt = $conn->prepare("SELECT subjects FROM school_subjects WHERE id = ?");
    bind_params_safe($sStmt, [$subject_id]);
    $sStmt->execute();
    $subRow = $sStmt->get_result()->fetch_assoc();
    $subject = $subRow ? $subRow['subjects'] : '';
    $sStmt->close();
}

// Fetch questions (we need types/answers for MCQ grading and to store theory)
$qStmt = $conn->prepare("SELECT id, question_type, correct_answer, marks FROM questions WHERE exam_id = ?");
bind_params_safe($qStmt, [$exam_id]);
$qStmt->execute();
$questions = $qStmt->get_result()->fetch_all(MYSQLI_ASSOC);
$qStmt->close();

if (!$questions) {
    $_SESSION['msg'] = "No questions available for this exam.";
    $_SESSION['type'] = "warning";
    header("Location: dashboard.php");
    exit;
}

// If front-end sent Qtype, respect it. Otherwise we try to infer:
$qtype_in_request = $postedQtype;
if (empty($qtype_in_request)) {
    // infer from submitted answers: if answers provided for mcq question ids treat as mcq, etc.
    $has_mcq_answer = false;
    $has_theory_answer = false;
    foreach ($questions as $q) {
        $qid = (int)$q['id'];
        if (isset($answers[$qid]) && $answers[$qid] !== '') {
            if (strtolower($q['question_type']) === 'mcq') $has_mcq_answer = true;
            if (strtolower($q['question_type']) === 'theory') $has_theory_answer = true;
        }
    }
    if ($has_mcq_answer && !$has_theory_answer) $qtype_in_request = 'mcq';
    elseif ($has_theory_answer && !$has_mcq_answer) $qtype_in_request = 'theory';
    elseif ($has_mcq_answer && $has_theory_answer) $qtype_in_request = 'mixed';
    else {
        $_SESSION['msg'] = "No answers detected.";
        $_SESSION['type'] = "warning";
        header("Location: dashboard.php");
        exit;
    }
}

// For this implementation we require qtype to be either 'mcq' or 'theory'.
// If 'mixed' appears (both submitted at once), we will process both but prefer that UI submit each separately.
$submitting_mcq = ($qtype_in_request === 'mcq' || $qtype_in_request === 'mixed');
$submitting_theory = ($qtype_in_request === 'theory' || $qtype_in_request === 'mixed');

// Calculate MCQ score only if MCQ part is being submitted now
$mcq_score = 0.0;
$theory_answers = []; // qid => text

if ($submitting_mcq) {
    foreach ($questions as $q) {
        if (strtolower($q['question_type']) !== 'mcq') continue;
        $qid = (int)$q['id'];
        $submitted = null;
        if (isset($answers[$qid])) $submitted = trim($answers[$qid]);
        elseif (isset($answers[(string)$qid])) $submitted = trim($answers[(string)$qid]);
        if ($submitted === null || $submitted === '') continue;
        $correct = strtolower(trim($q['correct_answer'] ?? ''));
        if (strtolower($submitted) === $correct) {
            $mcq_score += (float)($q['marks'] ?? 0);
        }
    }
    $mcq_score = round($mcq_score, 2);
}

if ($submitting_theory) {
    foreach ($questions as $q) {
        if (strtolower($q['question_type']) !== 'theory') continue;
        $qid = (int)$q['id'];
        $submitted = '';
        if (isset($answers[$qid])) $submitted = trim($answers[$qid]);
        elseif (isset($answers[(string)$qid])) $submitted = trim($answers[(string)$qid]);
        $theory_answers[$qid] = $submitted;
    }
}

/* -------------------------
 Prevent reattempts for the same part
--------------------------*/
$checkAttempt = $conn->prepare("SELECT COUNT(*) AS cnt FROM student_exam_attempts WHERE exam_id = ? AND student_id = ? AND qtype = ?");
if ($submitting_mcq) {
    $q = 'mcq';
    bind_params_safe($checkAttempt, [$exam_id, $student_id, $q]);
    $checkAttempt->execute();
    $cnt = $checkAttempt->get_result()->fetch_assoc()['cnt'] ?? 0;
    if ($cnt > 0) {
        $_SESSION['msg'] = "You have already attempted the objective (MCQ) part of this exam. Re-attempt is not allowed.";
        $_SESSION['type'] = "warning";
        header("Location: dashboard.php");
        exit;
    }
}
if ($submitting_theory) {
    $q = 'theory';
    bind_params_safe($checkAttempt, [$exam_id, $student_id, $q]);
    $checkAttempt->execute();
    $cnt = $checkAttempt->get_result()->fetch_assoc()['cnt'] ?? 0;
    if ($cnt > 0) {
        $_SESSION['msg'] = "You have already submitted the theory part of this exam. Re-attempt is not allowed.";
        $_SESSION['type'] = "warning";
        header("Location: dashboard.php");
        exit;
    }
}
$checkAttempt->close();

/* -------------------------
 Start DB transaction
--------------------------*/
$conn->begin_transaction();

try {
    // Lock existing exam_group_results row for this student+group
    $findStmt = $conn->prepare("SELECT * FROM exam_group_results WHERE student_id = ? AND exam_group_id = ? FOR UPDATE");
    bind_params_safe($findStmt, [$student_id, $exam_group_id]);
    $findStmt->execute();
    $existing = $findStmt->get_result()->fetch_assoc();
    $findStmt->close();

    $existing_id = $existing['id'] ?? null;
    $existing_mcq = isset($existing['mcq_score']) ? $existing['mcq_score'] : null;
    $existing_theory = isset($existing['theory_score']) ? $existing['theory_score'] : null;

    /* -------------------------
       PROCESS MCQ (if submitted)
       ------------------------- */
    if ($submitting_mcq) {
        // If exam_group_results exists, update only mcq_score (and total if theory_score exists and is numeric).
        if ($existing_id) {
            $new_total = null;
            $new_status = 'graded';

            // If theory has already been graded (teacher set theory_score) compute total
            if ($existing_theory !== null && $existing_theory !== '') {
                $new_total = $mcq_score + (float)$existing_theory;
                $new_status = 'graded';
            } else {
                // If this exam has theory parts (exam.hastheory === 'Yes') then status pending
                $hasTheoryFlag = isset($exam['hastheory']) && strtoupper($exam['hastheory']) === 'YES';
                if ($hasTheoryFlag) {
                    $new_total = null;
                    $new_status = 'pending';
                } else {
                    $new_total = $mcq_score;
                    $new_status = 'graded';
                }
            }

            $update = $conn->prepare("UPDATE exam_group_results SET mcq_score = ?, total = ?, status = ?, remarks = ?, exam_type = ?, updated_at = NOW() WHERE id = ?");
            $remarks = 'Auto-graded MCQ exam';
            // bind dynamically, robust to NULLs and numeric strings
            bind_params_safe($update, [$mcq_score, $new_total, $new_status, $remarks, $exam_type_label, $existing_id]);
            if (!$update->execute()) throw new Exception("Failed updating mcq: " . $update->error);
            $update->close();
        } else {
            // Insert a new exam_group_results row with mcq_score set; theory_score remains NULL
            $hasTheoryFlag = isset($exam['hastheory']) && strtoupper($exam['hastheory']) === 'YES';
            if ($hasTheoryFlag) {
                $statusVal = 'pending';
                $totalVal = null;
            } else {
                $statusVal = 'graded';
                $totalVal = $mcq_score;
            }

            $insert = $conn->prepare("INSERT INTO exam_group_results (student_id, exam_group_id, exam_type, class, term, session, subject, mcq_score, theory_score, total, status, remarks, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, NOW())");
            $remarks = 'Auto-graded MCQ exam';

            // NOTE: values order must match SQL placeholders. We supply 11 values (11 placeholders).
            bind_params_safe($insert, [
                $student_id,
                $exam_group_id,
                $exam_type_label,
                $class,
                $term,
                $session_yr,
                $subject,
                $mcq_score,
                $totalVal,
                $statusVal,
                $remarks
            ]);

            if (!$insert->execute()) throw new Exception("Failed inserting mcq row: " . $insert->error);
            $existing_id = $insert->insert_id;
            $insert->close();
        }

        // Insert attempt record for MCQ
        $attemptStmt = $conn->prepare("INSERT INTO student_exam_attempts (qtype, exam_id, student_id, started_at, submitted_at, score, remarks) VALUES (?, ?, ?, ?, ?, ?, ?)");
        $atype = 'mcq';
        $attemptScore = $mcq_score;
        $remarks = 'Auto-graded MCQ exam';
        bind_params_safe($attemptStmt, [$atype, $exam_id, $student_id, $started_at, $submitted_at, $attemptScore, $remarks]);
        if (!$attemptStmt->execute()) throw new Exception("Failed inserting mcq attempt: " . $attemptStmt->error);
        $attemptStmt->close();
    }

    /* -------------------------
       PROCESS THEORY (if submitted)
       ------------------------- */
    if ($submitting_theory) {
        // Ensure a row exists (do not touch mcq_score); if none create placeholder with mcq_score NULL
        if (!$existing_id) {
            $statusVal = 'pending';
            $remarks = 'Theory submitted for review';
            $ins = $conn->prepare("INSERT INTO exam_group_results (student_id, exam_group_id, exam_type, class, term, session, subject, mcq_score, theory_score, total, status, remarks, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, NULL, NULL, NULL, ?, ?, NOW())");
            bind_params_safe($ins, [$student_id, $exam_group_id, $exam_type_label, $class, $term, $session_yr, $subject, $statusVal, $remarks]);
            if (!$ins->execute()) throw new Exception("Failed inserting theory placeholder: " . $ins->error);
            $existing_id = $ins->insert_id;
            $ins->close();
        } else {
            // Update only status/remarks/exam_type; DO NOT change mcq_score
            $statusVal = 'pending';
            $remarks = 'Theory submitted for review';
            $upd = $conn->prepare("UPDATE exam_group_results SET status = ?, remarks = ?, exam_type = ?, updated_at = NOW() WHERE id = ?");
            bind_params_safe($upd, [$statusVal, $remarks, $exam_type_label, $existing_id]);
            if (!$upd->execute()) throw new Exception("Failed updating theory status: " . $upd->error);
            $upd->close();
        }

        // Save theory answers (delete old then insert)
        if (!empty($theory_answers)) {
            $del = $conn->prepare("DELETE FROM exam_answers WHERE exam_group_result_id = ? AND student_id = ?");
            bind_params_safe($del, [$existing_id, $student_id]);
            if (!$del->execute()) throw new Exception("Failed deleting old answers: " . $del->error);
            $del->close();

            $insAns = $conn->prepare("INSERT INTO exam_answers (student_id, exam_group_result_id, question_id, answer_text) VALUES (?, ?, ?, ?)");
            foreach ($theory_answers as $qid => $ansText) {
                $safe = trim((string)$ansText);
                bind_params_safe($insAns, [$student_id, $existing_id, (int)$qid, $safe]);
                if (!$insAns->execute()) throw new Exception("Failed inserting answer: " . $insAns->error);
            }
            $insAns->close();
        }

        // Insert attempt record for Theory
        $attemptStmt = $conn->prepare("INSERT INTO student_exam_attempts (qtype, exam_id, student_id, started_at, submitted_at, score, remarks) VALUES (?, ?, ?, ?, ?, ?, ?)");
        $atype = 'theory';
        $attemptScore = null;
        $remarks = 'Theory submitted for review';
        bind_params_safe($attemptStmt, [$atype, $exam_id, $student_id, $started_at, $submitted_at, $attemptScore, $remarks]);
        if (!$attemptStmt->execute()) throw new Exception("Failed inserting theory attempt: " . $attemptStmt->error);
        $attemptStmt->close();
    }

    // Remove exam session
    $delSess = $conn->prepare("DELETE FROM student_exam_sessions WHERE exam_id = ? AND student_id = ?");
    bind_params_safe($delSess, [$exam_id, $student_id]);
    $delSess->execute();
    $delSess->close();

    $conn->commit();

    // Clear client malpractice key
    echo "<script>localStorage.removeItem('malpracticeCount_{$student_id}_{$exam_id}');</script>";

    // Messages (strict separation)
    if ($submitting_mcq && !$submitting_theory) {
        $_SESSION['last_mcq_score'] = $mcq_score;
        $_SESSION['msg'] = "Exam submitted successfully. Your objective score is {$mcq_score}.";
        $_SESSION['type'] = "success";
    } elseif ($submitting_theory && !$submitting_mcq) {
        $_SESSION['msg'] = "Theory Exam Submitted for review.";
        $_SESSION['type'] = "success";
    } elseif ($submitting_mcq && $submitting_theory) {
        // If both in one shot we still show MCQ message (UI should avoid submitting both at once)
        $_SESSION['last_mcq_score'] = $mcq_score;
        $_SESSION['msg'] = "Exam submitted successfully. Your objective score is {$mcq_score}.";
        $_SESSION['type'] = "success";
    } else {
        $_SESSION['msg'] = "Exam submitted.";
        $_SESSION['type'] = "success";
    }

    header("Location: dashboard.php");
    exit;

} catch (Exception $e) {
    $conn->rollback();
    error_log("submit_exam.php error: " . $e->getMessage());
    $_SESSION['msg'] = "An error occurred while submitting the exam: " . $e->getMessage();
    $_SESSION['type'] = "danger";
    header("Location: dashboard.php");
    exit;
}
