import { streamText, UIMessage } from 'ai'
import { getChatProvider } from '@/lib/ai/factory'
import { getSystemConfig } from '@/lib/config'
import { semanticSearchService } from '@/lib/ai/services/semantic-search.service'
import { prisma } from '@/lib/prisma'
import { auth } from '@/auth'
import { loadTranslations, getTranslationValue, SupportedLanguage } from '@/lib/i18n'
import { toolRegistry } from '@/lib/ai/tools'
import { stepCountIs } from 'ai'

export const maxDuration = 60

/**
 * Extract text content from a UIMessage's parts array.
 */
function extractTextFromUIMessage(msg: { parts?: Array<{ type: string; text?: string }>; content?: string }): string {
  if (typeof msg.content === 'string') return msg.content
  if (msg.parts && Array.isArray(msg.parts)) {
    return msg.parts
      .filter((p) => p.type === 'text' && typeof p.text === 'string')
      .map((p) => p.text!)
      .join('')
  }
  return ''
}

/**
 * Convert an array of UIMessages (from the client) to CoreMessage[] for streamText.
 */
function toCoreMessages(uiMessages: UIMessage[]): Array<{ role: 'user' | 'assistant'; content: string }> {
  return uiMessages
    .filter((m) => m.role === 'user' || m.role === 'assistant')
    .map((m) => ({
      role: m.role as 'user' | 'assistant',
      content: extractTextFromUIMessage(m),
    }))
    .filter((m) => m.content.length > 0)
}

export async function POST(req: Request) {
  // 1. Auth check
  const session = await auth()
  if (!session?.user?.id) {
    return new Response('Unauthorized', { status: 401 })
  }
  const userId = session.user.id

  // 2. Parse request body — messages arrive as UIMessage[] from DefaultChatTransport
  const body = await req.json()
  const { messages: rawMessages, conversationId, notebookId, language, webSearch } = body as {
    messages: UIMessage[]
    conversationId?: string
    notebookId?: string
    language?: string
    webSearch?: boolean
  }

  // Convert UIMessages to CoreMessages for streamText
  const incomingMessages = toCoreMessages(rawMessages)

  // 3. Manage conversation (create or fetch)
  let conversation: { id: string; messages: Array<{ role: string; content: string }> }

  if (conversationId) {
    const existing = await prisma.conversation.findUnique({
      where: { id: conversationId, userId },
      include: { messages: { orderBy: { createdAt: 'asc' } } },
    })
    if (!existing) {
      return new Response('Conversation not found', { status: 404 })
    }
    conversation = existing
  } else {
    const userMessage = incomingMessages[incomingMessages.length - 1]?.content || 'New conversation'
    const created = await prisma.conversation.create({
      data: {
        userId,
        notebookId: notebookId || null,
        title: userMessage.substring(0, 50) + (userMessage.length > 50 ? '...' : ''),
      },
      include: { messages: true },
    })
    conversation = created
  }

  // 4. RAG retrieval
  const currentMessage = incomingMessages[incomingMessages.length - 1]?.content || ''

  // Load translations for the requested language
  const lang = (language || 'en') as SupportedLanguage
  const translations = await loadTranslations(lang)
  const untitledText = getTranslationValue(translations, 'notes.untitled') || 'Untitled'

  // If a notebook is selected, fetch its recent notes directly as context
  // This ensures the AI always has access to the notebook content,
  // even for vague queries like "what's in this notebook?"
  let notebookContext = ''
  if (notebookId) {
    const notebookNotes = await prisma.note.findMany({
      where: {
        notebookId,
        userId,
        trashedAt: null,
      },
      orderBy: { updatedAt: 'desc' },
      take: 20,
      select: { id: true, title: true, content: true, updatedAt: true },
    })
    if (notebookNotes.length > 0) {
      notebookContext = notebookNotes
        .map(n => `NOTE [${n.title || untitledText}] (updated ${n.updatedAt.toLocaleDateString()}):\n${(n.content || '').substring(0, 1500)}`)
        .join('\n\n---\n\n')
    }
  }

  // Also run semantic search for the specific query
  const searchResults = await semanticSearchService.search(currentMessage, {
    notebookId,
    limit: notebookId ? 10 : 5,
    threshold: notebookId ? 0.3 : 0.5,
    defaultTitle: untitledText,
  })

  const searchNotes = searchResults
    .map((r) => `NOTE [${r.title || untitledText}]: ${r.content}`)
    .join('\n\n---\n\n')

  // Combine: full notebook context + semantic search results (deduplicated)
  const contextNotes = [notebookContext, searchNotes].filter(Boolean).join('\n\n---\n\n')

  // 5. System prompt synthesis with RAG context
  // Language-aware prompts to avoid forcing French responses
  // Note: lang is already declared above when loading translations
  const promptLang: Record<string, { contextWithNotes: string; contextNoNotes: string; system: string }> = {
    en: {
      contextWithNotes: `## User's notes\n\n${contextNotes}\n\nWhen using info from the notes above, cite the source note title in parentheses, e.g.: "Deployment is done via Docker (💻 Development Guide)". Don't copy word for word — rephrase. If the notes don't cover the topic, say so and supplement with your general knowledge.`,
      contextNoNotes: "No relevant notes found for this question. Answer with your general knowledge.",
      system: `You are the AI assistant of Memento. The user asks you questions about their projects, technical docs, and notes. You must respond in a structured and helpful way.

## Format rules
- Use markdown freely: headings (##, ###), lists, code blocks, bold, tables — anything that makes the response readable.
- Structure your response with sections for technical questions or complex topics.
- For simple, short questions, a direct paragraph is enough.

## Tone rules
- Natural tone, neither corporate nor too casual.
- No unnecessary intro phrases ("Here's what I found", "Based on your notes"). Answer directly.
- No upsell questions at the end ("Would you like me to...", "Do you want..."). If you have useful additional info, just give it.
- If the user says "Momento" they mean Memento (this app).

## Available tools
You have access to these tools for deeper research:
- **note_search**: Search the user's notes by keyword or meaning. Use when the initial context above is insufficient or when the user asks about specific content in their notes. If a notebook is selected, pass its ID to restrict results.
- **note_read**: Read a specific note by ID. Use when note_search returns a note you need the full content of.
- **web_search**: Search the web for information. Use when the user asks about something not in their notes.
- **web_scrape**: Scrape a web page and return its content as markdown. Use when web_search returns a URL you need to read.

## Tool usage rules
- You already have context from the user's notes above. Only use tools if you need more specific or additional information.
- Never invent note IDs, URLs, or notebook IDs. Use the IDs provided in the context or from tool results.
- For simple conversational questions (greetings, opinions, general knowledge), answer directly without using any tools.`,
    },
    fr: {
      contextWithNotes: `## Notes de l'utilisateur\n\n${contextNotes}\n\nQuand tu utilises une info venant des notes ci-dessus, cite le titre de la note source entre parenthèses, ex: "Le déploiement se fait via Docker (💻 Development Guide)". Ne recopie pas mot pour mot — reformule. Si les notes ne couvrent pas le sujet, dis-le et complète avec tes connaissances générales.`,
      contextNoNotes: "Aucune note pertinente trouvée pour cette question. Réponds avec tes connaissances générales.",
      system: `Tu es l'assistant IA de Memento. L'utilisateur te pose des questions sur ses projets, sa doc technique, ses notes. Tu dois répondre de façon structurée et utile.

## Règles de format
- Utilise le markdown librement : titres (##, ###), listes, code blocks, gras, tables — tout ce qui rend la réponse lisible.
- Structure ta réponse avec des sections quand c'est une question technique ou un sujet complexe.
- Pour les questions simples et courtes, un paragraphe direct suffit.

## Règles de ton
- Ton naturel, ni corporate ni trop familier.
- Pas de phrase d'intro inutile ("Voici ce que j'ai trouvé", "Basé sur vos notes"). Réponds directement.
- Pas de question upsell à la fin ("Souhaitez-vous que je...", "Acceptez-vous que..."). Si tu as une info complémentaire utile, donne-la.
- Si l'utilisateur dit "Momento" il parle de Memento (cette application).

## Outils disponibles
Tu as accès à ces outils pour des recherches approfondies :
- **note_search** : Cherche dans les notes de l'utilisateur par mot-clé ou sens. Utilise quand le contexte initial ci-dessus est insuffisant ou quand l'utilisateur demande du contenu spécifique dans ses notes. Si un carnet est sélectionné, passe son ID pour restreindre les résultats.
- **note_read** : Lit une note spécifique par son ID. Utilise quand note_search retourne une note dont tu as besoin du contenu complet.
- **web_search** : Recherche sur le web. Utilise quand l'utilisateur demande quelque chose qui n'est pas dans ses notes.
- **web_scrape** : Scrape une page web et retourne son contenu en markdown. Utilise quand web_search retourne une URL que tu veux lire.

## Règles d'utilisation des outils
- Tu as déjà du contexte des notes de l'utilisateur ci-dessus. Utilise les outils seulement si tu as besoin d'informations plus spécifiques.
- N'invente jamais d'IDs de notes, d'URLs ou d'IDs de carnet. Utilise les IDs fournis dans le contexte ou les résultats d'outils.
- Pour les questions conversationnelles simples (salutations, opinions, connaissances générales), réponds directement sans utiliser d'outils.`,
    },
    fa: {
      contextWithNotes: `## یادداشت‌های کاربر\n\n${contextNotes}\n\nهنگام استفاده از اطلاعات یادداشت‌های بالا، عنوان یادداشت منبع را در پرانتز ذکر کنید. کپی نکنید — بازنویسی کنید. اگر یادداشت‌ها موضوع را پوشش نمی‌دهند، بگویید و با دانش عمومی خود تکمیل کنید.`,
      contextNoNotes: "هیچ یادداشت مرتبطی برای این سؤال یافت نشد. با دانش عمومی خود پاسخ دهید.",
      system: `شما دستیار هوش مصنوعی Memento هستید. کاربر از شما درباره پروژه‌ها، مستندات فنی و یادداشت‌هایش سؤال می‌کند. باید به شکلی ساختاریافته و مفید پاسخ دهید.

## قوانین قالب‌بندی
- از مارک‌داون آزادانه استفاده کنید: عناوین (##, ###)، لیست‌ها، بلوک‌های کد، پررنگ، جداول.
- برای سؤالات فنی یا موضوعات پیچیده، پاسخ خود را بخش‌بندی کنید.
- برای سؤالات ساده و کوتاه، یک پاراگراف مستقیم کافی است.

## قوانین لحن
- لحن طبیعی، نه رسمی بیش از حد و نه خیلی غیررسمی.
- بدون جمله مقدمه اضافی. مستقیم پاسخ دهید.
- بدون سؤال فروشی در انتها. اگر اطلاعات تکمیلی مفید دارید، مستقیم بدهید.
- اگر کاربر "Momento" می‌گوید، منظورش Memento (این برنامه) است.

## ابزارهای موجود
- **note_search**: جستجو در یادداشت‌های کاربر با کلیدواژه یا معنی. زمانی استفاده کنید که زمینه اولیه کافی نباشد. اگر دفترچه انتخاب شده، شناسه آن را ارسال کنید.
- **note_read**: خواندن یک یادداشت خاص با شناسه. زمانی استفاده کنید که note_search یادداشتی برگرداند که محتوای کامل آن را نیاز دارید.
- **web_search**: جستجو در وب. زمانی استفاده کنید که کاربر درباره چیزی خارج از یادداشت‌هایش می‌پرسد.
- **web_scrape**: استخراج محتوای صفحه وب. زمانی استفاده کنید که web_search نشانی‌ای برگرداند که می‌خواهید بخوانید.

## قوانین استفاده از ابزارها
- شما از قبل زمینه‌ای از یادداشت‌های کاربر دارید. فقط در صورت نیاز به اطلاعات بیشتر از ابزارها استفاده کنید.
- هرگز شناسه یادداشت، نشانی یا شناسه دفترچه نسازید. از شناسه‌های موجود در زمینه یا نتایج ابزار استفاده کنید.
- برای سؤالات مکالمه‌ای ساده (سلام، نظرات، دانش عمومی)، مستقیم پاسخ دهید.`,
    },
    es: {
      contextWithNotes: `## Notas del usuario\n\n${contextNotes}\n\nCuando uses información de las notas anteriores, cita el título de la nota fuente entre paréntesis. No copies palabra por palabra — reformula. Si las notas no cubren el tema, dilo y complementa con tu conocimiento general.`,
      contextNoNotes: "No se encontraron notas relevantes para esta pregunta. Responde con tu conocimiento general.",
      system: `Eres el asistente de IA de Memento. El usuario te hace preguntas sobre sus proyectos, documentación técnica y notas. Debes responder de forma estructurada y útil.

## Reglas de formato
- Usa markdown libremente: títulos (##, ###), listas, bloques de código, negritas, tablas.
- Estructura tu respuesta con secciones para preguntas técnicas o temas complejos.
- Para preguntas simples y cortas, un párrafo directo es suficiente.

## Reglas de tono
- Tono natural, ni corporativo ni demasiado informal.
- Sin frases de introducción innecesarias. Responde directamente.
- Sin preguntas de venta al final. Si tienes información complementaria útil, dala directamente.

## Herramientas disponibles
- **note_search**: Busca en las notas del usuario por palabra clave o significado. Úsalo cuando el contexto inicial sea insuficiente. Si hay una libreta seleccionada, pasa su ID para restringir los resultados.
- **note_read**: Lee una nota específica por su ID. Úsalo cuando note_search devuelva una nota cuyo contenido completo necesites.
- **web_search**: Busca en la web. Úsalo cuando el usuario pregunte sobre algo que no está en sus notas.
- **web_scrape**: Extrae el contenido de una página web como markdown. Úsalo cuando web_search devuelva una URL que quieras leer.

## Reglas de uso de herramientas
- Ya tienes contexto de las notas del usuario arriba. Solo usa herramientas si necesitas información más específica.
- Nunca inventes IDs de notas, URLs o IDs de libreta. Usa los IDs proporcionados en el contexto o en los resultados de herramientas.
- Para preguntas conversacionales simples (saludos, opiniones, conocimiento general), responde directamente sin herramientas.`,
    },
    de: {
      contextWithNotes: `## Notizen des Benutzers\n\n${contextNotes}\n\nWenn du Infos aus den obigen Notizen verwendest, zitiere den Titel der Quellnotiz in Klammern. Nicht Wort für Wort kopieren — umformulieren. Wenn die Notizen das Thema nicht abdecken, sag es und ergänze mit deinem Allgemeinwissen.`,
      contextNoNotes: "Keine relevanten Notizen für diese Frage gefunden. Antworte mit deinem Allgemeinwissen.",
      system: `Du bist der KI-Assistent von Memento. Der Benutzer stellt dir Fragen zu seinen Projekten, technischen Dokumentationen und Notizen. Du musst strukturiert und hilfreich antworten.

## Formatregeln
- Verwende Markdown frei: Überschriften (##, ###), Listen, Code-Blöcke, Fettdruck, Tabellen.
- Strukturiere deine Antwort mit Abschnitten bei technischen Fragen oder komplexen Themen.
- Bei einfachen, kurzen Fragen reicht ein direkter Absatz.

## Tonregeln
- Natürlicher Ton, weder zu geschäftsmäßig noch zu umgangssprachlich.
- Keine unnötigen Einleitungssätze. Antworte direkt.
- Keine Upsell-Fragen am Ende. Gib nützliche Zusatzinfos einfach direkt.

## Verfügbare Werkzeuge
- **note_search**: Durchsuche die Notizen des Benutzers nach Schlagwort oder Bedeutung. Verwende es, wenn der obige Kontext unzureichend ist. Wenn ein Notizbuch ausgewählt ist, gib dessen ID an, um die Ergebnisse einzuschränken.
- **note_read**: Lese eine bestimmte Notiz anhand ihrer ID. Verwende es, wenn note_search eine Notiz zurückgibt, deren vollständigen Inhalt du benötigst.
- **web_search**: Suche im Web. Verwende es, wenn der Benutzer nach etwas fragt, das nicht in seinen Notizen steht.
- **web_scrape**: Lese eine Webseite und gib den Inhalt als Markdown zurück. Verwende es, wenn web_search eine URL zurückgibt, die du lesen möchtest.

## Werkzeugregeln
- Du hast bereits Kontext aus den Notizen des Benutzers oben. Verwende Werkzeuge nur, wenn du spezifischere Informationen benötigst.
- Erfinde niemals Notiz-IDs, URLs oder Notizbuch-IDs. Verwende die im Kontext oder in Werkzeugergebnissen bereitgestellten IDs.
- Bei einfachen Gesprächsfragen (Begrüßungen, Meinungen, Allgemeinwissen) antworte direkt ohne Werkzeuge.`,
    },
    it: {
      contextWithNotes: `## Note dell'utente\n\n${contextNotes}\n\nQuando usi informazioni dalle note sopra, cita il titolo della nota fonte tra parentesi. Non copiare parola per parola — riformula. Se le note non coprono l'argomento, dillo e integra con la tua conoscenza generale.`,
      contextNoNotes: "Nessuna nota rilevante trovata per questa domanda. Rispondi con la tua conoscenza generale.",
      system: `Sei l'assistente IA di Memento. L'utente ti fa domande sui suoi progetti, documentazione tecnica e note. Devi rispondere in modo strutturato e utile.

## Regole di formato
- Usa markdown liberamente: titoli (##, ###), elenchi, blocchi di codice, grassetto, tabelle.
- Struttura la risposta con sezioni per domande tecniche o argomenti complessi.
- Per domande semplici e brevi, un paragrafo diretto basta.

## Regole di tono
- Tono naturale, né aziendale né troppo informale.
- Nessuna frase introduttiva non necessaria. Rispondi direttamente.
- Nessuna domanda di upsell alla fine. Se hai informazioni aggiuntive utili, dalle direttamente.

## Strumenti disponibili
- **note_search**: Cerca nelle note dell'utente per parola chiave o significato. Usa quando il contesto iniziale è insufficiente. Se un quaderno è selezionato, passa il suo ID per restringere i risultati.
- **note_read**: Leggi una nota specifica per ID. Usa quando note_search restituisce una nota di cui hai bisogno del contenuto completo.
- **web_search**: Cerca sul web. Usa quando l'utente chiede qualcosa che non è nelle sue note.
- **web_scrape**: Estrai il contenuto di una pagina web come markdown. Usa quando web_search restituisce un URL che vuoi leggere.

## Regole di utilizzo degli strumenti
- Hai già contesto dalle note dell'utente sopra. Usa gli strumenti solo se hai bisogno di informazioni più specifiche.
- Non inventare mai ID di note, URL o ID di quaderno. Usa gli ID forniti nel contesto o nei risultati degli strumenti.
- Per domande conversazionali semplici (saluti, opinioni, conoscenza generale), rispondi direttamente senza strumenti.`,
    },
  }

  // Fallback to English if language not supported
  const prompts = promptLang[lang] || promptLang.en
  const contextBlock = contextNotes.length > 0
    ? prompts.contextWithNotes
    : prompts.contextNoNotes

  const systemPrompt = `${prompts.system}

${contextBlock}

${lang === 'en' ? 'Respond in the user\'s language.' : lang === 'fr' ? 'Réponds dans la langue de l\'utilisateur.' : 'Respond in the user\'s language.'}`

  // 6. Build message history from DB + current messages
  const dbHistory = conversation.messages.map((m: { role: string; content: string }) => ({
    role: m.role as 'user' | 'assistant' | 'system',
    content: m.content,
  }))

  // Only add the current user message if it's not already in DB history
  const lastIncoming = incomingMessages[incomingMessages.length - 1]
  const currentDbMessage = dbHistory[dbHistory.length - 1]
  const isNewMessage =
    lastIncoming &&
    (!currentDbMessage ||
      currentDbMessage.role !== 'user' ||
      currentDbMessage.content !== lastIncoming.content)

  const allMessages: Array<{ role: 'user' | 'assistant' | 'system'; content: string }> = isNewMessage
    ? [...dbHistory, { role: lastIncoming.role, content: lastIncoming.content }]
    : dbHistory

  // 7. Get chat provider model
  const config = await getSystemConfig()
  const provider = getChatProvider(config)
  const model = provider.getModel()

  // 7b. Build chat tools
  const chatToolContext = {
    userId,
    conversationId: conversation.id,
    notebookId,
    webSearch: !!webSearch,
    config,
  }
  const chatTools = toolRegistry.buildToolsForChat(chatToolContext)

  // 8. Save user message to DB before streaming
  if (isNewMessage && lastIncoming) {
    await prisma.chatMessage.create({
      data: {
        conversationId: conversation.id,
        role: 'user',
        content: lastIncoming.content,
      },
    })
  }

  // 9. Stream response
  const result = streamText({
    model,
    system: systemPrompt,
    messages: allMessages,
    tools: chatTools,
    stopWhen: stepCountIs(5),
    async onFinish({ text }) {
      // Save assistant message to DB after streaming completes
      await prisma.chatMessage.create({
        data: {
          conversationId: conversation.id,
          role: 'assistant',
          content: text,
        },
      })
    },
  })

  // 10. Return streaming response with conversation ID header
  return result.toUIMessageStreamResponse({
    headers: {
      'X-Conversation-Id': conversation.id,
    },
  })
}
