#!/usr/bin/env python3
"""Construye el indice del RAG sin abrir la interfaz.

    flatpak run --command=python3 io.github.qwersyk.Newelle//master \
        ~/COFRE/CODERS/JARVIS/config/construir_indice.py

En la app esto es el boton "Update Index" de los ajustes del RAG, que lanza un
hilo y va enseñando el progreso en una barra. Desde aqui se llama a lo mismo
pero en primer plano, para poder dejarlo corriendo en una terminal y ver si
falla, que con 20 000 fragmentos conviene.

Antes hay que haber preparado el corpus con indexar.py: este script indexa lo
que haya en la carpeta de documentos, sin filtrar nada.
"""
import os
import sys
import time

sys.path.insert(0, "/app/share/newelle")
sys.path.insert(0, os.path.expanduser(
    "~/.var/app/io.github.qwersyk.Newelle/config/pip"))

from gi.repository import Gio  # noqa: E402

from newelle.handlers.rag.llamaindex_handler import LlamaIndexHanlder  # noqa: E402
from newelle.handlers.embeddings.model2vec import Model2VecHandler  # noqa: E402

CONFIG = os.path.expanduser("~/.var/app/io.github.qwersyk.Newelle/config")
MODELOS = os.path.join(CONFIG, "models")
DOCUMENTOS = os.path.join(MODELOS, "documents")


def main():
    if not os.path.isdir(DOCUMENTOS):
        print(f"no existe {DOCUMENTOS}")
        print("prepara antes el corpus: python3 config/indexar.py --hacerlo")
        return 1

    ficheros = [f for f in os.listdir(DOCUMENTOS) if not f.startswith(".")]
    if not ficheros:
        print("la carpeta de documentos esta vacia")
        return 1

    tam = sum(os.path.getsize(os.path.join(DOCUMENTOS, f))
              for f in ficheros) / 1048576
    print(f"{len(ficheros)} documentos · {tam:.1f} MB")

    settings = Gio.Settings.new("io.github.qwersyk.Newelle")
    rag = LlamaIndexHanlder(settings, MODELOS)

    if not rag.is_installed():
        print("faltan dependencias del RAG (llama-index, faiss, tiktoken)")
        return 1

    # El RAG necesita el modelo de embeddings para vectorizar, y opcionalmente
    # un LLM secundario para reescribir la consulta. Aqui solo hace falta el
    # primero: se indexa, no se pregunta.
    rag.embedding = Model2VecHandler(settings, MODELOS)
    if not rag.embedding.is_installed():
        print("instalando model2vec...")
        rag.embedding.install()
    # create_index solo le pide load_model() al LLM —para indexar no hace falta
    # generar nada— asi que un cascaron sirve y evita cargar 3,7 GB de modelo
    # para vectorizar documentos.
    class SinModelo:
        def load_model(self, *a, **k):
            return True

    rag.llm = SinModelo()
    rag.secondary_llm = SinModelo()

    trozo = rag.get_setting("chunk_size")
    print(f"fragmentos de {trozo} tokens · esto tarda, no lo cortes\n")

    ini = time.time()
    try:
        rag.create_index()
    except Exception as e:
        print(f"\nfallo al indexar: {type(e).__name__}: {e}")
        return 1

    minutos = (time.time() - ini) / 60
    _, data_path = rag.get_paths()
    peso = sum(
        os.path.getsize(os.path.join(dp, f))
        for dp, _, fs in os.walk(data_path) for f in fs) / 1048576

    # create_index se traga sus propias excepciones e imprime el error, asi que
    # terminar sin fallo no significa que haya indice. Se comprueba el disco.
    if peso < 0.1:
        print(f"\nno se creo indice ({peso:.1f} MB en {data_path})")
        print("mira el error de arriba")
        return 1

    print(f"\nindice construido en {minutos:.1f} min · {peso:.1f} MB")
    print(f"en {data_path}")
    return 0


if __name__ == "__main__":
    sys.exit(main())
