import { NextRequest, NextResponse } from 'next/server' import { Prisma } from '@prisma/client' import prisma from '@/lib/prisma' import { auth } from '@/auth' import { revalidatePath } from 'next/cache' import { PatchNotebookSchema } from '@/lib/validators' async function getDescendantIds(notebookId: string): Promise { const ids: string[] = [] const children = await prisma.notebook.findMany({ where: { parentId: notebookId }, select: { id: true }, }) for (const child of children) { ids.push(child.id) ids.push(...await getDescendantIds(child.id)) } return ids } export async function PATCH( request: NextRequest, { params }: { params: Promise<{ id: string }> } ) { const session = await auth() if (!session?.user?.id) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } try { const { id } = await params const body = await request.json() const parsed = PatchNotebookSchema.safeParse(body) if (!parsed.success) { return NextResponse.json( { success: false, error: 'Invalid request body', details: parsed.error.flatten() }, { status: 400 } ) } const { name, icon, color, order, trashedAt, parentId } = parsed.data const existing = await prisma.notebook.findUnique({ where: { id }, select: { userId: true } }) if (!existing) { return NextResponse.json( { success: false, error: 'Notebook not found' }, { status: 404 } ) } if (existing.userId !== session.user.id) { return NextResponse.json( { success: false, error: 'Forbidden' }, { status: 403 } ) } if (parentId !== undefined) { if (parentId !== null) { if (parentId === id) { return NextResponse.json( { success: false, error: 'A notebook cannot be its own parent' }, { status: 400 } ) } const target = await prisma.notebook.findFirst({ where: { id: parentId, userId: session.user.id } }) if (!target) { return NextResponse.json( { success: false, error: 'Parent notebook not found' }, { status: 404 } ) } const descendants = await getDescendantIds(id) if (descendants.includes(parentId)) { return NextResponse.json( { success: false, error: 'Cannot move a notebook into its own descendant' }, { status: 400 } ) } } } const updateData: Prisma.NotebookUpdateInput = {} if (name !== undefined) updateData.name = name if (icon !== undefined) updateData.icon = icon if (color !== undefined) updateData.color = color if (order !== undefined) updateData.order = order if (trashedAt !== undefined) updateData.trashedAt = trashedAt ? new Date(trashedAt) : null if (parentId !== undefined) updateData.parentId = parentId if (trashedAt !== undefined) { const descendantIds = await getDescendantIds(id) const allIds = [id, ...descendantIds] await prisma.notebook.updateMany({ where: { id: { in: allIds }, userId: session.user.id }, data: { trashedAt }, }) } else { await prisma.notebook.update({ where: { id }, data: updateData, }) } try { revalidatePath('/home') } catch {} return NextResponse.json({ success: true }) } catch (error) { console.error('Error updating notebook:', error) return NextResponse.json( { success: false, error: 'Failed to update notebook' }, { status: 500 } ) } } export async function DELETE( request: NextRequest, { params }: { params: Promise<{ id: string }> } ) { const session = await auth() if (!session?.user?.id) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } try { const { id } = await params const notebook = await prisma.notebook.findUnique({ where: { id }, select: { userId: true, name: true } }) if (!notebook) { return NextResponse.json( { success: false, error: 'Notebook not found' }, { status: 404 } ) } if (notebook.userId !== session.user.id) { return NextResponse.json( { success: false, error: 'Forbidden' }, { status: 403 } ) } await prisma.notebook.delete({ where: { id } }) try { revalidatePath('/home') } catch {} return NextResponse.json({ success: true, message: `Notebook "${notebook.name}" permanently deleted`, }) } catch (error) { console.error('Error deleting notebook:', error) return NextResponse.json( { success: false, error: 'Failed to delete notebook' }, { status: 500 } ) } }