<?php
// Start output buffering to prevent accidental HTML/warnings from breaking JSON responses
ob_start();
require_once '../../config/database.php';
require_once '../../config/auth.php';
require_once '../../config/rbac.php';
require_once '../../includes/functions.php';

// Helper to send clean JSON responses (clears any buffered output first)
function send_json($data, $httpCode = 200) {
    if (ob_get_length()) {
        $buf = ob_get_clean();
        if (!empty($buf)) {
            error_log('[attempts.php] buffered output before JSON response: ' . substr($buf, 0, 2000));
        }
    }
    http_response_code($httpCode);
    header('Content-Type: application/json');
    echo json_encode($data);
    exit;
}

// Check authentication
if (!isLoggedIn()) {
    send_json(['success' => false, 'message' => 'Authentication required'], 401);
}

$db = getDB();
$userId = $_SESSION['user_id'];
$method = $_SERVER['REQUEST_METHOD'];

try {
    if ($method !== 'POST') {
        throw new Exception('Method not allowed');
    }

    $rawInput = file_get_contents('php://input');
    // Log raw input for debugging save/submit failures
    if ($rawInput !== false) {
        error_log('[attempts.php] raw input: ' . substr($rawInput, 0, 2000));
    } else {
        error_log('[attempts.php] raw input: (false)');
    }
    $data = json_decode($rawInput, true);
    if ($rawInput === false || ($data === null && json_last_error() !== JSON_ERROR_NONE)) {
        throw new Exception('Invalid JSON data: ' . json_last_error_msg());
    }

    $examId = intval($data['exam_id'] ?? 0);
    $action = $data['action'] ?? '';

    if (!$examId || !$action) {
        throw new Exception('Exam ID and action are required');
    }

    // Verify exam exists and is published
    $stmt = $db->prepare("SELECT * FROM exams WHERE id = ? AND status = 'published'");
    $stmt->execute([$examId]);
    $exam = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$exam) {
        throw new Exception('Exam not found or not available');
    }

    // Check permissions based on user role
    if (hasAnyRole(['student'])) {
        // Student permissions - can only access their own attempts
        $studentId = $userId;

        // Check if student is enrolled in course (if exam is course-specific)
        if ($exam['course_id']) {
            $stmt = $db->prepare("SELECT id FROM course_enrollments WHERE course_id = ? AND student_id = ? AND status IN ('enrolled', 'in_progress')");
            $stmt->execute([$exam['course_id'], $studentId]);
            if (!$stmt->fetch()) {
                throw new Exception('You must be enrolled in this course to take this exam');
            }
        }
    } elseif (hasAnyRole(['instructor', 'admin'])) {
        // Instructor permissions - can access any attempt for their exams
        if ($exam['instructor_id'] !== $userId && !hasAnyRole(['admin'])) {
            throw new Exception('Access denied');
        }
        $studentId = intval($data['student_id'] ?? 0);
        if (!$studentId) {
            throw new Exception('Student ID required for instructor actions');
        }
    } else {
        throw new Exception('Access denied');
    }

    switch ($action) {
        case 'start':
            // Start a new exam attempt
            handleStartAttempt($db, $examId, $studentId, $exam);
            break;

        case 'save':
            // Save progress
            handleSaveProgress($db, $examId, $studentId, $data);
            break;

        case 'submit':
            // Submit exam
            handleSubmitExam($db, $examId, $studentId, $data, $exam);
            break;

        case 'get':
            // Get attempt details
            handleGetAttempt($db, $examId, $studentId);
            break;

        default:
            throw new Exception('Invalid action');
    }

} catch (Exception $e) {
    // Log exception for debugging
    error_log('[attempts.php] exception: ' . $e->getMessage());
    send_json(['success' => false, 'message' => $e->getMessage()], 400);
}

function handleStartAttempt($db, $examId, $studentId, $exam) {
    // Check if student already has an in-progress attempt
    $stmt = $db->prepare("
        SELECT id, attempt_number FROM exam_attempts
        WHERE exam_id = ? AND student_id = ? AND status = 'in_progress'
        ORDER BY started_at DESC LIMIT 1
    ");
    $stmt->execute([$examId, $studentId]);
    $existingAttempt = $stmt->fetch(PDO::FETCH_ASSOC);

    if ($existingAttempt) {
        // Resume existing attempt
        send_json([
            'success' => true,
            'message' => 'Resumed existing attempt',
            'attempt_id' => $existingAttempt['id'],
            'attempt_number' => $existingAttempt['attempt_number']
        ]);
    }

    // Check attempt limits
    $stmt = $db->prepare("
        SELECT COUNT(*) as attempt_count, MAX(attempt_number) as max_attempt
        FROM exam_attempts
        WHERE exam_id = ? AND student_id = ?
    ");
    $stmt->execute([$examId, $studentId]);
    $attemptInfo = $stmt->fetch(PDO::FETCH_ASSOC);

    $nextAttemptNumber = ($attemptInfo['max_attempt'] ?? 0) + 1;

    if ($exam['max_attempts'] > 0 && $nextAttemptNumber > $exam['max_attempts']) {
        throw new Exception('Maximum attempts exceeded');
    }

    // Create new attempt
    $stmt = $db->prepare("
        INSERT INTO exam_attempts (
            exam_id, student_id, attempt_number, status, started_at, ip_address, browser_info
        ) VALUES (?, ?, ?, 'in_progress', NOW(), ?, ?)
    ");

    $ipAddress = $_SERVER['REMOTE_ADDR'] ?? '';
    $browserInfo = json_encode([
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
        'accept_language' => $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''
    ]);

    $stmt->execute([$examId, $studentId, $nextAttemptNumber, $ipAddress, $browserInfo]);
    $attemptId = $db->lastInsertId();

    send_json([
        'success' => true,
        'message' => 'Exam attempt started',
        'attempt_id' => $attemptId,
        'attempt_number' => $nextAttemptNumber
    ]);
}

function handleSaveProgress($db, $examId, $studentId, $data) {
    // Find the in-progress attempt
    $stmt = $db->prepare("
        SELECT id FROM exam_attempts
        WHERE exam_id = ? AND student_id = ? AND status = 'in_progress'
        ORDER BY started_at DESC LIMIT 1
    ");
    $stmt->execute([$examId, $studentId]);
    $attempt = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$attempt) {
        throw new Exception('No active attempt found');
    }

    $answers = $data['answers'] ?? [];
    $timeSpent = intval($data['time_spent'] ?? 0);

    // Update time_spent in exam_attempts
    $stmt = $db->prepare("
        UPDATE exam_attempts
        SET time_spent_seconds = ?, last_accessed = NOW()
        WHERE id = ?
    ");
    $stmt->execute([$timeSpent, $attempt['id']]);

    // Save individual answers to student_answers table
    if (!empty($answers)) {
        $ipAddress = $_SERVER['REMOTE_ADDR'] ?? '';
        $browserInfo = json_encode([
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
            'accept_language' => $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''
        ]);

        foreach ($answers as $questionId => $answer) {
            // Determine answer content and type
            $answerContent = '';
            $answerType = 'text';

            if ($answer !== null && $answer !== '') {
                if (is_array($answer)) {
                    $answerContent = json_encode($answer);
                    $answerType = 'json';
                } else {
                    $answerContent = $answer;
                }
            }

            // Insert or update the answer - include all required columns
            $stmt = $db->prepare("
                INSERT INTO student_answers (
                    exam_id, student_id, attempt_id, question_id,
                    answer_content, answer_type, status, is_draft, is_final,
                    time_spent_seconds, ip_address, browser_info,
                    started_at, first_save_at, last_save_at, save_count,
                    source
                ) VALUES (?, ?, ?, ?, ?, ?, 'saved', TRUE, FALSE, ?, ?, ?, NOW(), NOW(), NOW(), 1, 'web')
                ON DUPLICATE KEY UPDATE
                    answer_content = VALUES(answer_content),
                    answer_type = VALUES(answer_type),
                    last_save_at = NOW(),
                    save_count = save_count + 1,
                    time_spent_seconds = VALUES(time_spent_seconds),
                    status = VALUES(status)
            ");
            $stmt->execute([
                $examId, $studentId, $attempt['id'], $questionId,
                $answerContent, $answerType, $timeSpent, $ipAddress, $browserInfo
            ]);
        }
    }

    send_json(['success' => true, 'message' => 'Progress saved']);
}

function handleSubmitExam($db, $examId, $studentId, $data, $exam) {
    // Find the in-progress attempt
    $stmt = $db->prepare("
        SELECT id FROM exam_attempts
        WHERE exam_id = ? AND student_id = ? AND status = 'in_progress'
        ORDER BY started_at DESC LIMIT 1
    ");
    $stmt->execute([$examId, $studentId]);
    $attempt = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$attempt) {
        throw new Exception('No active attempt found');
    }

    $answers = $data['answers'] ?? [];
    $timeSpent = intval($data['time_spent'] ?? 0);
    
    // Debug logging
    error_log('[handleSubmitExam] Exam ID: ' . $examId . ', Student ID: ' . $studentId . ', Answers count: ' . count($answers));
    error_log('[handleSubmitExam] Answers data: ' . json_encode(array_keys($answers)));

    // Calculate score
    $scoreData = calculateExamScore($db, $examId, $answers, $exam['passing_score']);

    // Update attempt status with score and completion data
    $stmt = $db->prepare("
        UPDATE exam_attempts
        SET status = 'submitted',
            score = ?,
            max_score = ?,
            percentage = ?,
            is_passed = ?,
            completed_at = NOW(),
            time_spent_seconds = ?
        WHERE id = ?
    ");

    $res = $stmt->execute([
        $scoreData['score'],
        $scoreData['max_score'],
        $scoreData['percentage'],
        $scoreData['is_passed'] ? 1 : 0,
        $timeSpent,
        $attempt['id']
    ]);
    if ($res === false) {
        $err = $stmt->errorInfo();
        error_log('[attempts.php] update attempt failed: ' . implode(' | ', $err));
        throw new Exception('Failed to save submission');
    }

    // Save individual answers to student_answers table
    saveStudentAnswers($db, $examId, $studentId, $attempt['id'], $answers, $scoreData, $timeSpent);

    // Update question analytics
    updateQuestionAnalytics($db, $examId, $answers);

    send_json([
        'success' => true,
        'message' => 'Exam submitted successfully',
        'score' => $scoreData['score'],
        'max_score' => $scoreData['max_score'],
        'percentage' => $scoreData['percentage'],
        'passed' => $scoreData['is_passed']
    ]);
}

function handleGetAttempt($db, $examId, $studentId) {
    $stmt = $db->prepare("
        SELECT ea.*, e.title as exam_title, e.total_points, e.passing_score,
               ea.time_spent_seconds as time_spent
        FROM exam_attempts ea
        JOIN exams e ON ea.exam_id = e.id
        WHERE ea.exam_id = ? AND ea.student_id = ?
        ORDER BY ea.started_at DESC LIMIT 1
    ");
    $stmt->execute([$examId, $studentId]);
    $attempt = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$attempt) {
        throw new Exception('Attempt not found');
    }

    // Get saved answers from student_answers table
    $stmt = $db->prepare("
        SELECT question_id, answer_content, answer_type
        FROM student_answers
        WHERE exam_id = ? AND student_id = ? AND attempt_id = ?
        ORDER BY question_id
    ");
    $stmt->execute([$examId, $studentId, $attempt['id']]);
    $savedAnswers = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Format answers as expected by frontend
    $answers = [];
    foreach ($savedAnswers as $answer) {
        $questionId = $answer['question_id'];
        $content = $answer['answer_content'];
        if ($answer['answer_type'] === 'json') {
            $answers[$questionId] = json_decode($content, true);
        } else {
            $answers[$questionId] = $content;
        }
    }

    $attempt['answers'] = json_encode($answers);

    send_json([
        'success' => true,
        'attempt' => $attempt
    ]);
}

function calculateExamScore($db, $examId, $answers, $passingScore) {
    // Get exam questions with correct answers
    $stmt = $db->prepare("
        SELECT eq.question_id, eq.points, q.question_type, qo.id as option_id, qo.is_correct
        FROM exam_questions eq
        JOIN questions q ON eq.question_id = q.id
        LEFT JOIN question_options qo ON q.id = qo.question_id
        WHERE eq.exam_id = ?
        ORDER BY eq.question_id, qo.id
    ");
    $stmt->execute([$examId]);
    $questionData = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Group by question
    $questions = [];
    foreach ($questionData as $row) {
        $qid = $row['question_id'];
        if (!isset($questions[$qid])) {
            $questions[$qid] = [
                'type' => $row['question_type'],
                'points' => $row['points'],
                'options' => []
            ];
        }
        if ($row['option_id']) {
            $questions[$qid]['options'][] = [
                'id' => $row['option_id'],
                'is_correct' => $row['is_correct']
            ];
        }
    }

    $totalScore = 0;
    $maxScore = 0;

    foreach ($questions as $questionId => $question) {
        $maxScore += $question['points'];
        $studentAnswer = $answers[$questionId] ?? null;

        if (!$studentAnswer) {
            continue; // No answer given
        }

        $points = 0;

        switch ($question['type']) {
            case 'mcq_single':
            case 'true_false':
                // Check if selected option is correct
                foreach ($question['options'] as $option) {
                    if ($option['id'] == $studentAnswer && $option['is_correct']) {
                        $points = $question['points'];
                        break;
                    }
                }
                break;

            case 'mcq_multiple':
                // Check multiple selections
                if (is_array($studentAnswer)) {
                    $correctOptions = array_filter($question['options'], fn($opt) => $opt['is_correct']);
                    $selectedCorrect = 0;
                    $totalCorrect = count($correctOptions);

                    foreach ($studentAnswer as $selectedId) {
                        foreach ($correctOptions as $correctOption) {
                            if ($correctOption['id'] == $selectedId) {
                                $selectedCorrect++;
                                break;
                            }
                        }
                    }

                    // Partial credit: points proportional to correct selections
                    if ($totalCorrect > 0) {
                        $points = ($selectedCorrect / $totalCorrect) * $question['points'];
                    }
                }
                break;

            case 'essay':
            case 'short_answer':
            case 'code':
                // Manual grading required - for now, give 0 points
                // This will be updated when instructor grades
                $points = 0;
                break;

            case 'matching':
                // Check matching answers
                if (is_array($studentAnswer)) {
                    $correctMatches = 0;
                    $totalMatches = 0;

                    foreach ($question['options'] as $option) {
                        if ($option['is_correct']) {
                            $totalMatches++;
                            if (isset($studentAnswer[$option['id']]) &&
                                $studentAnswer[$option['id']] == $option['id']) {
                                $correctMatches++;
                            }
                        }
                    }

                    if ($totalMatches > 0) {
                        $points = ($correctMatches / $totalMatches) * $question['points'];
                    }
                }
                break;

            case 'fill_blanks':
                // Automatic grading for fill-in-the-blanks
                if (is_array($studentAnswer)) {
                    // Get correct answers from explanation, split by comma
                    $correctAnswers = array_map('trim', explode(',', $question['explanation'] ?? ''));
                    $correctCount = 0;
                    $totalBlanks = count($correctAnswers);

                    foreach ($studentAnswer as $index => $answer) {
                        $correctIndex = $index - 1; // answers are 1-based
                        if (isset($correctAnswers[$correctIndex]) &&
                            strtolower(trim($answer)) === strtolower($correctAnswers[$correctIndex])) {
                            $correctCount++;
                        }
                    }

                    if ($totalBlanks > 0) {
                        $points = ($correctCount / $totalBlanks) * $question['points'];
                    }
                }
                break;
        }

        $totalScore += $points;
    }

    $percentage = $maxScore > 0 ? round(($totalScore / $maxScore) * 100, 2) : 0;

    return [
        'score' => round($totalScore, 2),
        'max_score' => $maxScore,
        'percentage' => $percentage,
        'is_passed' => $percentage >= $passingScore
    ];
}

function updateQuestionAnalytics($db, $examId, $answers) {
    // Get exam questions
    $stmt = $db->prepare("
        SELECT eq.question_id, q.question_type
        FROM exam_questions eq
        JOIN questions q ON eq.question_id = q.id
        WHERE eq.exam_id = ?
    ");
    $stmt->execute([$examId]);
    $questions = $stmt->fetchAll(PDO::FETCH_ASSOC);

    $date = date('Y-m-d');

    foreach ($questions as $question) {
        $questionId = $question['question_id'];
        $studentAnswer = $answers[$questionId] ?? null;

        // Get correct answers
        $stmt = $db->prepare("
            SELECT COUNT(*) as correct_count FROM question_options
            WHERE question_id = ? AND is_correct = TRUE
        ");
        $stmt->execute([$questionId]);
        $correctCount = $stmt->fetch(PDO::FETCH_ASSOC)['correct_count'];

        $isCorrect = false;

        if ($studentAnswer) {
            switch ($question['question_type']) {
                case 'mcq_single':
                case 'true_false':
                    $stmt = $db->prepare("
                        SELECT is_correct FROM question_options
                        WHERE id = ? AND question_id = ?
                    ");
                    $stmt->execute([$studentAnswer, $questionId]);
                    $result = $stmt->fetch(PDO::FETCH_ASSOC);
                    $isCorrect = $result && $result['is_correct'];
                    break;

                case 'mcq_multiple':
                    if (is_array($studentAnswer) && !empty($studentAnswer)) {
                        $placeholders = str_repeat('?,', count($studentAnswer) - 1) . "?";
                        $stmt = $db->prepare("
                            SELECT COUNT(*) as correct_selected FROM question_options
                            WHERE id IN ($placeholders) AND is_correct = TRUE
                        ");
                        $stmt->execute($studentAnswer);
                        $correctSelected = $stmt->fetch(PDO::FETCH_ASSOC)['correct_selected'];
                        $isCorrect = $correctSelected == $correctCount;
                    } elseif (is_array($studentAnswer) && empty($studentAnswer)) {
                        $isCorrect = $correctCount == 0; // If no correct options, and student selected none, it's correct
                    }
                    break;
            }
        }

        // Update or insert analytics
        $stmt = $db->prepare("
            INSERT INTO question_analytics (
                question_id, exam_id, date, times_used, correct_answers, incorrect_answers
            ) VALUES (?, ?, ?, 1, ?, ?)
            ON DUPLICATE KEY UPDATE
                times_used = times_used + 1,
                correct_answers = correct_answers + ?,
                incorrect_answers = incorrect_answers + ?
        ");

        $correct = $isCorrect ? 1 : 0;
        $incorrect = $isCorrect ? 0 : 1;

        $stmt->execute([
            $questionId, $examId, $date,
            $correct, $incorrect,
            $correct, $incorrect
        ]);
    }
}

/**
 * Save individual student answers to the student_answers table
 * 
 * This function saves all exam responses to the student_answers table with:
 * - Student identifier
 * - Exam identifier
 * - Timestamp of submission
 * - Individual question answers
 * - Score if applicable
 * - Additional metadata for data integrity
 * 
 * @param PDO $db Database connection
 * @param int $examId Exam ID
 * @param int $studentId Student ID
 * @param int $attemptId Attempt ID
 * @param array $answers Student answers indexed by question_id
 * @param array $scoreData Score data from calculateExamScore
 * @param int $timeSpent Total time spent in seconds
 */
function saveStudentAnswers($db, $examId, $studentId, $attemptId, $answers, $scoreData, $timeSpent) {
    // Get exam questions with their details
    $stmt = $db->prepare("
        SELECT eq.question_id, eq.points, q.question_type, q.content as question_content
        FROM exam_questions eq
        JOIN questions q ON eq.question_id = q.id
        WHERE eq.exam_id = ?
        ORDER BY eq.sort_order, eq.question_id
    ");
    $stmt->execute([$examId]);
    $questions = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Log for debugging
    error_log('[saveStudentAnswers] Exam ID: ' . $examId . ', Questions found: ' . count($questions) . ', Answers provided: ' . count($answers));
    
    if (empty($questions)) {
        error_log('[saveStudentAnswers] ERROR: No questions found for exam ID: ' . $examId);
        // Don't throw - still save answers for any questions that were answered
    }

    // Get correct answers for each question type to determine is_correct
    $questionScores = getQuestionScores($db, $examId, $answers);

    // Get client metadata
    $ipAddress = $_SERVER['REMOTE_ADDR'] ?? '';
    $browserInfo = json_encode([
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
        'accept_language' => $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''
    ]);
    $deviceInfo = json_encode([
        'platform' => $_SERVER['HTTP_SEC_CH_UA_PLATFORM'] ?? '',
        'mobile' => $_SERVER['HTTP_SEC_CH_UA_MOBILE'] ?? ''
    ]);
    $clientTimezone = $_SERVER['HTTP_X_TIMEZONE'] ?? 'UTC';
    $sessionId = session_id();

    $db->beginTransaction();

    try {

        $submittedAt = date('Y-m-d H:i:s');
        $insertedCount = 0;
        
        foreach ($questions as $question) {
            $questionId = $question['question_id'];
            $maxPoints = floatval($question['points']);
            $questionType = $question['question_type'];
            $studentAnswer = $answers[$questionId] ?? null;

            // Determine answer content and type
            $answerContent = '';
            $answerType = 'text';
            $isCorrect = false;
            $score = 0;
            $gradingMethod = 'auto';
            $isGraded = true;

            if ($studentAnswer !== null && $studentAnswer !== '') {
                // Format answer content based on type
                if (is_array($studentAnswer)) {
                    $answerContent = json_encode($studentAnswer);
                    $answerType = 'json';
                } else {
                    $answerContent = $studentAnswer;
                    // Determine answer type based on question type
                    if (in_array($questionType, ['mcq_single', 'true_false'])) {
                        $answerType = 'numeric';
                    } elseif (in_array($questionType, ['essay', 'short_answer', 'code'])) {
                        $answerType = 'text';
                    }
                }

                // Get score for this question
                $questionScoreData = $questionScores[$questionId] ?? ['points' => 0, 'is_correct' => false];
                $score = floatval($questionScoreData['points']);
                $isCorrect = $questionScoreData['is_correct'] ?? false;

                // Essay/short answer/code questions need manual grading
                if (in_array($questionType, ['essay', 'short_answer', 'code'])) {
                    $gradingMethod = 'manual';
                    $isGraded = false;
                    $score = 0; // No auto-score for manual questions
                    $isCorrect = null;
                }
            } else {
                // No answer given
                $answerContent = '';
                $isCorrect = false;
                $score = 0;
            }

            // Calculate text metrics
            $answerLength = mb_strlen($answerContent, 'UTF-8');
            $wordCount = $answerContent ? preg_match_all('/\S+/u', $answerContent) : 0;
            $charCount = $answerLength;

            // Generate answer hash for integrity checking
            $answerHash = md5($answerContent);

            // Determine status based on grading method
            $status = $isGraded ? 'submitted' : 'pending_manual_review';

            // Insert the answer into student_answers table
            // Make sure to include ALL required columns
                $stmt = $db->prepare("
                    INSERT INTO student_answers (
                        exam_id, student_id, attempt_id, question_id,
                        answer_content, answer_type, is_correct, score, max_score,
                        is_graded, grading_method, time_spent_seconds,
                        ip_address, browser_info, submitted_at, started_at,
                        status, is_draft, is_final, source
                    ) VALUES (
                        ?, ?, ?, ?,
                        ?, ?, ?, ?, ?,
                        ?, ?, ?, 
                        ?, ?, NOW(), NOW(),
                        ?, 0, 1, 'web'
                    )
                    ON DUPLICATE KEY UPDATE
                        answer_content = VALUES(answer_content),
                        answer_type = VALUES(answer_type),
                        is_correct = VALUES(is_correct),
                        score = VALUES(score),
                        max_score = VALUES(max_score),
                        is_graded = VALUES(is_graded),
                        grading_method = VALUES(grading_method),
                        time_spent_seconds = VALUES(time_spent_seconds),
                        status = VALUES(status),
                        is_final = VALUES(is_final),
                        submitted_at = VALUES(submitted_at),
                        updated_at = NOW()
                ");

                $stmt->execute([
                    $examId,
                    $studentId,
                    $attemptId,
                    $questionId,
                    $answerContent,
                    $answerType,
                    $isCorrect !== null ? ($isCorrect ? 1 : 0) : null,
                    $score,
                    $maxPoints,
                    $isGraded ? 1 : 0,
                    $gradingMethod,
                    $timeSpent,
                    $ipAddress,
                    $browserInfo,
                    $status
                ]);
                $insertedCount++;
        }

        $db->commit();
        error_log('[saveStudentAnswers] Successfully saved ' . $insertedCount . ' answers for exam ' . $examId . ', student ' . $studentId);

    } catch (Exception $e) {
        $db->rollBack();
        error_log('[saveStudentAnswers] Error: ' . $e->getMessage());
        // Don't throw - the exam submission should still succeed even if student_answers fails
        // The attempt is already saved with the score
    }
}

/**
 * Get scores for individual questions
 * 
 * @param PDO $db Database connection
 * @param int $examId Exam ID
 * @param array $answers Student answers
 * @return array Question scores indexed by question_id
 */
function getQuestionScores($db, $examId, $answers) {
    $result = [];

    // Get exam questions with correct answers
    $stmt = $db->prepare("
        SELECT eq.question_id, eq.points, q.question_type, qo.id as option_id, qo.is_correct
        FROM exam_questions eq
        JOIN questions q ON eq.question_id = q.id
        LEFT JOIN question_options qo ON q.id = qo.question_id
        WHERE eq.exam_id = ?
        ORDER BY eq.question_id, qo.id
    ");
    $stmt->execute([$examId]);
    $questionData = $stmt->fetchAll(PDO::FETCH_ASSOC);

    // Group by question
    $questions = [];
    foreach ($questionData as $row) {
        $qid = $row['question_id'];
        if (!isset($questions[$qid])) {
            $questions[$qid] = [
                'type' => $row['question_type'],
                'points' => $row['points'],
                'options' => []
            ];
        }
        if ($row['option_id']) {
            $questions[$qid]['options'][] = [
                'id' => $row['option_id'],
                'is_correct' => $row['is_correct']
            ];
        }
    }

    // Calculate score for each question
    foreach ($questions as $questionId => $question) {
        $maxScore = $question['points'];
        $studentAnswer = $answers[$questionId] ?? null;
        $points = 0;
        $isCorrect = false;

        if ($studentAnswer !== null && $studentAnswer !== '') {
            switch ($question['type']) {
                case 'mcq_single':
                case 'true_false':
                    foreach ($question['options'] as $option) {
                        if ($option['id'] == $studentAnswer && $option['is_correct']) {
                            $points = $maxScore;
                            $isCorrect = true;
                            break;
                        }
                    }
                    break;

                case 'mcq_multiple':
                    if (is_array($studentAnswer)) {
                        $correctOptions = array_filter($question['options'], fn($opt) => $opt['is_correct']);
                        $selectedCorrect = 0;
                        $totalCorrect = count($correctOptions);

                        foreach ($studentAnswer as $selectedId) {
                            foreach ($correctOptions as $correctOption) {
                                if ($correctOption['id'] == $selectedId) {
                                    $selectedCorrect++;
                                    break;
                                }
                            }
                        }

                        if ($totalCorrect > 0) {
                            $points = ($selectedCorrect / $totalCorrect) * $maxScore;
                            $isCorrect = $selectedCorrect == $totalCorrect;
                        }
                    }
                    break;

                case 'matching':
                    if (is_array($studentAnswer)) {
                        $correctMatches = 0;
                        $totalMatches = 0;

                        foreach ($question['options'] as $option) {
                            if ($option['is_correct']) {
                                $totalMatches++;
                                if (isset($studentAnswer[$option['id']]) &&
                                    $studentAnswer[$option['id']] == $option['id']) {
                                    $correctMatches++;
                                }
                            }
                        }

                        if ($totalMatches > 0) {
                            $points = ($correctMatches / $totalMatches) * $maxScore;
                            $isCorrect = $correctMatches == $totalMatches;
                        }
                    }
                    break;

                case 'fill_blanks':
                    if (is_array($studentAnswer)) {
                        // Get correct answers from explanation
                        $correctAnswers = [];
                        // Note: In production, you'd want to store correct answers in a dedicated field
                        // For now, we'll mark as not auto-graded
                        $points = 0;
                        $isCorrect = false;
                    }
                    break;

                case 'essay':
                case 'short_answer':
                case 'code':
                    // Manual grading required
                    $points = 0;
                    $isCorrect = false;
                    break;
            }
        }

        $result[$questionId] = [
            'points' => round($points, 2),
            'max_points' => $maxScore,
            'is_correct' => $isCorrect
        ];
    }

    return $result;
}
?>