import { NextRequest, NextResponse } from "next/server"; import { db } from "@/lib/db"; import { getServerSession } from "next-auth"; import { authOptions } from "@/lib/auth"; import { Prisma } from "@/lib/generated/prisma"; async function requireAdmin() { const session = await getServerSession(authOptions); if (!session || (session.user as any).role !== "ADMIN") { return null; } return session; } function calculateResult(answers: number[], questions: Array<{ correctAnswer: number }>) { let correct = 0; questions.forEach((question, index) => { if (answers[index] === question.correctAnswer) { correct += 1; } }); return { correct, score: questions.length > 0 ? Math.round((correct / questions.length) * 100) : 0, }; } function validateTierConfig(input: { goldWinnersCount: number; silverWinnersCount: number; bronzeWinnersCount: number; goldMinCorrect: number | null; silverMinCorrect: number | null; bronzeMinCorrect: number | null; }) { if (input.goldWinnersCount < 0 || input.silverWinnersCount < 0 || input.bronzeWinnersCount < 0) { return "Winner counts cannot be negative"; } if (input.goldWinnersCount + input.silverWinnersCount + input.bronzeWinnersCount <= 0) { return "At least one winner must be configured"; } if (input.goldWinnersCount > 0 && input.goldMinCorrect == null) { return "Gold minimum correct answers is required"; } if (input.silverWinnersCount > 0 && input.silverMinCorrect == null) { return "Silver minimum correct answers is required"; } if (input.bronzeWinnersCount > 0 && input.bronzeMinCorrect == null) { return "Bronze minimum correct answers is required"; } return null; } export async function PUT(req: NextRequest, { params }: { params: Promise<{ id: string }> }) { try { const session = await requireAdmin(); if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); const { id } = await params; const { date, windowStart, windowEnd, goldWinnersCount, silverWinnersCount, bronzeWinnersCount, goldMinCorrect, silverMinCorrect, bronzeMinCorrect, questions, } = await req.json(); const parsedInput = { goldWinnersCount: Number(goldWinnersCount), silverWinnersCount: Number(silverWinnersCount), bronzeWinnersCount: Number(bronzeWinnersCount), goldMinCorrect: goldMinCorrect == null ? null : Number(goldMinCorrect), silverMinCorrect: silverMinCorrect == null ? null : Number(silverMinCorrect), bronzeMinCorrect: bronzeMinCorrect == null ? null : Number(bronzeMinCorrect), }; const validationError = validateTierConfig(parsedInput); if (validationError) { return NextResponse.json({ error: validationError }, { status: 400 }); } if (!Array.isArray(questions) || questions.length === 0) { return NextResponse.json({ error: "At least one question is required" }, { status: 400 }); } const quiz = await db.dailyQuiz.findUnique({ where: { id }, include: { submissions: { select: { id: true, answers: true }, }, }, }); if (!quiz) return NextResponse.json({ error: "Quiz not found" }, { status: 404 }); if (quiz.isProcessed) { return NextResponse.json({ error: "Quiz can no longer be edited after lottery processing" }, { status: 400 }); } const normalizedQuestions = questions.map((q: any, index: number) => ({ questionText: q.questionText, options: q.options, correctAnswer: Number(q.correctAnswer), order: index, })); const updatedQuiz = await db.$transaction(async (tx) => { await tx.quizQuestion.deleteMany({ where: { quizId: id } }); const updated = await tx.dailyQuiz.update({ where: { id }, data: { date: new Date(`${date}T00:00:00.000Z`), windowStart: new Date(windowStart), windowEnd: new Date(windowEnd), goldWinnersCount: parsedInput.goldWinnersCount, silverWinnersCount: parsedInput.silverWinnersCount, bronzeWinnersCount: parsedInput.bronzeWinnersCount, goldMinCorrect: parsedInput.goldMinCorrect, silverMinCorrect: parsedInput.silverMinCorrect, bronzeMinCorrect: parsedInput.bronzeMinCorrect, questions: { create: normalizedQuestions, }, }, include: { questions: { orderBy: { order: "asc" } }, }, }); for (const submission of quiz.submissions) { const result = calculateResult(submission.answers, normalizedQuestions); await tx.quizSubmission.update({ where: { id: submission.id }, data: { score: result.score, correctAnswers: result.correct, }, }); } return updated; }); return NextResponse.json(updatedQuiz); } catch (error) { console.error("Failed to update quiz", error); if (error instanceof Prisma.PrismaClientKnownRequestError) { if (error.code === "P2002") { return NextResponse.json({ error: "Quiz date already exists" }, { status: 409 }); } } return NextResponse.json({ error: "Failed to update quiz" }, { status: 500 }); } } export async function DELETE(_: NextRequest, { params }: { params: Promise<{ id: string }> }) { const session = await requireAdmin(); if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); const { id } = await params; const quiz = await db.dailyQuiz.findUnique({ where: { id }, select: { id: true, isProcessed: true, }, }); if (!quiz) return NextResponse.json({ error: "Quiz not found" }, { status: 404 }); if (quiz.isProcessed) { return NextResponse.json({ error: "Quiz can no longer be deleted after lottery processing" }, { status: 400 }); } await db.dailyQuiz.delete({ where: { id } }); return NextResponse.json({ success: true }); }