<?php

declare(strict_types=1);

namespace App\Controllers;

use App\Core\Controller;

/**
 * Exam Controller
 *
 * Handles exam listing, taking exams, submitting answers,
 * and viewing exam results. Supports up to 3 attempts per exam
 * with proper attempt tracking and user name display.
 */
class ExamController extends Controller
{
    /**
     * Maximum attempts allowed per exam per student.
     */
    private const MAX_ATTEMPTS = 3;

    /**
     * Display all published exams with user attempt info.
     */
    public function index(): void
    {
        $examModel = $this->model('Exam');
        $exams = $examModel->getPublished();

        // Get current user info for attempt tracking and name display
        $user = getCurrentUser();
        $userId = null;
        $studentId = null;
        $userName = '';
        $isLoggedIn = $user !== null;

        if ($user !== null) {
            $userId = (int) $user['id'];
            $studentModel = $this->model('Student');
            $student = $studentModel->getByUserId($userId);
            if ($student !== null) {
                $studentId = (int) $student['id'];
            }
            $userName = $user['name'] ?? '';
        }

        // Attach attempt info to each exam
        $examResultModel = $this->model('ExamResult');
        foreach ($exams as &$exam) {
            $attemptCount = 0;
            $latestResult = null;
            if ($studentId !== null) {
                $attemptCount = $examResultModel->countAttempts((int) $exam['id'], $studentId);
                $latestResult = $examResultModel->getLatestAttempt((int) $exam['id'], $studentId);
            }
            $exam['attempt_count'] = $attemptCount;
            $exam['remaining_attempts'] = max(0, self::MAX_ATTEMPTS - $attemptCount);
            $exam['latest_result'] = $latestResult;
            $exam['can_attempt'] = ($attemptCount < self::MAX_ATTEMPTS);
        }
        unset($exam);

        $this->view('exam/index', [
            'title'      => 'Online Exams - Gmind Institute',
            'exams'      => $exams,
            'currentNav' => 'exam',
            'userName'   => $userName,
            'isLoggedIn' => $isLoggedIn,
        ]);
    }

    /**
     * Display an exam for a student to take.
     *
     * Requires the student to be logged in. Loads the exam
     * with all its questions for the exam interface.
     * Checks attempt limits (max 3 attempts).
     *
     * @param int $id The exam ID
     */
    public function take(int $id): void
    {
        requireLogin();

        $examModel = $this->model('Exam');
        $exam = $examModel->getWithQuestions($id);

        if (empty($exam) || !(bool) $exam['is_published']) {
            flash('error', 'Exam not found or not available.');
            redirect(BASE_PATH . '/exam');
        }

        // Get user info
        $user = getCurrentUser();
        $userName = '';
        $studentId = null;
        $attemptCount = 0;

        if ($user !== null) {
            $userName = $user['name'] ?? '';
            $studentModel = $this->model('Student');
            $student = $studentModel->getByUserId((int) $user['id']);

            if ($student !== null) {
                $studentId = (int) $student['id'];

                // Count existing attempts
                $examResultModel = $this->model('ExamResult');
                $attemptCount = $examResultModel->countAttempts($id, $studentId);

                // Check attempt limit
                if ($attemptCount >= self::MAX_ATTEMPTS) {
                    flash('error', 'You have used all ' . self::MAX_ATTEMPTS . ' attempts for this exam.');
                    redirect(BASE_PATH . '/exam/' . $id . '/result');
                }
            }
        }

        // Remove correct answers from questions before sending to view
        $safeQuestions = array_map(function (array $q): array {
            unset($q['correct_answer']);
            return $q;
        }, $exam['questions']);

        $exam['questions'] = $safeQuestions;

        $this->view('exam/take', [
            'title'             => $exam['title'] . ' - Gmind Institute',
            'exam'              => $exam,
            'currentNav'        => 'exam',
            'userName'          => $userName,
            'attemptCount'      => $attemptCount,
            'remainingAttempts' => max(0, self::MAX_ATTEMPTS - $attemptCount),
            'maxAttempts'       => self::MAX_ATTEMPTS,
        ]);
    }

    /**
     * Process exam submission: calculate score and store result.
     *
     * Requires the student to be logged in. Validates CSRF token,
     * calculates the score from submitted answers, and stores
     * the exam result with attempt number.
     *
     * @param int $id The exam ID
     */
    public function submit(int $id): void
    {
        requireLogin();
        $this->validateCsrf();

        $examModel = $this->model('Exam');
        $exam = $examModel->find($id);

        if (empty($exam) || !(bool) $exam['is_published']) {
            flash('error', 'Exam not found or not available.');
            redirect(BASE_PATH . '/exam');
        }

        // Get current student
        $user = getCurrentUser();
        if ($user === null) {
            flash('error', 'Please log in to submit an exam.');
            redirect(BASE_PATH . '/login');
        }

        $studentModel = $this->model('Student');
        $student = $studentModel->getByUserId((int) $user['id']);

        if ($student === null) {
            flash('error', 'Student record not found. Please contact administration.');
            redirect(BASE_PATH . '/exam');
        }

        $studentId = (int) $student['id'];

        // Count existing attempts and check limit
        $examResultModel = $this->model('ExamResult');
        $attemptCount = $examResultModel->countAttempts($id, $studentId);
        $nextAttemptNumber = $attemptCount + 1;

        if ($attemptCount >= self::MAX_ATTEMPTS) {
            flash('error', 'You have already used all ' . self::MAX_ATTEMPTS . ' attempts for this exam.');
            redirect(BASE_PATH . '/exam/' . $id . '/result');
        }

        // Collect answers from POST (format: answers[question_id] = 'a'|'b'|'c'|'d')
        $rawAnswers = $_POST['answers'] ?? [];
        $answers = [];

        foreach ($rawAnswers as $questionId => $answer) {
            $qId = (int) $questionId;
            $ans = sanitize((string) $answer);
            if (in_array($ans, ['a', 'b', 'c', 'd'], true)) {
                $answers[$qId] = $ans;
            }
        }

        // Calculate score
        $score = $examResultModel->calculateScore($id, $answers);

        try {
            // Store the exam result with attempt number
            $examResultModel->create([
                'exam_id'        => $id,
                'student_id'     => $studentId,
                'attempt_number' => $nextAttemptNumber,
                'total_obtained' => $score['obtained'],
                'total_marks'    => $score['total'],
                'percentage'     => $score['percentage'],
                'status'         => $score['status'],
                'started_at'     => $_POST['started_at'] ?? date('Y-m-d H:i:s'),
                'completed_at'   => date('Y-m-d H:i:s'),
            ]);

            $attemptId = (int) $this->db->lastInsertId();

            flash('success', $score['status'] === 'pass'
                ? 'Congratulations! You passed the exam with ' . $score['percentage'] . '%.'
                : 'You scored ' . $score['percentage'] . '%. Keep studying and try again!');
            redirect(BASE_PATH . '/exam/' . $id . '/result/' . $attemptId);
        } catch (\Throwable $e) {
            error_log('Exam submission error: ' . $e->getMessage());
            flash('error', 'Something went wrong submitting your exam. Please try again.');
            redirect(BASE_PATH . '/exam/' . $id);
        }
    }

    /**
     * Display the exam result for a specific attempt.
     *
     * Requires the student to be logged in.
     * If no attemptId provided, shows the latest attempt.
     *
     * @param int $id The exam ID
     * @param int|null $attemptId Specific attempt ID (optional)
     */
    public function result(int $id, ?int $attemptId = null): void
    {
        requireLogin();

        // Ensure attempt_number column exists
        $this->ensureAttemptNumberColumn();

        $user = getCurrentUser();
        if ($user === null) {
            flash('error', 'Please log in to view exam results.');
            redirect(BASE_PATH . '/login');
        }

        $userName = $user['name'] ?? '';

        $studentModel = $this->model('Student');
        $student = $studentModel->getByUserId((int) $user['id']);

        if ($student === null) {
            flash('error', 'Student record not found.');
            redirect(BASE_PATH . '/exam');
        }

        $studentId = (int) $student['id'];
        $examResultModel = $this->model('ExamResult');

        // Get the specific attempt or the latest one
        if ($attemptId !== null && $attemptId > 0) {
            $result = $examResultModel->findWithExam($attemptId);
            // Verify this result belongs to this student and exam
            if ($result === null || (int) $result['student_id'] !== $studentId || (int) $result['exam_id'] !== $id) {
                flash('error', 'Result not found.');
                redirect(BASE_PATH . '/exam');
            }
        } else {
            $result = $examResultModel->getLatestAttempt($id, $studentId);
        }

        if ($result === null) {
            flash('error', 'No result found for this exam.');
            redirect(BASE_PATH . '/exam');
        }

        // Get all attempts for this exam by this student (for attempt navigation)
        $allAttempts = $examResultModel->getAttemptsByExamAndStudent($id, $studentId);

        // Get exam with questions for answer review
        $examModel = $this->model('Exam');
        $exam = $examModel->getWithQuestions($id);

        // Calculate correct/wrong counts from the score details
        $correct = 0;
        $wrong = 0;
        if (!empty($exam['questions'])) {
            // Re-calculate from the stored result
            $totalQ = count($exam['questions']);
            $obtainedMarks = (float) ($result['total_obtained'] ?? 0);
            $passMarks = (int) ($exam['pass_marks'] ?? 0);
            // We don't have individual answers stored, so use percentage-based estimation
            // Actually, we should store the answers. For now, use the total marks to figure out correct count
            $marksPerQuestion = [];
            $totalPossibleMarks = 0;
            foreach ($exam['questions'] as $q) {
                $marksPerQuestion[] = (int) ($q['marks'] ?? 1);
                $totalPossibleMarks += (int) ($q['marks'] ?? 1);
            }
            // Estimate correct count from obtained marks
            $correct = $totalPossibleMarks > 0 ? (int) round($obtainedMarks / ($totalPossibleMarks / count($exam['questions']))) : 0;
            $wrong = $totalQ - $correct;
            // More accurate: use the total_obtained directly
            $correct = 0;
            $wrong = 0;
            $remainingMarks = $obtainedMarks;
            foreach ($marksPerQuestion as $mq) {
                if ($remainingMarks >= $mq) {
                    $correct++;
                    $remainingMarks -= $mq;
                }
            }
            $wrong = count($exam['questions']) - $correct;
        }

        $this->view('exam/result', [
            'title'       => 'Exam Result - Gmind Institute',
            'result'      => $result,
            'exam'        => $exam,
            'currentNav'  => 'exam',
            'userName'    => $userName,
            'studentName' => $user['name'] ?? $student['enrollment_no'] ?? 'Student',
            'allAttempts' => $allAttempts,
            'correct'     => $correct,
            'wrong'       => $wrong,
            'maxAttempts' => self::MAX_ATTEMPTS,
        ]);
    }

    /**
     * Ensure the attempt_number column exists in exam_results table.
     * Auto-migrates the database schema if needed.
     */
    private function ensureAttemptNumberColumn(): void
    {
        static $migrated = false;

        if ($migrated) {
            return;
        }

        try {
            $stmt = $this->db->query("SHOW COLUMNS FROM `exam_results` LIKE 'attempt_number'");
            if ($stmt !== false && $stmt->fetch() === false) {
                $this->db->exec('ALTER TABLE `exam_results` ADD COLUMN `attempt_number` INT UNSIGNED NOT NULL DEFAULT 1 AFTER `student_id`');
                $this->db->exec('ALTER TABLE `exam_results` ADD KEY `idx_attempt_number` (`exam_id`, `student_id`, `attempt_number`)');
                error_log('ExamController: Added attempt_number column to exam_results');
            }
            $migrated = true;
        } catch (\Throwable $e) {
            error_log('ExamController: Migration check error: ' . $e->getMessage());
        }
    }
}
