/**
 * Contextual Auto-Tagging Service (IA2)
 * Suggests labels from the current notebook's existing labels
 * OR creates new label suggestions for empty notebooks
 */

import { prisma } from '@/lib/prisma'
import { getAIProvider } from '@/lib/ai/factory'
import { getSystemConfig } from '@/lib/config'

export interface LabelSuggestion {
  label: string
  confidence: number // 0-100
  reasoning?: string
  isNewLabel?: boolean // true if this is a suggestion to CREATE a new label
}

export class ContextualAutoTagService {
  /**
   * Suggest labels for a note
   * @param noteContent - Content of the note
   * @param notebookId - ID of the notebook (to get available labels)
   * @param userId - User ID
   * @returns Array of label suggestions (max 3)
   */
  async suggestLabels(
    noteContent: string,
    notebookId: string | null,
    userId: string,
    language: string = 'en'
  ): Promise<LabelSuggestion[]> {
    // If no notebook, return empty (no context)
    if (!notebookId) {
      return []
    }

    // Get notebook with its labels
    const notebook = await prisma.notebook.findFirst({
      where: {
        id: notebookId,
        userId,
      },
      include: {
        labels: {
          orderBy: {
            name: 'asc',
          },
        },
      },
    })

    if (!notebook) {
      return []
    }

    // CASE 1: Notebook has existing labels → suggest from them (IA2)
    if (notebook.labels.length > 0) {
      return await this.suggestFromExistingLabels(noteContent, notebook, language)
    }

    // CASE 2: Notebook has NO labels → suggest NEW labels to create
    return await this.suggestNewLabels(noteContent, notebook, language)
  }

  /**
   * Suggest labels from existing labels in the notebook (IA2)
   */
  private async suggestFromExistingLabels(
    noteContent: string,
    notebook: any,
    language: string
  ): Promise<LabelSuggestion[]> {
    const availableLabels = notebook.labels.map((l: any) => l.name)

    // Build prompt with available labels
    const prompt = this.buildPrompt(noteContent, notebook.name, availableLabels, language)

    try {
      const config = await getSystemConfig()
      const provider = getAIProvider(config)

      // Use generateText with JSON response
      const response = await provider.generateText(prompt)

      // Improved JSON parsing with multiple fallback strategies
      let parsed: any

      // Strategy 1: Direct parse
      try {
        parsed = JSON.parse(response)
      } catch (e) {
        // Strategy 2: Extract JSON from markdown code blocks
        const codeBlockMatch = response.match(/```(?:json)?\s*(\{[\s\S]*?\}|\[[\s\S]*?\])\s*```/)
        if (codeBlockMatch) {
          parsed = JSON.parse(codeBlockMatch[1])
        } else {
          // Strategy 3: Extract JSON object or array
          const jsonArrayMatch = response.match(/\[[\s\S]*\]/)
          const jsonObjectMatch = response.match(/\{[\s\S]*\}/)

          if (jsonArrayMatch) {
            let cleanedJson = jsonArrayMatch[0]
            cleanedJson = cleanedJson.replace(/,\s*([}\]])/g, '$1')
            cleanedJson = cleanedJson.replace(/([{,]\s*)([a-zA-Z_][a-zA-Z0-9_]*)\s*:/g, '$1"$2":')
            parsed = JSON.parse(cleanedJson)
          } else if (jsonObjectMatch) {
            let cleanedJson = jsonObjectMatch[0]
            cleanedJson = cleanedJson.replace(/,\s*([}\]])/g, '$1')
            cleanedJson = cleanedJson.replace(/([{,]\s*)([a-zA-Z_][a-zA-Z0-9_]*)\s*:/g, '$1"$2":')
            parsed = JSON.parse(cleanedJson)
          } else {
            console.error('❌ Could not extract JSON from response')
            return []
          }
        }
      }

      // Handle both formats: array directly OR {suggestions: array}
      let suggestionsArray = parsed
      if (parsed.suggestions && Array.isArray(parsed.suggestions)) {
        suggestionsArray = parsed.suggestions
      } else if (Array.isArray(parsed)) {
        suggestionsArray = parsed
      } else {
        console.error('❌ Invalid response structure:', parsed)
        return []
      }

      // Filter and map suggestions
      const suggestions = suggestionsArray
        .filter((s: any) => {
          // Must be in available labels
          return availableLabels.includes(s.label) && s.confidence > 0.6
        })
        .map((s: any) => ({
          label: s.label,
          confidence: Math.round(s.confidence * 100),
          reasoning: s.reasoning || '',
          isNewLabel: false,
        }))
        .sort((a: any, b: any) => b.confidence - a.confidence)
        .slice(0, 3) // Max 3 suggestions

      return suggestions as LabelSuggestion[]
    } catch (error) {
      console.error('Failed to suggest labels:', error)
      return []
    }
  }

  /**
   * Suggest NEW labels to create for empty notebooks (Hybrid IA2+IA4)
   */
  private async suggestNewLabels(
    noteContent: string,
    notebook: any,
    language: string
  ): Promise<LabelSuggestion[]> {
    // Build prompt to suggest NEW labels based on content
    const prompt = this.buildNewLabelsPrompt(noteContent, notebook.name, language)

    try {
      const config = await getSystemConfig()
      const provider = getAIProvider(config)

      // Use generateText with JSON response
      const response = await provider.generateText(prompt)

      // Improved JSON parsing with multiple fallback strategies
      let parsed: any

      // Strategy 1: Direct parse
      try {
        parsed = JSON.parse(response)
      } catch (e) {
        // Strategy 2: Extract JSON from markdown code blocks
        const codeBlockMatch = response.match(/```(?:json)?\s*(\{[\s\S]*?\}|\[[\s\S]*?\])\s*```/)
        if (codeBlockMatch) {
          parsed = JSON.parse(codeBlockMatch[1])
        } else {
          // Strategy 3: Extract JSON object or array
          const jsonArrayMatch = response.match(/\[[\s\S]*\]/)
          const jsonObjectMatch = response.match(/\{[\s\S]*\}/)

          if (jsonArrayMatch) {
            let cleanedJson = jsonArrayMatch[0]
            cleanedJson = cleanedJson.replace(/,\s*([}\]])/g, '$1')
            cleanedJson = cleanedJson.replace(/([{,]\s*)([a-zA-Z_][a-zA-Z0-9_]*)\s*:/g, '$1"$2":')
            parsed = JSON.parse(cleanedJson)
          } else if (jsonObjectMatch) {
            let cleanedJson = jsonObjectMatch[0]
            cleanedJson = cleanedJson.replace(/,\s*([}\]])/g, '$1')
            cleanedJson = cleanedJson.replace(/([{,]\s*)([a-zA-Z_][a-zA-Z0-9_]*)\s*:/g, '$1"$2":')
            parsed = JSON.parse(cleanedJson)
          } else {
            console.error('❌ Could not extract JSON from response')
            return []
          }
        }
      }

      // Handle both formats: array directly OR {suggestions: array}
      let suggestionsArray = parsed
      if (parsed.suggestions && Array.isArray(parsed.suggestions)) {
        suggestionsArray = parsed.suggestions
      } else if (Array.isArray(parsed)) {
        suggestionsArray = parsed
      } else {
        console.error('❌ Invalid response structure:', parsed)
        return []
      }

      // Filter and map suggestions
      const suggestions = suggestionsArray
        .filter((s: any) => {
          return s.label && s.label.length > 0 && s.confidence > 0.6
        })
        .map((s: any) => ({
          label: s.label,
          confidence: Math.round(s.confidence * 100),
          reasoning: s.reasoning || '',
          isNewLabel: true, // Mark as new label suggestion
        }))
        .sort((a: any, b: any) => b.confidence - a.confidence)
        .slice(0, 3) // Max 3 suggestions

      return suggestions as LabelSuggestion[]
    } catch (error) {
      console.error('❌ Failed to suggest new labels:', error)
      return []
    }
  }

  /**
   * Build the AI prompt for contextual label suggestion (localized)
   */
  private buildPrompt(noteContent: string, notebookName: string, availableLabels: string[], language: string = 'en'): string {
    const labelList = availableLabels.map(l => `- ${l}`).join('\n')

    const instructions: Record<string, string> = {
      fr: `
Tu es un assistant qui suggère les labels les plus appropriés pour une note.

CONTENU DE LA NOTE :
${noteContent.substring(0, 1000)}

NOTEBOOK ACTUEL :
${notebookName}

LABELS DISPONIBLES DANS CE NOTEBOOK :
${labelList}

TÂCHE :
Analyse le contenu de la note et suggère les labels les PLUS appropriés parmi les labels disponibles ci-dessus.
Considère :
1. La pertinence du label pour le contenu
2. Le nombre de labels (maximum 3 suggestions)
3. La confiance (seuil minimum : 0.6)

RÈGLES :
- Suggère SEULEMENT des labels qui sont dans la liste des labels disponibles
- Retourne au maximum 3 suggestions
- Chaque suggestion doit avoir une confiance > 0.6
- Si aucun label n'est pertinent, retourne un tableau vide

FORMAT DE RÉPONSE (JSON uniquement) :
{
  "suggestions": [
    { "label": "nom_du_label", "confidence": 0.85, "reasoning": "Pourquoi ce label est pertinent" }
  ]
}

Ta réponse :
`.trim(),
      en: `
You are an assistant that suggests the most appropriate labels for a note.

NOTE CONTENT:
${noteContent.substring(0, 1000)}

CURRENT NOTEBOOK:
${notebookName}

AVAILABLE LABELS IN THIS NOTEBOOK:
${labelList}

TASK:
Analyze the note content and suggest the MOST appropriate labels from the available labels above.
Consider:
1. Label relevance to content
2. Number of labels (maximum 3 suggestions)
3. Confidence (minimum threshold: 0.6)

RULES:
- Suggest ONLY labels that are in the available labels list
- Return maximum 3 suggestions
- Each suggestion must have confidence > 0.6
- If no label is relevant, return an empty array

RESPONSE FORMAT (JSON only):
{
  "suggestions": [
    { "label": "label_name", "confidence": 0.85, "reasoning": "Why this label is relevant" }
  ]
}

Your response:
`.trim(),
      fa: `
شما یک دستیار هستید که مناسب‌ترین برچسب‌ها را برای یک یادداشت پیشنهاد می‌دهید.

محتوای یادداشت:
${noteContent.substring(0, 1000)}

دفترچه فعلی:
${notebookName}

برچسب‌های موجود در این دفترچه:
${labelList}

وظیفه:
محتوای یادداشت را تحلیل کنید و مناسب‌ترین برچسب‌ها را از لیست برچسب‌های موجود در بالا پیشنهاد دهید.
در نظر بگیرید:
1. ارتباط برچسب با محتوا
2. تعداد برچسب‌ها (حداکثر ۳ پیشنهاد)
3. اطمینان (حداقل آستانه: 0.6)

قوانین:
- فقط برچسب‌هایی را پیشنهاد دهید که در لیست برچسب‌های موجود هستند
- حداکثر ۳ پیشنهاد برگردانید
- هر پیشنهاد باید دارای اطمینان > 0.6 باشد
- اگر هیچ برچسبی مرتبط نیست، یک اینرایه خالی برگردانید

فرمت پاسخ (فقط JSON):
{
  "suggestions": [
    { "label": "نام_برچسب", "confidence": 0.85, "reasoning": "چرا این برچسب مرتبط است" }
  ]
}

پاسخ شما:
`.trim(),
      es: `
Eres un asistente que sugiere las etiquetas más apropiadas para una nota.

CONTENIDO DE LA NOTA:
${noteContent.substring(0, 1000)}

CUADERNO ACTUAL:
${notebookName}

ETIQUETAS DISPONIBLES EN ESTE CUADERNO:
${labelList}

TAREA:
Analiza el contenido de la nota y sugiere las etiquetas MÁS apropiadas de las etiquetas disponibles arriba.
Considera:
1. Relevancia de la etiqueta para el contenido
2. Número de etiquetas (máximo 3 sugerencias)
3. Confianza (umbral mínimo: 0.6)

REGLAS:
- Sugiere SOLO etiquetas que estén en la lista de etiquetas disponibles
- Devuelve máximo 3 sugerencias
- Cada sugerencia debe tener confianza > 0.6
- Si ninguna etiqueta es relevante, devuelve un array vacío

FORMATO DE RESPUESTA (solo JSON):
{
  "suggestions": [
    { "label": "nombre_etiqueta", "confidence": 0.85, "reasoning": "Por qué esta etiqueta es relevante" }
  ]
}

Tu respuesta:
`.trim(),
      de: `
Du bist ein Assistent, der die passendsten Labels für eine Notiz vorschlägt.

NOTIZINHALT:
${noteContent.substring(0, 1000)}

AKTUELLES NOTIZBUCH:
${notebookName}

VERFÜGBARE LABELS IN DIESEM NOTIZBUCH:
${labelList}

AUFGABE:
Analysiere den Notizinhalt und schlage die AM BESTEN geeigneten Labels aus den oben verfügbaren Labels vor.
Berücksichtige:
1. Relevanz des Labels für den Inhalt
2. Anzahl der Labels (maximal 3 Vorschläge)
3. Konfidenz (Mindestschwellenwert: 0.6)

REGELN:
- Schlage NUR Labels vor, die in der Liste der verfügbaren Labels sind
- Gib maximal 3 Vorschläge zurück
- Jeder Vorschlag muss eine Konfidenz > 0.6 haben
- Wenn kein Label relevant ist, gib ein leeres Array zurück

ANTWORTFORMAT (nur JSON):
{
  "suggestions": [
    { "label": "label_name", "confidence": 0.85, "reasoning": "Warum dieses Label relevant ist" }
  ]
}

Deine Antwort:
`.trim()
    }

    return instructions[language] || instructions['en'] || instructions['fr']
  }

  /**
   * Build the AI prompt for NEW label suggestions (when notebook is empty) (localized)
   */
  private buildNewLabelsPrompt(noteContent: string, notebookName: string, language: string = 'en'): string {
    const instructions: Record<string, string> = {
      fr: `
Tu es un assistant qui suggère de nouveaux labels pour organiser une note.

CONTENU DE LA NOTE :
${noteContent.substring(0, 1000)}

NOTEBOOK ACTUEL :
${notebookName}

CONTEXTE :
Ce notebook n'a pas encore de labels. Tu dois suggérer les PREMIERS labels appropriés pour cette note.

TÂCHE :
Analyse le contenu de la note et suggère 1-3 labels qui seraient pertinents pour organiser cette note.
Considère :
1. Les sujets ou thèmes abordés
2. Le type de contenu (idée, tâche, référence, etc.)
3. Le contexte du notebook "${notebookName}"

RÈGLES :
- Les labels doivent être COURTS (1-2 mots maximum)
- Les labels doivent être en minuscules
- Évite les accents si possible (ex: "idee" au lieu de "idée")
- Retourne au maximum 3 suggestions
- Chaque suggestion doit avoir une confiance > 0.6

IMPORTANT : Réponds UNIQUEMENT avec du JSON valide, sans texte avant ou après. Pas de markdown, pas de code blocks.

FORMAT DE RÉPONSE (JSON brut, sans markdown) :
{"suggestions":[{"label":"nom_du_label","confidence":0.85,"reasoning":"Pourquoi ce label est pertinent"}]}

Ta réponse (JSON brut uniquement) :
`.trim(),
      en: `
You are an assistant that suggests new labels to organize a note.

NOTE CONTENT:
${noteContent.substring(0, 1000)}

CURRENT NOTEBOOK:
${notebookName}

CONTEXT:
This notebook has no labels yet. You must suggest the FIRST appropriate labels for this note.

TASK:
Analyze the note content and suggest 1-3 labels that would be relevant to organize this note.
Consider:
1. Topics or themes covered
2. Content type (idea, task, reference, etc.)
3. Context of the notebook "${notebookName}"

RULES:
- Labels must be SHORT (max 1-2 words)
- Labels must be lowercase
- Avoid accents if possible
- Return maximum 3 suggestions
- Each suggestion must have confidence > 0.6

IMPORTANT: Respond ONLY with valid JSON, without text before or after. No markdown, no code blocks.

RESPONSE FORMAT (raw JSON, no markdown):
{"suggestions":[{"label":"label_name","confidence":0.85,"reasoning":"Why this label is relevant"}]}

Your response (raw JSON only):
`.trim(),
      fa: `
شما یک دستیار هستید که برچسب‌های جدیدی برای سازماندهی یک یادداشت پیشنهاد می‌دهید.

محتوای یادداشت:
${noteContent.substring(0, 1000)}

دفترچه فعلی:
${notebookName}

زمینه:
این دفترچه هنوز هیچ برچسبی ندارد. شما باید اولین برچسب‌های مناسب را برای این یادداشت پیشنهاد دهید.

وظیفه:
محتوای یادداشت را تحلیل کنید و ۱-۳ برچسب پیشنهاد دهید که برای سازماندهی این یادداشت مرتبط باشند.
در نظر بگیرید:
1. موضوعات یا تم‌های پوشش داده شده
2. نوع محتوا (ایده، وظیفه، مرجع و غیره)
3. زمینه دفترچه "${notebookName}"

قوانین:
- برچسب‌ها باید کوتاه باشند (حداکثر ۱-۲ کلمه)
- برچسب‌ها باید با حروف کوچک باشند
- حداکثر ۳ پیشنهاد برگردانید
- هر پیشنهاد باید دارای اطمینان > 0.6 باشد

مهم: فقط با یک JSON معتبر پاسخ دهید، بدون متن قبل یا بعد. بدون مارک‌داون، بدون بلوک کد.

فرمت پاسخ (JSON خام، بدون مارک‌داون):
{"suggestions":[{"label":"نام_برچسب","confidence":0.85,"reasoning":"چرا این برچسب مرتبط است"}]}

پاسخ شما (فقط JSON خام):
`.trim(),
      es: `
Eres un asistente que sugiere nuevas etiquetas para organizar una nota.

CONTENIDO DE LA NOTA:
${noteContent.substring(0, 1000)}

CUADERNO ACTUAL:
${notebookName}

CONTEXTO:
Este cuaderno aún no tiene etiquetas. Debes sugerir las PRIMERAS etiquetas apropiadas para esta nota.

TAREA:
Analiza el contenido de la nota y sugiere 1-3 etiquetas que serían relevantes para organizar esta nota.
Considera:
1. Temas o tópicos cubiertos
2. Tipo de contenido (idea, tarea, referencia, etc.)
3. Contexto del cuaderno "${notebookName}"

REGLAS:
- Las etiquetas deben ser CORTAS (máx 1-2 palabras)
- Las etiquetas deben estar en minúsculas
- Evita acentos si es posible
- Devuelve máximo 3 sugerencias
- Cada sugerencia debe tener confianza > 0.6

IMPORTANTE: Responde SOLO con JSON válido, sin texto antes o después. Sin markdown, sin bloques de código.

FORMATO DE RESPUESTA (JSON crudo, sin markdown):
{"suggestions":[{"label":"nombre_etiqueta","confidence":0.85,"reasoning":"Por qué esta etiqueta es relevante"}]}

Tu respuesta (solo JSON crudo):
`.trim(),
      de: `
Du bist ein Assistent, der neue Labels vorschlägt, um eine Notiz zu organisieren.

NOTIZINHALT:
${noteContent.substring(0, 1000)}

AKTUELLES NOTIZBUCH:
${notebookName}

KONTEXT:
Dieses Notizbuch hat noch keine Labels. Du musst die ERSTEN passenden Labels für diese Notiz vorschlagen.

AUFGABE:
Analysiere den Notizinhalt und schlage 1-3 Labels vor, die relevant wären, um diese Notiz zu organisieren.
Berücksichtige:
1. Abgedeckte Themen oder Bereiche
2. Inhaltstyp (Idee, Aufgabe, Referenz, usw.)
3. Kontext des Notizbuchs "${notebookName}"

REGELN:
- Labels müssen KURZ sein (max 1-2 Wörter)
- Labels müssen kleingeschrieben sein
- Vermeide Akzente wenn möglich
- Gib maximal 3 Vorschläge zurück
- Jeder Vorschlag muss eine Konfidenz > 0.6 haben

WICHTIG: Antworte NUR mit gültigem JSON, ohne Text davor oder danach. Kein Markdown, keine Code-Blöcke.

ANTWORTFORMAT (rohes JSON, kein Markdown):
{"suggestions":[{"label":"label_name","confidence":0.85,"reasoning":"Warum dieses Label relevant ist"}]}

Deine Antwort (nur rohes JSON):
`.trim()
    }

    return instructions[language] || instructions['en'] || instructions['fr']
  }
}

// Export singleton instance
export const contextualAutoTagService = new ContextualAutoTagService()
