import { NextResponse } from 'next/server' import { auth } from '@/auth' import prisma from '@/lib/prisma' import { agentSuggestionService } from '@/lib/ai/services/agent-suggestion.service' import { bridgeNotesService } from '@/lib/ai/services/bridge-notes.service' import { gmailScannerService } from '@/lib/integrations/gmail-scanner.service' import { hasUserAiConsent } from '@/lib/consent/server-consent' import { getSystemConfig } from '@/lib/config' import { getChatProvider } from '@/lib/ai/factory' function excerptNoteContent(content: string | null | undefined, max = 200): string { if (!content) return '' const plain = content.replace(/<[^>]+>/g, ' ').replace(/ /g, ' ').replace(/\s+/g, ' ').trim() if (plain.length <= max) return plain return `${plain.slice(0, max)}…` } async function fetchWritingActivity(userId: string, since: Date) { const rows = await prisma.$queryRaw>` SELECT to_char("updatedAt" AT TIME ZONE 'UTC', 'YYYY-MM-DD') AS date, COUNT(*)::int AS count FROM "Note" WHERE "userId" = ${userId} AND "trashedAt" IS NULL AND "updatedAt" >= ${since} GROUP BY 1 ORDER BY 1 ASC ` return rows.map(r => ({ date: r.date, count: Number(r.count) })) } /** * GET /api/briefing — agrégat rapide pour le tableau de bord. * Pas de génération IA synchrone ; les pistes sont dans GET /api/briefing/paths. */ export async function GET() { 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 ninetyDaysAgo = new Date(now) ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90) const [aiConsent, aiSettings, systemConfig] = await Promise.all([ hasUserAiConsent(), prisma.userAISettings.findUnique({ where: { userId }, select: { memoryEcho: true }, }), getSystemConfig(), ]) const aiProviderReady = !!getChatProvider(systemConfig) const memoryEchoEnabled = aiSettings?.memoryEcho ?? true const aiActive = aiConsent && aiProviderReady && memoryEchoEnabled const [ recentNotes, inboxCount, dueFlashcards, upcomingReminders, unviewedInsights, recentAgentActions, pinnedNotes, writingActivity, gmailStatus, agentSuggestions, bridgeSuggestions, ] = await Promise.all([ prisma.note.findMany({ where: { userId, trashedAt: null, isArchived: false }, select: { id: true, title: true, content: true, color: true, notebookId: true, updatedAt: true, createdAt: true, }, orderBy: { updatedAt: 'desc' }, take: 6, }), prisma.note.count({ where: { userId, notebookId: null, isArchived: false, trashedAt: null }, }), prisma.flashcard.count({ where: { deck: { userId }, nextReviewAt: { lte: now } }, }), prisma.note.findMany({ where: { userId, reminder: { gte: now }, isReminderDone: false, trashedAt: null, }, select: { id: true, title: true, reminder: true, notebookId: true }, orderBy: { reminder: 'asc' }, take: 5, }), prisma.memoryEchoInsight.findMany({ where: { userId, viewed: false, dismissed: false }, select: { id: true, insight: true, similarityScore: true, insightDate: true, viewed: true, note1: { select: { id: true, title: true, content: true } }, note2: { select: { id: true, title: true, content: true } }, }, orderBy: { insightDate: 'desc' }, take: 5, }).catch(() => []), prisma.agentAction.findMany({ where: { agent: { userId }, status: 'completed', createdAt: { gte: new Date(now.getTime() - 48 * 60 * 60 * 1000) }, }, select: { id: true, result: true, createdAt: true, agent: { select: { id: true, name: true, type: true } }, }, orderBy: { createdAt: 'desc' }, take: 3, }).catch(() => []), prisma.note.findMany({ where: { userId, isPinned: true, trashedAt: null, isArchived: false }, select: { id: true, title: true, notebookId: true, updatedAt: true }, orderBy: { updatedAt: 'desc' }, take: 5, }), fetchWritingActivity(userId, ninetyDaysAgo).catch(() => []), gmailScannerService.getStatus(userId).catch(() => ({ connected: false, recentCaptures: 0, })), agentSuggestionService.getPending(userId, 3).catch(() => []), bridgeNotesService.getBridgeSuggestions(userId).catch(() => []), ]) let insightRows = unviewedInsights if (insightRows.length < 5) { const viewedRecent = await prisma.memoryEchoInsight.findMany({ where: { userId, dismissed: false, viewed: true, id: { notIn: insightRows.map(i => i.id) }, }, select: { id: true, insight: true, similarityScore: true, insightDate: true, viewed: true, note1: { select: { id: true, title: true, content: true } }, note2: { select: { id: true, title: true, content: true } }, }, orderBy: { insightDate: 'desc' }, take: 5 - insightRows.length, }).catch(() => []) insightRows = [...insightRows, ...viewedRecent] } const notebookIds = [...new Set(recentNotes.map(n => n.notebookId).filter(Boolean))] as string[] const notebooks = notebookIds.length > 0 ? await prisma.notebook.findMany({ where: { id: { in: notebookIds } }, select: { id: true, name: true, color: true, icon: true }, }) : [] const notebookMap = new Map(notebooks.map(n => [n.id, n])) return NextResponse.json({ recentNotes: recentNotes.map(n => ({ ...n, notebook: n.notebookId ? notebookMap.get(n.notebookId) || null : null, })), inboxCount, dueFlashcards, upcomingReminders: upcomingReminders.map(r => ({ id: r.id, title: r.title, reminder: r.reminder?.toISOString() || null, notebookId: r.notebookId, })), insights: insightRows.map(i => ({ id: i.id, insight: i.insight, score: i.similarityScore, date: i.insightDate.toISOString(), viewed: i.viewed, note1: { id: i.note1.id, title: i.note1.title }, note2: { id: i.note2.id, title: i.note2.title }, note1Excerpt: excerptNoteContent(i.note1.content), note2Excerpt: excerptNoteContent(i.note2.content), })), bridgeSuggestions: bridgeSuggestions.slice(0, 3).map(s => ({ clusterAId: s.clusterAId, clusterBId: s.clusterBId, clusterAName: s.clusterAName, clusterBName: s.clusterBName, suggestedTitle: s.suggestedTitle, suggestedContent: s.suggestedContent, justification: s.justification, })), ai: { consent: aiConsent, memoryEchoEnabled, providerReady: aiProviderReady, active: aiActive, }, agentActions: recentAgentActions.map(a => ({ id: a.id, agentName: a.agent?.name || 'Agent', agentType: a.agent?.type || 'custom', result: a.result ? String(a.result).slice(0, 200) : null, createdAt: a.createdAt.toISOString(), })), gmail: gmailStatus, pinnedNotes: pinnedNotes.map(n => ({ id: n.id, title: n.title, notebookId: n.notebookId, updatedAt: n.updatedAt.toISOString(), })), writingActivity, agentSuggestions: agentSuggestions.map(s => ({ id: s.id, topic: s.topic, reason: s.reason, suggestedType: s.suggestedType, suggestedFrequency: s.suggestedFrequency, relatedNoteCount: (() => { try { return JSON.parse(s.relatedNoteIds).length } catch { return 0 } })(), clusterId: s.clusterId, })), }) }