import { NextResponse } from 'next/server' import { auth } from '@/auth' import prisma from '@/lib/prisma' import { isCardMastered } from '@/lib/flashcards/sm2' export async function GET() { try { const session = await auth() if (!session?.user?.id) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } const userId = session.user.id const now = new Date() const [reviews, allCards] = await Promise.all([ prisma.flashcardReview.findMany({ where: { card: { deck: { userId } } }, select: { reviewedAt: true, grade: true }, orderBy: { reviewedAt: 'desc' }, take: 500, }), prisma.flashcard.findMany({ where: { deck: { userId } }, select: { id: true, interval: true, easinessFactor: true, front: true, deck: { select: { name: true } } }, }), ]) const heatmapMap = new Map() for (const r of reviews) { const day = r.reviewedAt.toISOString().slice(0, 10) heatmapMap.set(day, (heatmapMap.get(day) || 0) + 1) } const heatmap = Array.from(heatmapMap.entries()) .map(([date, count]) => ({ date, count })) .sort((a, b) => a.date.localeCompare(b.date)) .slice(-90) const totalCards = allCards.length const masteredCount = allCards.filter((c) => isCardMastered(c.interval)).length const retentionRate = totalCards > 0 ? Math.round((masteredCount / totalCards) * 100) : 0 const weekMs = 7 * 24 * 60 * 60 * 1000 const retentionByWeek: { week: string; rate: number }[] = [] for (let i = 7; i >= 0; i--) { const weekEnd = new Date(now.getTime() - i * weekMs) const weekStart = new Date(weekEnd.getTime() - weekMs) const cardsAtWeek = allCards.filter((c) => c.interval >= 7 * (8 - i)) const masteredAtWeek = cardsAtWeek.filter((c) => isCardMastered(c.interval)).length const rate = cardsAtWeek.length > 0 ? Math.round((masteredAtWeek / cardsAtWeek.length) * 100) : retentionRate retentionByWeek.push({ week: weekStart.toISOString().slice(0, 10), rate, }) } const difficultCards = [...allCards] .sort((a, b) => a.easinessFactor - b.easinessFactor) .slice(0, 5) .map((c) => ({ id: c.id, front: c.front.slice(0, 120), easinessFactor: c.easinessFactor, deckName: c.deck.name, })) return NextResponse.json({ heatmap, retentionRate, retentionByWeek, difficultCards, totalReviews: reviews.length, }) } catch (error) { console.error('[flashcards/stats]', error) return NextResponse.json({ error: 'Failed to load stats' }, { status: 500 }) } }