#!/usr/bin/env python3
"""Calienta la cache de voz: coge las frases que cayeron a Piper (misses.log,
que escribe jarvis-voz.sh) y las regenera con la voz CLONADA (XTTS) en
cache_voz/. La proxima vez que JARVIS diga esa frase, sonara con tu voz al
instante. Cuanto mas usas JARVIS, mas habla con tu voz.

    python calienta_cache.py [cuda|cpu] [--min N]

--min N: solo frases vistas al menos N veces (por defecto 1 = todas las nuevas).
No reproduce nada. Pensado para correr de noche (timer de systemd).
"""
import os, sys, re, json, time, hashlib, collections
os.environ.setdefault("COQUI_TOS_AGREED", "1")

DISP = "cuda"
MIN = 1
for i, a in enumerate(sys.argv[1:]):
    if a in ("cuda", "cpu"):
        DISP = a
    elif a == "--min" and i + 2 <= len(sys.argv) - 1:
        MIN = int(sys.argv[i + 2])

BASE = os.path.dirname(os.path.abspath(__file__))
MISSES = os.path.join(BASE, "misses.log")
CACHE = os.path.join(BASE, "cache_voz")
sys.path.insert(0, BASE)
from clonador import Clonador, clave   # la voz, definida en un solo sitio
os.makedirs(CACHE, exist_ok=True)


def cacheable(f):
    # ni muy corta ni muy larga; nada con rutas (frases unicas que no repiten);
    # ni mayoria de digitos (numeros sueltos de telemetria, casi unicos)
    if not (3 <= len(f) <= 180):
        return False
    if "/" in f or "\\" in f:
        return False
    letras = sum(c.isalpha() for c in f)
    return letras >= max(3, len(f) * 0.5)


def main():
    if not os.path.exists(MISSES):
        print("no hay misses.log todavia; nada que calentar."); return 0
    with open(MISSES, encoding="utf-8") as f:
        frases = [ln.rstrip("\n") for ln in f if ln.strip()]
    cuenta = collections.Counter(frases)

    pendientes = []
    for frase, n in cuenta.items():
        if n < MIN or not cacheable(frase):
            continue
        dst = os.path.join(CACHE, clave(frase) + ".wav")
        if not os.path.exists(dst):
            pendientes.append((frase, n))
    pendientes.sort(key=lambda x: -x[1])  # las mas repetidas primero
    print(f"{len(cuenta)} frases distintas en el log · "
          f"{len(pendientes)} nuevas a generar (min={MIN})", flush=True)
    if not pendientes:
        return 0

    t0 = time.time()
    voz = Clonador(DISP)
    print(f"modelo cargado en {time.time()-t0:.1f}s", flush=True)

    hechas = 0
    for i, (frase, n) in enumerate(pendientes, 1):
        dst = os.path.join(CACHE, clave(frase) + ".wav")
        try:
            t0 = time.time()
            voz.genera(frase, dst)
            hechas += 1
            print(f"  [{i}/{len(pendientes)}] x{n} {time.time()-t0:.1f}s  "
                  f"{frase[:50]}", flush=True)
        except Exception as e:
            print(f"  fallo '{frase[:40]}': {e}", flush=True)
    print(f"\nlisto: {hechas} frases nuevas en la voz clonada. "
          f"cache -> {CACHE}", flush=True)
    return 0


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