import { useEffect, useRef } from 'react'
import { useNavigate } from '@tanstack/react-router'
import { cn } from '@/lib/utils'
import { useTestStore } from '@/store/test-store'
import { useRequireAuth } from '@/hooks/use-require-auth'
import { useAuthStore } from '@/store/auth-store'
import { saveTestResult } from '@/lib/progress-service'
import { useAwardActivity } from '@/hooks/use-gamification'
import { XP_REWARDS } from '@/lib/gamification-service'
const ANSWER_LABELS = ['A', 'B', 'C', 'D']
function formatTime(s: number) {
const m = Math.floor(s / 60)
const sec = s % 60
if (m === 0) return `${sec}s`
return `${m}m ${sec}s`
}
export function TestResult() {
const navigate = useNavigate()
const { testId, testName, parts, answers, timeUsed, reset } = useTestStore()
const { isAuthenticated, isLoading } = useRequireAuth()
const user = useAuthStore((s) => s.user)
const savedRef = useRef(false)
const { mutate: awardActivity } = useAwardActivity()
// Flatten all questions across parts
const allQuestions = parts.flatMap(p => p.questions)
useEffect(() => {
if (isLoading) return
if (!isAuthenticated) navigate({ to: '/toeic' })
}, [isLoading, isAuthenticated, navigate])
useEffect(() => {
if (!user || savedRef.current || allQuestions.length === 0) return
savedRef.current = true
const correct = allQuestions.filter(q => answers[q.id] === q.correctAnswer).length
awardActivity({ xp: XP_REWARDS.test })
saveTestResult(user.id, {
testId,
selectedParts: parts.map(p => p.partNumber),
score: correct,
total: allQuestions.length,
timeUsed,
answers: allQuestions.map(q => ({
questionId: q.id,
selected: answers[q.id] ?? null,
correct: answers[q.id] === q.correctAnswer,
})),
})
}, [user, allQuestions.length])
if (allQuestions.length === 0) {
return (
Không có dữ liệu bài thi.
)
}
const correct = allQuestions.filter(q => answers[q.id] === q.correctAnswer).length
const wrong = allQuestions.filter(q => answers[q.id] !== null && answers[q.id] !== undefined && answers[q.id] !== q.correctAnswer).length
const skipped = allQuestions.filter(q => answers[q.id] === null || answers[q.id] === undefined).length
const total = allQuestions.length
const percent = total > 0 ? Math.round((correct / total) * 100) : 0
const circumference = 2 * Math.PI * 52
const offset = circumference - (percent / 100) * circumference
return (
{/* Score header */}
{correct}/{total}
điểm
{percent >= 80 ? 'Xuất sắc!' : percent >= 60 ? 'Hoàn thành!' : 'Cố gắng hơn nhé!'}
{testName}
{[
{ label: 'Đúng', value: correct, cls: 'bg-green-50 border-green-100 text-green-600' },
{ label: 'Sai', value: wrong, cls: 'bg-red-50 border-red-100 text-red-600' },
{ label: 'Bỏ qua', value: skipped, cls: 'bg-slate-50 border-slate-200 text-slate-500' },
{ label: 'Thời gian', value: formatTime(timeUsed), cls: 'bg-blue-50 border-blue-100 text-blue-600' },
].map(({ label, value, cls }) => (
))}
{/* Answer review grouped by part */}
{parts.map(part => (
Part {part.partNumber} — {part.partName}
{part.questions.map((q, i) => {
const userAnswer = answers[q.id] ?? null
const isCorrect = userAnswer === q.correctAnswer
const isSkipped = userAnswer === null || userAnswer === undefined
return (
{i + 1}
{q.text &&
{q.text}
}
{q.options.map((opt, j) => (
{ANSWER_LABELS[j]}. {opt}
))}
{q.explanation && (
Giải thích: {q.explanation}
)}
{isCorrect
? check_circle
: isSkipped
? remove_circle
: cancel}
)
})}
))}
)
}