import { NextRequest, NextResponse } from "next/server" import { prisma } from "@/lib/db" export async function GET(request: NextRequest) { const childId = request.nextUrl.searchParams.get("childId") if (!childId) { return NextResponse.json({ error: "childId required" }, { status: 400 }) } const today = new Date() today.setHours(0, 0, 0, 0) const [totalSessions, todaySessions, milestones, fsrsCards, lastSession, weeklySessions, milestoneRecords, curriculumProgress, errorPatterns, allAttempts] = await Promise.all([ prisma.sessionLog.count({ where: { childId } }), prisma.sessionLog.count({ where: { childId, startedAt: { gte: today } }, }), prisma.milestone.count({ where: { childId } }), prisma.fsrsCard.findMany({ where: { childId } }), prisma.sessionLog.findFirst({ where: { childId }, orderBy: { startedAt: "desc" }, include: { skillAttempts: true }, }), prisma.sessionLog.findMany({ where: { childId, startedAt: { gte: new Date(Date.now() - 7 * 86400000) } }, orderBy: { startedAt: "asc" }, select: { startedAt: true, durationSec: true }, }), prisma.milestone.findMany({ where: { childId }, orderBy: { reachedAt: "desc" }, select: { skillCode: true, reachedAt: true }, }), prisma.curriculumProgress.findMany({ where: { childId }, orderBy: { stage: "asc" }, }), prisma.skillAttempt.findMany({ where: { errorType: { not: null }, sessionLog: { childId }, }, select: { errorType: true }, }), prisma.skillAttempt.findMany({ where: { correct: false, exerciseId: { not: null }, sessionLog: { childId }, }, select: { exerciseId: true, errorType: true }, }), ]) const todayMinutes = todaySessions > 0 ? await prisma.sessionLog.aggregate({ where: { childId, startedAt: { gte: today } }, _sum: { durationSec: true }, }).then((r) => Math.round((r._sum.durationSec ?? 0) / 60)) : 0 const totalSkills = fsrsCards.length const masteredSkills = fsrsCards.filter((c) => c.reps >= 5 && c.stability > 30).length const masteryPercent = totalSkills > 0 ? Math.round((masteredSkills / totalSkills) * 100) : 0 const weeklyData = weeklySessions.map((s) => ({ date: s.startedAt.toISOString().slice(0, 10), minutes: Math.round((s.durationSec ?? 0) / 60), })) const skillBreakdown = fsrsCards.map((c) => ({ skillCode: c.skillCode, stability: c.stability, difficulty: c.difficulty, reps: c.reps, due: c.dueAt.toISOString(), })) const streak = await calculateStreak(childId) const totalErrors = allAttempts.length const errorTypeCount = errorPatterns.reduce>((acc, e) => { const key = e.errorType || "sin-clasificar" acc[key] = (acc[key] || 0) + 1 return acc }, {}) const stageProgress = curriculumProgress.map((cp) => ({ topic: cp.topic, stage: cp.stage, completedAt: cp.completedAt?.toISOString() || null, })) return NextResponse.json({ totalSessions, todaySessions, todayMinutes, masteryPercent, masteredSkills, totalSkills, milestones, streak, weeklyData, skillBreakdown, milestoneData: milestoneRecords, stageProgress, errorTypeCount, totalErrors, lastSession: lastSession ? { startedAt: lastSession.startedAt.toISOString(), durationSec: lastSession.durationSec, skillsPracticed: lastSession.skillsPracticed, correctCount: lastSession.skillAttempts.filter((a) => a.correct).length, totalAttempts: lastSession.skillAttempts.length, } : null, }) } async function calculateStreak(childId: string): Promise { const logs = await prisma.sessionLog.findMany({ where: { childId }, orderBy: { startedAt: "desc" }, select: { startedAt: true }, }) if (logs.length === 0) return 0 const days = [ ...new Set(logs.map((l) => l.startedAt.toISOString().slice(0, 10))), ].sort() .reverse() let streak = 1 const today = new Date().toISOString().slice(0, 10) if (days[0] !== today && days[0] !== getYesterday()) return 0 for (let i = 1; i < days.length; i++) { const prev = new Date(days[i - 1]) const curr = new Date(days[i]) const diff = (prev.getTime() - curr.getTime()) / 86400000 if (Math.round(diff) === 1) { streak++ } else { break } } return streak } function getYesterday(): string { const d = new Date() d.setDate(d.getDate() - 1) return d.toISOString().slice(0, 10) }