/**
 * Équivalent de `python manage.py index_content`.
 * Indexe KnowledgeItem, FAQ, Page, Article et Document pour le chatbot RAG.
 * Usage : node scripts/index-content.js [--only-missing]
 */
require("dotenv").config();
const { Op } = require("sequelize");
const { sequelize, FAQ, Page, Article, Document, KnowledgeItem, KnowledgeChunk } = require("../src/models");
const { getEmbedding, indexKnowledgeItem } = require("../src/services/chatbotService");

function chunkText(text, maxChars = 800) {
  const paragraphs = text.split("\n").map((p) => p.trim()).filter(Boolean);
  const chunks = [];
  let current = "";
  for (const p of paragraphs) {
    if (current.length + p.length > maxChars && current) {
      chunks.push(current);
      current = "";
    }
    current += (current ? " " : "") + p;
  }
  if (current) chunks.push(current);
  return chunks;
}

async function indexOne(text, sourceType, sourceId, language) {
  const embedding = await getEmbedding(text, "RETRIEVAL_DOCUMENT");
  await KnowledgeChunk.create({ source_type: sourceType, source_id: sourceId, language, content: text, embedding });
  return embedding !== null;
}

async function reindexAll() {
  let indexed = 0, failed = 0;

  for (const item of await KnowledgeItem.findAll({ where: { is_published: true } })) {
    await indexKnowledgeItem(item);
    indexed += 2; // fr + en
  }

  for (const [lang, suffix] of [["fr", "_fr"], ["en", "_en"]]) {
    for (const faq of await FAQ.findAll()) {
      const text = `Q: ${faq[`question${suffix}`] || faq.question_fr}\nR: ${faq[`answer${suffix}`] || faq.answer_fr}`;
      indexed += 1;
      if (!(await indexOne(text, "faq", faq.id, lang))) failed += 1;
    }

    for (const page of await Page.findAll({ where: { is_published: true } })) {
      const body = page[`body${suffix}`] || page.body_fr || "";
      for (const chunk of chunkText(body)) {
        indexed += 1;
        if (!(await indexOne(chunk, "page", page.id, lang))) failed += 1;
      }
    }

    for (const article of await Article.findAll({ where: { is_published: true } })) {
      const body = article[`body${suffix}`] || article.body_fr || "";
      for (const chunk of chunkText(body)) {
        indexed += 1;
        if (!(await indexOne(chunk, "article", article.id, lang))) failed += 1;
      }
    }

    for (const doc of await Document.findAll({ where: { is_public: true, extracted_text: { [Op.ne]: "" } } })) {
      for (const chunk of chunkText(doc.extracted_text)) {
        indexed += 1;
        if (!(await indexOne(chunk, "document", doc.id, lang))) failed += 1;
      }
    }
  }

  return { indexed, failed };
}

async function fillMissingEmbeddings() {
  let indexed = 0, failed = 0;
  const missing = await KnowledgeChunk.findAll({ where: { embedding: null } });
  for (const chunk of missing) {
    const embedding = await getEmbedding(chunk.content, "RETRIEVAL_DOCUMENT");
    indexed += 1;
    if (embedding) {
      chunk.embedding = embedding;
      await chunk.save();
    } else {
      failed += 1;
    }
  }
  return { indexed, failed };
}

async function main() {
  await sequelize.authenticate();
  const onlyMissing = process.argv.includes("--only-missing");

  let result;
  if (onlyMissing) {
    result = await fillMissingEmbeddings();
  } else {
    await KnowledgeChunk.destroy({ where: { source_type: { [Op.ne]: "knowledge_item" } } });
    result = await reindexAll();
  }

  if (result.failed) {
    console.warn(
      `${result.failed} fragment(s) enregistré(s) sans embedding (API Gemini indisponible). ` +
      "Relancez `node scripts/index-content.js --only-missing` plus tard pour les compléter."
    );
  }
  console.log(`${result.indexed} fragment(s) indexé(s) au total.`);
  process.exit(0);
}

main().catch((err) => {
  console.error("Échec de l'indexation :", err);
  process.exit(1);
});
