import { NextResponse } from 'next/server' import { auth } from '@/auth' import prisma from '@/lib/prisma' import { getChatProvider } from '@/lib/ai/factory' import { getSystemConfig } from '@/lib/config' import { redis } from '@/lib/redis' import { detectUserLanguage } from '@/lib/i18n/detect-user-language' const CACHE_TTL_SEC = 3600 /** * GET /api/briefing/sentiment * Analyzes the emotional tone of recent notes (last 7 days) using LLM. */ 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 locale = await detectUserLanguage() const cacheKey = `briefing:sentiment:${userId}:${locale}` try { const cached = await redis.get(cacheKey) if (cached) return NextResponse.json(JSON.parse(cached)) } catch {} const weekAgo = new Date() weekAgo.setDate(weekAgo.getDate() - 7) const recentNotes = await prisma.note.findMany({ where: { userId, trashedAt: null, isArchived: false, updatedAt: { gte: weekAgo }, }, select: { title: true, content: true }, take: 20, orderBy: { updatedAt: 'desc' }, }) if (recentNotes.length < 3) { return NextResponse.json({ available: false, reason: 'Not enough notes this week', }) } const snippets = recentNotes.map(n => { const text = n.content.replace(/<[^>]+>/g, ' ').replace(/ /g, ' ').replace(/\s+/g, ' ').trim() return `${n.title || ''}: ${text.slice(0, 200)}` }).join('\n---\n') try { const config = await getSystemConfig() const provider = getChatProvider(config) if (!provider) { return NextResponse.json({ available: false, reason: 'No AI provider' }) } const localeLabel = locale === 'fr' ? 'French' : locale === 'en' ? 'English' : locale const prompt = `Analyze the emotional patterns in these notes from the past week. Return ONLY valid JSON (no markdown, no code fences). Write "summary" and "topTopic" in ${localeLabel} (locale code: ${locale}). Keep dominantEmotion keys in English as listed. {"dominantEmotion":"focused|curious|enthusiastic|frustrated|calm|anxious|creative|reflective","sentimentScore":number from -1 to 1,"emotions":{"focused":number,"curious":number,"enthusiastic":number,"frustrated":number,"calm":number,"anxious":number,"creative":number,"reflective":number},"summary":"one sentence describing the emotional pattern","topTopic":"most discussed topic"} Notes: ${snippets.slice(0, 3000)}` const result = await provider.generateText(prompt) const jsonMatch = result.match(/\{[\s\S]*\}/) if (!jsonMatch) { return NextResponse.json({ available: false, reason: 'Parse error' }) } const parsed = JSON.parse(jsonMatch[0]) const payload = { available: true, ...parsed } try { await redis.setex(cacheKey, CACHE_TTL_SEC, JSON.stringify(payload)) } catch {} return NextResponse.json(payload) } catch (error) { console.error('[briefing/sentiment]', error) return NextResponse.json({ available: false, reason: 'Analysis failed' }) } }