import { NextResponse } from "next/server"; import { db } from "@/db"; import { quizSessions } from "@/db/schema"; import { eq, and } from "drizzle-orm"; import { getSession } from "@/lib/auth"; import { drawQuizQuestions, QUIZ_QUESTIONS, type QuizQuestion, } from "@/lib/quiz-questions"; const QUIZ_SESSION_EXPIRY_MS = 2 * 60 * 60 * 1000; // 2 hours (for in-progress quiz attempts only) interface StoredAnswer { questionId: string; answer: string; correct: boolean; } export async function GET() { const session = await getSession(); if (!session) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } // Quiz only needs to be passed once — no time-based expiry const [passedQuiz] = await db .select() .from(quizSessions) .where( and( eq(quizSessions.annotatorId, session.annotatorId), eq(quizSessions.passed, true), ), ) .orderBy(quizSessions.startedAt) .limit(1); if (passedQuiz) { return NextResponse.json({ hasPassedQuiz: true, quizSessionId: passedQuiz.id, }); } return NextResponse.json({ hasPassedQuiz: false }); } export async function POST(request: Request) { const session = await getSession(); if (!session) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const body = await request.json(); const { action } = body as { action: string }; if (action === "start") { return handleStart(session.annotatorId); } if (action === "answer") { const { quizSessionId, questionId, answer } = body as { quizSessionId: string; questionId: string; answer: string; }; if (!quizSessionId || !questionId || !answer) { return NextResponse.json( { error: "Missing required fields" }, { status: 400 }, ); } return handleAnswer( session.annotatorId, quizSessionId, questionId, answer, ); } return NextResponse.json({ error: "Invalid action" }, { status: 400 }); } async function handleStart(annotatorId: string) { const questions = drawQuizQuestions(8); const quizSessionId = crypto.randomUUID(); await db.insert(quizSessions).values({ id: quizSessionId, annotatorId, startedAt: new Date(), totalQuestions: 8, answers: "[]", }); return NextResponse.json({ quizSessionId, questions }); } async function handleAnswer( annotatorId: string, quizSessionId: string, questionId: string, answer: string, ) { const [quizSession] = await db .select() .from(quizSessions) .where(eq(quizSessions.id, quizSessionId)) .limit(1); if (!quizSession) { return NextResponse.json( { error: "Quiz session not found" }, { status: 404 }, ); } if (quizSession.annotatorId !== annotatorId) { return NextResponse.json({ error: "Unauthorized" }, { status: 403 }); } if (quizSession.completedAt) { return NextResponse.json( { error: "Quiz already completed" }, { status: 400 }, ); } const elapsed = Date.now() - quizSession.startedAt.getTime(); if (elapsed > QUIZ_SESSION_EXPIRY_MS) { return NextResponse.json({ error: "Quiz session expired" }, { status: 400 }); } const question = QUIZ_QUESTIONS.find( (q: QuizQuestion) => q.id === questionId, ); if (!question) { return NextResponse.json({ error: "Question not found" }, { status: 404 }); } const existingAnswers: StoredAnswer[] = JSON.parse(quizSession.answers); const alreadyAnswered = existingAnswers.some( (a) => a.questionId === questionId, ); if (alreadyAnswered) { return NextResponse.json( { error: "Question already answered" }, { status: 400 }, ); } const correct = answer === question.correctAnswer; const updatedAnswers: StoredAnswer[] = [ ...existingAnswers, { questionId, answer, correct }, ]; const allAnswered = updatedAnswers.length >= quizSession.totalQuestions; if (allAnswered) { const score = updatedAnswers.filter((a) => a.correct).length; const passed = score >= 7; await db .update(quizSessions) .set({ answers: JSON.stringify(updatedAnswers), completedAt: new Date(), score, passed, }) .where(eq(quizSessions.id, quizSessionId)); return NextResponse.json({ correct, correctAnswer: question.correctAnswer, explanation: question.explanation, score, passed, completed: true, }); } await db .update(quizSessions) .set({ answers: JSON.stringify(updatedAnswers) }) .where(eq(quizSessions.id, quizSessionId)); return NextResponse.json({ correct, correctAnswer: question.correctAnswer, explanation: question.explanation, completed: false, }); }