import { NextResponse } from 'next/server'; import { GoogleGenerativeAI } from '@google/generative-ai'; import { supabase } from '@/lib/supabase'; import { isTrainActiveOnDate } from '@/lib/circulation'; export async function POST(req: Request) { try { const body = await req.json(); const { query, regionSlug, currentDate } = body; if (!query) { return NextResponse.json({ error: "No query provided" }, { status: 400 }); } let parsedIntent: any = { intent: 'unknown' }; const apiKey = process.env.GEMINI_API_KEY; if (apiKey) { try { const genAI = new GoogleGenerativeAI(apiKey); const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' }); const prompt = ` Tu es un assistant de recherche pour un site régional de trains (TER). Date actuelle : ${currentDate}. L'utilisateur recherche : "${query}" Analyse l'intention de l'utilisateur et retourne UNIQUEMENT un objet JSON avec cette structure : { "intent": "train" | "itineraire" | "fiche_horaire" | "gare" | "unknown", "trainNumber": "numéro à 6 chiffres (ex: 891811) s'il cherche un train précis", "date": "YYYY-MM-DD s'il précise une date ou un jour (ex: demain, lundi, 24 sept), sinon null", "departureStation": "nom de la gare de départ s'il cherche un itinéraire", "arrivalStation": "nom de la gare d'arrivée s'il cherche un itinéraire", "station": "nom de la gare s'il cherche une gare ou ses fiches horaires", "line": "nom de la ligne (ex: Dijon Besançon) s'il cherche une fiche horaire de ligne" } Règles : - "train 891811 demain" -> intent: train, trainNumber: "891811", date: [date de demain] - "horaires dijon besancon" ou "itinéraire de dijon à seurre" -> intent: itineraire, departureStation: "Dijon", arrivalStation: "Besancon" (ou "Seurre") - "fiches horaires beaune" -> intent: fiche_horaire, station: "Beaune" - "gare de dijon" -> intent: gare, station: "Dijon" `; const result = await model.generateContent(prompt); const text = result.response.text(); const jsonMatch = text.match(/\{[\s\S]*\}/); if (jsonMatch) { parsedIntent = JSON.parse(jsonMatch[0]); } } catch (err) { console.error("AI Error:", err); } } // Fallback if AI fails or no API key if (parsedIntent.intent === 'unknown') { const q = query.toLowerCase(); const numMatch = q.match(/(\d{5,6})/); let fallbackDate = null; const days = ['dimanche', 'lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', 'samedi']; const foundDay = days.find(d => q.includes(d)); if (q.includes('demain')) { const d = new Date(currentDate); d.setDate(d.getDate() + 1); fallbackDate = d.toISOString().split('T')[0]; } else if (q.includes('aujourd\'hui')) { fallbackDate = currentDate.split('T')[0]; } else if (foundDay) { const currentDay = new Date(currentDate).getDay(); const targetDay = days.indexOf(foundDay); let diff = targetDay - currentDay; if (diff < 0) diff += 7; // next occurrence if (diff === 0 && q.includes('prochain')) diff += 7; const targetDate = new Date(currentDate); targetDate.setDate(targetDate.getDate() + diff); fallbackDate = targetDate.toISOString().split('T')[0]; } if (numMatch) { parsedIntent = { intent: 'train', trainNumber: numMatch[1], date: fallbackDate }; } else if (q.includes('itineraire') || q.includes('itinéraire') || q.includes('trajet')) { const parts = q.replace(/(itinéraire|itineraire|trajet)s? (de )?/i, '').split(/ à | vers /); if (parts.length === 2) { parsedIntent = { intent: 'itineraire', departureStation: parts[0].trim(), arrivalStation: parts[1].trim() }; } } else if (q.includes('fiche') || q.includes('horaire')) { if (q.includes('gare')) { parsedIntent = { intent: 'fiche_horaire', station: q.replace(/fiches? horaires?( de la)? gare( de)?/i, '').trim() }; } else { const parts = q.split(/ à | vers /); if (parts.length === 2) { parsedIntent = { intent: 'itineraire', departureStation: parts[0].replace(/horaires?( de )?/i, '').trim(), arrivalStation: parts[1].trim() }; } } } else if (q.includes('gare')) { parsedIntent = { intent: 'gare', station: q.replace(/gares?( de)?/i, '').trim() }; } else { // Ultimate fallback: "Dijon à Seurre" const parts = q.split(/ à | vers /); if (parts.length === 2) { parsedIntent = { intent: 'itineraire', departureStation: parts[0].trim(), arrivalStation: parts[1].trim() }; } } } const todayIso = new Date(currentDate).toISOString().split('T')[0]; const targetIsoDate = parsedIntent.date || todayIso; const targetDateFR = new Date(targetIsoDate).toLocaleDateString('fr-FR'); let actionResponse: any = { type: 'unknown', message: 'Je n\'ai pas bien compris votre recherche.' }; if (parsedIntent.intent === 'train' && parsedIntent.trainNumber) { const { data: trains } = await supabase .from('horaires') .select('*') .eq('numero_train', parsedIntent.trainNumber) .eq('actif', true) .is('deleted_at', null); if (!trains || trains.length === 0) { actionResponse = { type: 'error', message: `Désolé, nous n'avons pas trouvé le train n°${parsedIntent.trainNumber} dans notre base de données.` }; } else { const isActive = trains.some(train => isTrainActiveOnDate(train, targetIsoDate)); if (!isActive) { actionResponse = { type: 'error', message: `Le train ${parsedIntent.trainNumber} ne circule pas le ${targetDateFR}.` }; } else { const params = new URLSearchParams(); if (parsedIntent.date) { const parts = parsedIntent.date.split('-'); if (parts.length === 3) { params.set('date', `${parts[2]}/${parts[1]}/${parts[0]}`); } } const queryString = params.toString() ? `?${params.toString()}` : ''; actionResponse = { type: 'redirect', url: `/${regionSlug}/horaires/train/${parsedIntent.trainNumber}${queryString}`, message: `Je vous redirige vers les détails du train ${parsedIntent.trainNumber} pour le ${targetDateFR}.` }; } } } else if (parsedIntent.intent === 'itineraire' && parsedIntent.departureStation && parsedIntent.arrivalStation) { const params = new URLSearchParams(); params.set('departure', parsedIntent.departureStation); params.set('arrival', parsedIntent.arrivalStation); if (parsedIntent.date) { const parts = parsedIntent.date.split('-'); if (parts.length === 3) { params.set('outwardDate', `Lun. ${parts[2]}/${parts[1]}`); // basic format } } actionResponse = { type: 'redirect', url: `/${regionSlug}/horaires?${params.toString()}`, message: `Recherche de l'itinéraire de ${parsedIntent.departureStation} à ${parsedIntent.arrivalStation}...` }; } else if (parsedIntent.intent === 'fiche_horaire') { const params = new URLSearchParams(); if (parsedIntent.station) params.set('gare', parsedIntent.station); if (parsedIntent.line) params.set('ligne', parsedIntent.line); actionResponse = { type: 'redirect', url: `/${regionSlug}/se-deplacer/fiches-horaires?${params.toString()}`, message: `Ouverture des fiches horaires...` }; } else if (parsedIntent.intent === 'gare' && parsedIntent.station) { const searchName = parsedIntent.station; const { data: gares } = await supabase .from('gares') .select('nom') .ilike('nom', `%${searchName}%`) .is('deleted_at', null) .order('nom') .limit(5); if (!gares || gares.length === 0) { actionResponse = { type: 'error', message: `Désolé, nous n'avons trouvé aucune gare correspondant à "${searchName}".` }; } else if (gares.length === 1 && gares[0].nom.toLowerCase() === searchName.toLowerCase()) { actionResponse = { type: 'redirect', url: `/${regionSlug}/se-deplacer/gares?gare=${encodeURIComponent(gares[0].nom)}`, message: `Ouverture de la page de la gare de ${gares[0].nom}...` }; } else { // Multiple matches or partial match actionResponse = { type: 'options', message: `Plusieurs gares correspondent à votre recherche. Laquelle cherchez-vous ?`, options: gares.map(g => ({ title: `Gare de ${g.nom}`, url: `/${regionSlug}/se-deplacer/gares?gare=${encodeURIComponent(g.nom)}`, icon: 'place' })) }; } } return NextResponse.json(actionResponse); } catch (error) { console.error(error); return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); } }