/**
 * POST /api/chatbot-pensees
 * Chatbot RAG Pensées Écologiques — corpus FRACAS Bonpote
 * Appelle LightRAG PE (localhost:9621) avec contexte auteur optionnel.
 */
import { readFileSync } from 'node:fs'
import { join } from 'node:path'
import { checkRateLimitJson } from '~/server/utils/rateLimitJson'

const SYSTEM_PREFACE_PENSEES = `Tu es un agent du RAG Pensées Écologiques, infrastructure militante du collectif trans-former.fr.
Tu réponds en t'appuyant STRICTEMENT sur le corpus ingéré (auteurs FRACAS Bonpote : écosocialisme, éco-anarchisme, écoféminismes, écologies décoloniales, technocritique, pensées du vivant, décroissance...).

Règles :
- Cite les sources (auteur, livre) à chaque assertion importante.
- Si la question dépasse le corpus, dis-le clairement. Pas d'hallucination.
- Ton politique direct, pas de neutralité fade.
- Réponse en français, dense, sans délayage.
- Distingue les positions selon les écoles quand elles divergent.`

const SYSTEM_PREFACE_PROJETS = `Tu es un agent du RAG Projets de Jules Nény (architecte, collectif trans-former.fr).
Tu réponds STRICTEMENT à partir des documents projet (fichiers butte-pinson__*.md et autres projets archi de Jules).
N'utilise PAS le corpus FRACAS Pensées Écologiques pour répondre, sauf si l'usager te le demande explicitement.

Règles :
- Cite les sources (nom de projet, document) à chaque assertion importante.
- Si la question dépasse le corpus projet, dis-le clairement. Pas d'hallucination.
- Ton praticien réflexif : 1ère personne quand pertinent, narration située.
- Réponse en français, dense, sans délayage.`

const SYSTEM_PREFACE_BOTH = `Tu es un agent du RAG croisé Pensées x Projets de Jules Nény (architecte militant, collectif trans-former.fr).
CENTRE TA RÉPONSE sur les documents PROJETS (fichiers butte-pinson__*.md et autres projets archi).
Mobilise le corpus FRACAS Pensées (autres fichiers) UNIQUEMENT pour éclairer théoriquement les partis pris des projets, jamais l'inverse.

Pondération attendue : ~70% ancrage projet concret, ~30% éclairage théorique FRACAS.

Règles :
- Cite les sources (auteur ou nom de projet, document) à chaque assertion.
- Si un thème n'est pas couvert par les projets, dis-le clairement avant d'éventuellement étendre au corpus Pensées.
- Pas d'hallucination, pas d'extrapolation hors corpus.
- Ton praticien militant : direct, pas neutre, ancré dans la pratique architecturale.
- Réponse en français, dense, sans délayage.`

function buildPrefaceAuteur(nomAuteur: string, slug: string): string {
  return `Tu réponds EXCLUSIVEMENT depuis les livres de ${nomAuteur} présents dans le RAG (fichiers commençant par "${slug}__").
Si la question sort du périmètre de cet auteur, indique-le et propose de l'aborder sans le hashtag pour interroger la carte entière. Reste fidèle au style et à la pensée de ${nomAuteur}. Cite toujours le livre.

Règles :
- Cite les sources (titre du livre) à chaque assertion.
- Pas d'hallucination. Si l'info n'est pas dans le corpus de cet auteur, dis-le.
- N'introduis JAMAIS d'autres auteurs sauf si ${nomAuteur} les commente explicitement.
- Ton politique direct, pas de neutralité fade.
- Réponse en français, dense, sans délayage.`
}

interface AuteurIngere { id: string; nom: string; ingere: boolean }

let auteursIngeresCache: AuteurIngere[] | null = null

function loadAuteursIngeres(): AuteurIngere[] {
  if (auteursIngeresCache) return auteursIngeresCache
  try {
    const jsonPath = join(process.cwd(), 'public', 'data', 'auteurs-pensees.json')
    const raw = readFileSync(jsonPath, 'utf-8')
    const data = JSON.parse(raw)
    const list = (data.auteurs ?? [])
      .filter((a: any) => a.ingere === true)
      .map((a: any) => ({ id: String(a.id), nom: String(a.nom), ingere: true }))
    auteursIngeresCache = list
    return list
  } catch {
    auteursIngeresCache = []
    return []
  }
}

export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig()

  const ip = getHeader(event, 'x-forwarded-for')?.split(',')[0].trim()
    || event.node.req.socket?.remoteAddress
    || '0.0.0.0'
  const allowed = checkRateLimitJson(ip, 'chatbot-pensees', 20)
  if (!allowed) throw createError({ statusCode: 429, message: 'Limite de 20 questions par jour atteinte.' })

  const body = await readBody(event)
  if (!body?.query || body.query.trim().length < 3 || body.query.trim().length > 500) {
    throw createError({ statusCode: 400, message: 'Query invalide (3-500 caractères).' })
  }

  const query: string = body.query.trim()
  const mode: string = body.mode || 'hybrid'
  const corpus: string = body.corpus || 'both'
  const ragUrl: string = (config.ragPeUrl as string) || 'http://localhost:9621'
  const auteurSlug: string | null = body.auteur_slug?.trim().toLowerCase() || null

  let nomAuteurMatch: string | null = null
  if (auteurSlug) {
    const ingeres = loadAuteursIngeres()
    const auteur = ingeres.find(a => a.id === auteurSlug)
    nomAuteurMatch = auteur?.nom ?? null
  }

  let systemPreface: string
  if (auteurSlug && nomAuteurMatch) {
    systemPreface = buildPrefaceAuteur(nomAuteurMatch, auteurSlug)
  } else if (corpus === 'pensees') {
    systemPreface = SYSTEM_PREFACE_PENSEES
  } else if (corpus === 'projets') {
    systemPreface = SYSTEM_PREFACE_PROJETS
  } else {
    systemPreface = SYSTEM_PREFACE_BOTH
  }

  try {
    await $fetch(`${ragUrl}/health`, { timeout: 5000 })
  } catch {
    throw createError({ statusCode: 503, message: 'RAG indisponible pour l\'instant — réessaie dans quelques minutes.' })
  }

  const ragQuery = `${systemPreface}\n\nQuestion : ${query}`
  const ragBody: Record<string, any> = { query: ragQuery, mode }
  if (auteurSlug && nomAuteurMatch) {
    ragBody.hl_keywords = [nomAuteurMatch, auteurSlug]
    ragBody.ll_keywords = [auteurSlug]
  }

  let ragResponse: any
  try {
    ragResponse = await $fetch(`${ragUrl}/query`, {
      method: 'POST',
      body: ragBody,
      timeout: 90000,
    })
  } catch (e: any) {
    const status = e?.response?.status
    if (status === 429) throw createError({ statusCode: 429, message: 'RAG saturé — réessaie dans quelques instants.' })
    throw createError({ statusCode: 504, message: 'RAG en cours de processing — réessaie dans quelques secondes.' })
  }

  let chunksOnTarget = 0
  let chunksOffTarget = 0
  if (auteurSlug && nomAuteurMatch && Array.isArray(ragResponse.references)) {
    const slugPrefix = `${auteurSlug}__`
    for (const ref of ragResponse.references) {
      const fp = (ref.file_path ?? '').toLowerCase()
      if (!fp) continue
      if (fp.startsWith(slugPrefix)) chunksOnTarget++
      else chunksOffTarget++
    }
  }

  return {
    response: ragResponse.response ?? '',
    mode,
    corpus,
    auteur: auteurSlug && nomAuteurMatch ? { slug: auteurSlug, nom: nomAuteurMatch } : null,
    auteur_unmatched: auteurSlug && !nomAuteurMatch ? auteurSlug : null,
    auteur_chunks: auteurSlug && nomAuteurMatch ? { on_target: chunksOnTarget, off_target: chunksOffTarget } : null,
    filter: { couche: body.filter_couche ?? null, ecole: body.filter_ecole ?? null },
    timestamp: new Date().toISOString(),
  }
})
