#!/usr/bin/env python3
"""Genera acciones.json.

Se escribe aqui y no a mano en el JSON porque son mas de cien entradas con la
misma forma: en tabla se leen de un tirón y se ve si falta un acuse o si dos
frases chocan. El JSON es el producto; este fichero es la fuente.

    python3 _generar.py && python3 -m json.tool acciones.json >/dev/null

Columnas: id | frases (| separadas) | acuse | comando | respuesta

Reglas que conviene no romper:
  - Las frases van sin tildes ni signos: el motor normaliza lo que oye, pero
    aqui se comparan tal cual.
  - Frases cortas y genericas ("abre") arrastran todo lo demas; se prefieren
    de tres palabras o mas, que es como se habla de verdad.
  - Todo lo que borre, mate o apague lleva confirmar=True.
  - {argumento} solo en las que declaran captura.
"""
import json
import pathlib

# --------------------------------------------------------------- sistema
SISTEMA = [
    ("disco_libre", "cuanto espacio queda|espacio en disco|espacio libre en disco|cuanto disco queda",
     "Un momento, lo miro.", "df -h / | tail -1",
     "Quedan {campo4} libres de {campo2}, al {campo5} de uso."),
    ("disco_home", "como esta mi disco|cuanto ocupa mi disco|espacio de mi disco",
     "Un momento.", "df -h ~ | tail -1",
     "Su disco esta al {campo5}, con {campo4} libres de {campo2}."),
    ("disco_todos", "discos montados|particiones|unidades montadas",
     "Enseguida.", "df -h --output=target,size,pcent -x tmpfs -x devtmpfs | tail -n +2",
     "Hay {lineas} unidades montadas. {salida}"),
    ("memoria", "cuanta memoria|memoria libre|cuanta ram|ram libre",
     "Voy.", "free -g | awk '/Mem:/{print $2, $3, $7}'",
     "De {campo1} gigas hay {campo2} en uso y {campo3} disponibles."),
    ("swap", "como esta la swap|memoria de intercambio",
     "Voy.", "free -g | awk '/Swap:/{print $2, $3}'",
     "Swap: {campo2} gigas usados de {campo1}."),
    ("cpu_carga", "carga del sistema|como va el procesador|carga de cpu",
     "Un segundo.", "uptime | sed 's/.*average: //'",
     "La carga media es {ultima}."),
    ("cpu_modelo", "que procesador tengo|modelo de procesador|que cpu tengo",
     "Enseguida.", "lscpu | grep 'Model name' | cut -d: -f2 | xargs",
     "Lleva un {ultima}."),
    ("cpu_nucleos", "cuantos nucleos tengo|cuantos hilos tengo",
     "Voy.", "nproc", "Tiene {ultima} hilos disponibles."),
    ("temperatura", "que temperatura hay|como esta de temperatura|se esta calentando",
     "Lo compruebo.", "for z in /sys/class/thermal/thermal_zone*/temp; do cat $z; done | sort -rn | head -1 | awk '{printf \"%.0f\", $1/1000}'",
     "El punto mas caliente esta a {ultima} grados."),
    ("gpu_estado", "como esta la grafica|estado de la gpu|uso de la grafica",
     "Un momento.",
     "nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total,temperature.gpu --format=csv,noheader",
     "La grafica esta asi: {ultima}."),
    ("uptime", "cuanto lleva encendido|desde cuando esta encendido|tiempo encendido",
     "Voy.", "uptime -p | sed 's/^up //; s/ days\\?/ dias/; s/ hours\\?/ horas/; s/ minutes\\?/ minutos/; s/,\\([^,]*\\)$/ y\\1/'",
     "Lleva encendido {ultima}."),
    ("bateria", "cuanta bateria queda|como esta la bateria|nivel de bateria",
     "Lo miro.", "cat /sys/class/power_supply/BAT*/capacity 2>/dev/null | head -1",
     "La bateria esta al {ultima} por ciento."),
    ("kernel", "que kernel tengo|version del kernel|que nucleo tengo",
     "Enseguida.", "uname -r", "Kernel {ultima}."),
    ("distro", "que distribucion tengo|que sistema tengo|que linux tengo",
     "Voy.", "grep PRETTY_NAME /etc/os-release | cut -d'\"' -f2",
     "Esta usando {ultima}."),
    ("fecha", "que hora es|que dia es|dime la fecha",
     "", "date '+%H:%M del %A %d de %B'", "Son las {salida}."),
    ("procesos_top", "que esta consumiendo|que consume mas|procesos que mas gastan|quien se come la cpu",
     "Lo compruebo.", "ps -eo pcpu,comm --sort=-pcpu --no-headers | head -5 | awk '{c=$1; $1=\"\"; sub(/^ +/,\"\"); printf \"%s al %s por ciento\\n\", $0, c}'",
     "Lo que mas consume es {primera}."),
    ("procesos_memoria", "que consume mas memoria|quien se come la ram",
     "Lo compruebo.", "ps -eo comm,pmem --sort=-pmem --no-headers | head -5",
     "Por memoria: {salida}"),
    ("procesos_cuantos", "cuantos procesos hay|cuantos procesos corren",
     "Voy.", "ps -e --no-headers | wc -l", "Hay {ultima} procesos en marcha."),
    ("red_ip", "cual es mi ip|mi ip local|que ip tengo",
     "Un momento.", "hostname -I | awk '{print $1}'", "Su IP local es {ultima}."),
    ("red_ip_publica", "cual es mi ip publica|ip externa",
     "Salgo a mirarlo.", "curl -s --max-time 6 ifconfig.me", "Su IP publica es {ultima}."),
    ("red_wifi", "a que wifi estoy conectado|red wifi|como se llama la wifi",
     "Voy.", "nmcli -t -f NAME,TYPE connection show --active | grep wireless | cut -d: -f1",
     "Conectado a {ultima}."),
    ("red_velocidad", "cuanto trafico de red|trafico de red",
     "Lo miro.", "cat /proc/net/dev | awk '/wlp|enp/{rx+=$2; tx+=$10} END{printf \"%.1f %.1f\", rx/1e9, tx/1e9}'",
     "Desde el arranque: {campo1} gigas bajados y {campo2} subidos."),
    ("red_ping", "hay internet|tengo conexion|funciona internet",
     "Lo compruebo.",
     "ping -c1 -W2 1.1.1.1 >/dev/null 2>&1 && echo si || echo no",
     "Conexion: {ultima}."),
    ("puertos", "que puertos hay abiertos|puertos a la escucha|quien esta escuchando",
     "Un momento.", "ss -ltnp 2>/dev/null | awk 'NR>1{print $4}' | sort -u | head -8",
     "A la escucha: {salida}"),
    ("servicios_fallidos", "hay servicios caidos|servicios con fallos|algo ha fallado",
     "Lo reviso.", "systemctl --failed --no-legend | wc -l",
     "Servicios en fallo: {ultima}."),
    ("actualizaciones", "hay actualizaciones|paquetes por actualizar",
     "Lo compruebo, tarda un poco.",
     "apt list --upgradable 2>/dev/null | tail -n +2 | wc -l",
     "Hay {ultima} paquetes por actualizar."),
    ("usuarios", "quien esta conectado|hay alguien conectado",
     "Voy.", "who | wc -l", "Hay {ultima} sesiones abiertas."),
    ("gpu_procesos", "que usa la grafica|quien usa la gpu",
     "Lo miro.", "O=$(nvidia-smi --query-compute-apps=process_name,used_gpu_memory --format=csv,noheader | awk -F, '{n=split($1,p,\"/\"); printf \"%s%s\\n\", p[n], $2}'); echo \"${O:-nada, la grafica esta libre}\"",
     "En la grafica: {primera}"),
    ("audio_salida", "por donde suena|salida de audio|que altavoz esta puesto",
     "Voy.", "pactl get-default-sink 2>/dev/null", "La salida es {ultima}."),
    ("audio_micro", "que microfono hay puesto|entrada de audio",
     "Voy.", "pactl get-default-source 2>/dev/null", "La entrada es {ultima}."),
    ("pantallas", "cuantas pantallas hay|monitores conectados",
     "Voy.", "n=$(xrandr --listmonitors 2>/dev/null | head -1 | awk '{print $2}'); xrandr --listmonitors 2>/dev/null | tail -n +2 | awk '{print $NF}' | paste -sd', '; echo \"Tiene $n pantalla$([ \"$n\" = 1 ] || echo s) conectada$([ \"$n\" = 1 ] || echo s)\"",
     "{ultima}"),
    ("dispositivos_usb", "que hay conectado por usb|dispositivos usb",
     "Un momento.", "lsusb | cut -d' ' -f7- | head -6", "Por USB: {salida}"),
    ("bluetooth", "que hay emparejado por bluetooth|dispositivos bluetooth",
     "Voy.", "bluetoothctl devices 2>/dev/null | cut -d' ' -f3- | head -5",
     "Emparejados: {salida}"),
    ("flatpaks", "que flatpaks tengo|aplicaciones flatpak",
     "Voy.", "flatpak list --app --columns=name | head -10", "Instalados: {salida}"),
    ("docker", "hay contenedores corriendo|contenedores docker",
     "Lo miro.", "O=$(docker ps --format '{{.Names}}' 2>/dev/null | head -5); echo \"${O:-ninguno en marcha}\"",
     "Contenedores: {salida}"),
    ("git_estado", "como esta el repositorio|estado de git",
     "Voy.", "cd ~/COFRE/CODERS/JARVIS && git status --short | wc -l",
     "Hay {ultima} ficheros con cambios sin guardar."),
    ("ollama_estado", "esta ollama funcionando|como esta el modelo|que modelo hay cargado",
     "Lo compruebo.",
     "M=$(curl -s --max-time 3 http://localhost:11434/api/ps | grep -o '\"name\":\"[^\"]*' | cut -d'\"' -f4 | head -1); echo \"${M:-ninguno ahora mismo}\"",
     "Modelo cargado: {ultima}."),
    ("apagar_pantalla", "apaga la pantalla|apagar pantalla",
     "Enseguida.", "xset dpms force off", "Pantalla apagada."),
    # --- administracion: necesitan root, disparan el dialogo de contraseña ---
    # (Las de pentesting van en su propia lista, PENTEST, mas abajo.)
    ("actualizar_lista", "actualiza los repositorios|refresca los paquetes|actualiza la lista de paquetes",
     "Voy, necesito permiso de administrador.", "sudo apt update",
     "Repositorios actualizados, señor.", True),
    ("espacio_liberar", "limpia los paquetes viejos|libera espacio de apt|limpia el cache de paquetes",
     "Necesito permiso para esto.", "sudo apt autoclean",
     "Cache de paquetes limpiada, señor.", True),
    ("servicio_reiniciar", "reinicia el servicio de red|reinicia la red",
     "Necesito permiso de administrador.", "sudo systemctl restart NetworkManager",
     "Red reiniciada, señor.", True),
    ("fecha_sincronizar", "sincroniza la hora|pon la hora en hora",
     "Un momento, con permiso.", "sudo systemctl restart systemd-timesyncd",
     "Hora sincronizada, señor.", True),
    ("bloquear", "bloquea la sesion|bloquear pantalla|echa el cerrojo",
     "Ahora mismo.", "loginctl lock-session", "Sesion bloqueada."),
    ("volumen_subir", "sube el volumen|mas volumen",
     "", "pactl set-sink-volume @DEFAULT_SINK@ +10%", "Volumen subido."),
    ("volumen_bajar", "baja el volumen|menos volumen",
     "", "pactl set-sink-volume @DEFAULT_SINK@ -10%", "Volumen bajado."),
    ("volumen_silencio", "silencia el sistema|quita el sonido|mutea el sistema",
     "", "pactl set-sink-mute @DEFAULT_SINK@ toggle", "Sonido conmutado."),
    ("brillo_subir", "sube el brillo|mas brillo",
     "", "brightnessctl set +10% 2>/dev/null || xrandr --output $(xrandr | awk '/ connected/{print $1; exit}') --brightness 1",
     "Brillo subido."),
    ("captura_pantalla", "hazme una captura|captura de pantalla|haz una foto de la pantalla",
     "Enseguida se la hago.", "D=$(xdg-user-dir PICTURES 2>/dev/null || echo ~); F=$D/captura-$(date +%H%M%S).png; import -window root \"$F\" 2>/dev/null || gnome-screenshot -f \"$F\" 2>/dev/null || ffmpeg -loglevel error -f x11grab -video_size $(xdpyinfo | awk \"/dimensions/{print \\$2}\") -i $DISPLAY -frames:v 1 -y \"$F\" 2>/dev/null; [ -s \"$F\" ] && echo \"$F\"",
     "Captura guardada."),
    ("papelera_tamano", "cuanto ocupa la papelera|papelera llena",
     "Voy.", "du -sh ~/.local/share/Trash 2>/dev/null | cut -f1",
     "La papelera ocupa {ultima}."),
    ("descargas_listar", "que hay en descargas|ultimas descargas|que me he bajado",
     "Voy.", "ls -t ~/Downloads 2>/dev/null | head -6", "Hay {lineas} cosas en descargas"),
    ("descargas_abrir", "abre la carpeta de descargas|abreme descargas",
     "Enseguida se la abro.", "xdg-open ~/Downloads", "Carpeta de descargas abierta."),
    ("documentos_abrir", "abre mis documentos|abreme documentos",
     "Enseguida.", "xdg-open ~/Documents", "Documentos abierto."),
    ("cofre_abrir", "abre el cofre|abreme el cofre",
     "Enseguida.", "xdg-open ~/COFRE", "COFRE abierto."),
    ("ficheros_recientes", "que he tocado ultimamente|ficheros recientes|en que estaba trabajando",
     "Lo miro.",
     "find ~ -maxdepth 3 -type f -mmin -180 -not -path '*/.*' 2>/dev/null | head -6",
     "Tocado en las ultimas horas: {salida}"),
    ("grandes", "que ocupa mas sitio|que tengo en descargas|que me esta llenando la carpeta",
     "Lo miro.",
     "du -ah ~/Downloads ~/Descargas ~/Documents ~/Desktop 2>/dev/null | sort -rh | head -6",
     "Lo mas grande: {salida}"),
]

# ------------------------------------------------------- con argumento
CAPTURA = [
    ("buscar_fichero", "busca el fichero|buscame el fichero|encuentra el fichero",
     "Lo busco.", "timeout 12 find $HOME/Documents $HOME/Desktop $HOME/COFRE/CODERS $HOME/Downloads -iname '*{argumento}*' -not -path '*/.*' -not -path '*/node_modules/*' -not -path '*venv*' 2>/dev/null | head -12",
     "He encontrado {lineas}"),
    ("buscar_carpeta", "busca la carpeta|encuentra la carpeta",
     "La busco.", "find ~ -type d -iname '*{argumento}*' -not -path '*/.*' 2>/dev/null | head -12",
     "Carpetas encontradas: {salida}"),
    # busca un TEXTO dentro de los ficheros, recursivo, con ruta y linea
    ("grep_recursivo", "busca el texto|busca la palabra en mis ficheros|donde pone|que fichero contiene",
     "Lo busco en sus ficheros.", "timeout 12 grep -rIn --exclude-dir='.*' --exclude-dir='*venv*' --exclude-dir=node_modules --exclude-dir=__pycache__ --exclude-dir=modelos --exclude-dir=muestras -m1 {argumento} $HOME/Documents $HOME/Desktop $HOME/COFRE/CODERS 2>/dev/null | head -12",
     "{lineas} ficheros lo contienen."),
    ("leer_fichero", "leeme el fichero|abre y leeme|lee el fichero",
     "Voy a leerlo.", "f=$(timeout 12 find $HOME/Documents $HOME/Desktop $HOME/COFRE/CODERS -iname '*{argumento}*' -type f -not -path '*/.*' 2>/dev/null | head -1); [ -n \"$f\" ] && head -c 800 \"$f\" || echo \"no lo encuentro\"",
     "{salida}, señor"),
    ("abrir_fichero", "abre el fichero|abreme el fichero",
     "Enseguida se lo abro.",
     "f=$(find ~ -iname '*{argumento}*' -type f -not -path '*/.*' 2>/dev/null | head -1); "
     "[ -n \"$f\" ] && xdg-open \"$f\" && echo \"$f\" || echo 'no lo encuentro'",
     "Abierto {ultima}."),
    ("abrir_carpeta", "abre la carpeta|abreme la carpeta",
     "Enseguida.", "a={argumento}; if [ -d \"$a\" ]; then d=\"$a\"; else d=$(timeout 12 find $HOME/Documents $HOME/Desktop $HOME/COFRE $HOME/Downloads -type d -iname \"*$a*\" -not -path \"*/.*\" -not -path \"*/node_modules/*\" 2>/dev/null | head -1); fi; if [ -n \"$d\" ]; then xdg-open \"$d\" >/dev/null 2>&1 & echo \"Abierta $(basename \"$d\")\"; else echo \"No encuentro esa carpeta\"; fi",
     "{ultima}"),
    ("tamano_carpeta", "cuanto ocupa la carpeta|tamaño de la carpeta",
     "Lo calculo.",
     "a={argumento}; if [ -d \"$a\" ]; then d=\"$a\"; else d=$(timeout 12 find $HOME/Documents $HOME/Desktop $HOME/COFRE $HOME/Downloads -type d -iname \"*$a*\" -not -path \"*/.*\" 2>/dev/null | head -1); fi; [ -n \"$d\" ] && du -sh \"$d\" 2>/dev/null | cut -f1 || echo \"no la encuentro\"",
     "Ocupa {ultima}."),
    ("proceso_buscar", "esta corriendo|se esta ejecutando|hay algun proceso de",
     "Lo compruebo.", "pgrep -c -f {argumento} 2>/dev/null || echo 0",
     "Hay {ultima} procesos que encajan."),
    ("proceso_matar", "mata el proceso|cierra el proceso|termina el proceso",
     "Antes de matarlo, confirmeme.", "pkill -f {argumento}",
     "Proceso terminado.", True),
    ("instalado", "tengo instalado|esta instalado",
     "Lo miro.", "command -v {argumento} >/dev/null && echo si || echo no",
     "Instalado: {ultima}."),
    ("manual", "para que sirve el comando|que hace el comando",
     "Voy.", "whatis {argumento} 2>/dev/null | head -1", "{salida}"),
]

# --------------------------------------------------------------- oasis
# --------------------------------------------------------------- pentesting
# Red de seguridad: los escaneos ACTIVOS (que mandan paquetes a otros) llevan
# confirmar=True, asi que el dialogo muestra el comando exacto antes de lanzarse
# y la voz no puede disparar un escaneo sola. Los que apuntan a un objetivo se
# limitan a la RED LOCAL por construccion (autodetectan la subred o exigen IP
# privada); un objetivo de fuera se rechaza. Lo de solo lectura —buscar un
# exploit en la base local, resolver un dominio— va directo, no toca a nadie.
#
# Que apunte solo a la red local es a proposito: un asistente por voz que
# escanea lo que crea haber entendido, con salida al host, es un incidente
# esperando. Para objetivos de fuera, mejor a mano y con intencion.
PENTEST = [
    ("red_dispositivos", "escanea mi red|que hay en mi red|dispositivos en la red|quien esta en la red",
     "Voy a escanear la red local, necesito su permiso.",
     "R=$(ip route | awk '/proto.*src/{for(i=1;i<=NF;i++)if($i~/\\//)print $i; exit}'); "
     "sudo nmap -sn ${R:-192.168.1.0/24} | grep -E 'Nmap scan|MAC' | head -30",
     "Estos equipos hay en la red, señor: {salida}", True),
    ("puertos_host", "que puertos tiene abiertos|escanea los puertos de|escanea el host",
     "Un escaneo de puertos, con su permiso.",
     "T={argumento}; echo \"$T\" | grep -qE '^(10\\.|172\\.(1[6-9]|2[0-9]|3[01])\\.|192\\.168\\.|127\\.|localhost)' "
     "&& nmap -F \"$T\" | grep -E '^[0-9]+/|Nmap scan' | head -20 "
     "|| echo 'Solo objetivos de la red local, señor.'",
     "{salida}", True),
    ("mis_puertos", "que puertos tengo abiertos yo|mis puertos abiertos|que estoy exponiendo",
     "Lo compruebo.",
     "ss -ltnp 2>/dev/null | awk 'NR>1{print $4}' | sort -u | head -15",
     "Tengo {lineas} puertos a la escucha"),
    ("exploit_buscar", "busca un exploit para|busca exploits de|hay algun exploit de",
     "Lo busco en la base de exploits.",
     "searchsploit {argumento} 2>/dev/null | grep -vE '^-|Shellcode' | head -12",
     "Esto he encontrado, señor: {salida}"),
    ("dominio_resolver", "resuelve el dominio|que ip tiene el dominio|a que ip apunta",
     "Lo resuelvo.", "dig +short {argumento} 2>/dev/null | head -5",
     "Resuelve a: {salida}"),
    ("dominio_whois", "de quien es el dominio|quien registro|whois de",
     "Lo consulto.", "whois {argumento} 2>/dev/null | grep -iE 'registrar:|creation|expir|org' | head -6",
     "{salida}"),
    ("cabeceras_web", "que servidor usa|cabeceras de|que tecnologia usa la web",
     "Miro las cabeceras.",
     "curl -sI --max-time 8 {argumento_url} 2>/dev/null | grep -iE 'server:|x-powered|location' | head -6",
     "{salida}"),
    ("hash_identificar", "que tipo de hash es|identifica el hash",
     "Lo miro.",
     "H={argumento}; L=${#H}; case $L in 32)echo 'MD5, probablemente';; 40)echo 'SHA1';; "
     "64)echo 'SHA256';; *)echo \"$L caracteres, no lo tengo claro\";; esac",
     "{ultima}, señor."),
]

# las con argumento del bloque PENTEST (para marcar captura=True)
PENTEST_CAPTURA = {"puertos_host", "exploit_buscar", "dominio_resolver",
                   "dominio_whois", "cabeceras_web", "hash_identificar"}

OASIS = [
    ("oasis_corriendo", "esta oasis funcionando|esta corriendo oasis|oasis esta levantado",
     "Lo compruebo.",
     "pgrep -c -f 'oasis|ssb-server|sbot' 2>/dev/null || echo 0",
     "Procesos de Oasis en marcha: {ultima}."),
    ("oasis_sincronizando", "estan sincronizando los pubs|como van los pubs|se estan sincronizando los pubs",
     "Voy a mirar las conexiones.",
     "python3 -c \"import json,time; d=json.load(open('$HOME/.ssb/conn.json')); "
     "act=sum(1 for v in d.values() if time.time()*1000-v.get('stateChange',0)<300000); "
     "fail=sum(1 for v in d.values() if v.get('failure',0)>0); "
     "print(f'{len(d)} pubs, {act} activos hace poco, {fail} con fallos')\"",
     "{ultima}."),
    ("oasis_conexiones", "cuantos pubs hay conectados|conexiones de oasis",
     "Un momento.",
     "cat ~/.ssb/conn.json 2>/dev/null | grep -c 'connected' || echo 0",
     "Pubs conectados: {ultima}."),
    ("oasis_tamano", "cuanto ocupa oasis|tamaño de la base de ssb",
     "Lo calculo.", "du -sh ~/.ssb 2>/dev/null | cut -f1",
     "La base de SSB ocupa {ultima}."),
    ("oasis_abrir", "abre oasis|abreme oasis",
     "Enseguida se lo abro.",
     "xdg-open http://localhost:3000 2>/dev/null && echo abierto || echo 'no responde'",
     "Oasis: {ultima}."),
    ("oasis_blobs", "cuanto ocupan los blobs|tamaño de los blobs",
     "Voy.", "du -sh ~/.ssb/blobs 2>/dev/null | cut -f1", "Los blobs ocupan {ultima}."),
]


# Alias foneticos: lo que Whisper devuelve DE VERDAD, no lo que uno escribe.
# Medido con ocho ordenes habladas: "abre el correo" se transcribe "a ver el
# correo" (en castellano suenan igual), "los pubs" sale "los push" o "los
# puos", y "youtube" se parte en "yo tube". Añadirlos sube los aciertos sin
# tocar el modelo ni añadir un milisegundo.
ALIAS = {
    "ff_gmail": ["a ver el correo", "abrir el correo"],
    "oasis_sincronizando": ["estan sincronizando los push", "estan sincronizando los puos",
                            "sincronizando los pubs", "como van los push"],
    "oasis_corriendo": ["esta oasis funcionando", "esta corriendo casis"],
    "ff_buscar_youtube": ["busca en yo tube", "buscan youtube", "buscan yo tube",
                          "busca en you tube"],
    "disco_libre": ["cuando espacio queda", "cuanto espacio quedan"],
    "ff_youtube": ["abre yo tube", "abre you tube"],
    "descargas_abrir": ["abre descargas", "abreme las descargas"],
    "procesos_top": ["que esta consumiendo mas", "quien consume"],
    "temperatura": ["que temperatura tiene", "como esta la temperatura"],
    "bateria": ["cuanta bateria tengo", "como va la bateria"],
}


def con_señor(texto: str) -> str:
    """Mete el trato en las frases fijas.

    Son las que mas suenan —el acuse va en cada orden— y hasta ahora eran las
    unicas que no trataban de señor, asi que el personaje se caia justo ahi.
    """
    if not texto or "señor" in texto.lower():
        return texto
    if texto.endswith("."):
        return texto[:-1] + ", señor."
    if texto.endswith("?"):
        return texto[:-1] + ", señor?"
    return texto + ", señor"


# Abrir una terminal y poner un video: de lo primero que se le pide a un
# asistente de escritorio, y no estaban. Las busquedas van acotadas y con
# tope porque el home son casi mil gigas y un find sin limite no vuelve.
ESCRITORIO = [
    ("terminal_abrir", "abre una terminal|abreme una terminal|abre la terminal|saca una terminal|quiero una terminal",
     "Ahora mismo.", "gnome-terminal 2>/dev/null || kgx 2>/dev/null || xterm 2>/dev/null &",
     "Terminal abierta."),
    ("terminal_aqui", "abre una terminal en el cofre|terminal en el cofre",
     "Enseguida.", "gnome-terminal --working-directory=$HOME/COFRE 2>/dev/null || kgx 2>/dev/null &",
     "Ahi la tiene, en el cofre."),
    ("videos_listar", "que peliculas tengo|que videos tengo|lista mis videos",
     "Un momento, lo miro.", "timeout 12 find $HOME/Videos $HOME/Downloads $HOME/Desktop -type f -regextype posix-extended -iregex '.*\\.(mp4|mkv|avi|webm|mov|m4v)$' 2>/dev/null | head -12",
     "He encontrado {lineas}."),
]

# esta captura el nombre de la pelicula
ESCRITORIO_CAPTURA = [
    ("pelicula_abrir", "pon la pelicula|abre la pelicula|reproduce el video|pon el video|quiero ver",
     "Enseguida se la pongo.", "v=$(timeout 12 find $HOME/Videos $HOME/Downloads $HOME/Desktop -type f -regextype posix-extended -iregex '.*{argumento}.*\\.(mp4|mkv|avi|webm|mov|m4v)$' 2>/dev/null | head -1); if [ -n \"$v\" ]; then xdg-open \"$v\" >/dev/null 2>&1 & echo \"Poniendo $(basename \"$v\")\"; else echo \"No encuentro ese video\"; fi",
     "{ultima}"),
]

# Ventanas y pantallas. Toda la logica vive en config/ventanas.sh (un script del
# host): aqui solo estan las frases y la llamada.
#
# Las frases llevan TRES palabras o mas y evitan a proposito ser subcadena de
# las que ya existen con "pantalla" o "ventana" dentro —apagar_pantalla,
# bloquear, captura_pantalla, ff_firefox_privado, ff_firefox_cerrar,
# proceso_matar, terminal_abrir—, porque el emparejador gana por frase mas
# larga sin mirar el grupo y una frase mal elegida se las come.
VENTANAS = [
    ("ventanas_listar", "que ventanas tengo abiertas|que ventanas hay abiertas|lista las ventanas",
     "Un momento, lo miro.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh listar",
     "Tiene {lineas} ventanas abiertas."),

    ("ventana_izquierda", "pon la ventana a la izquierda|manda la ventana a la izquierda|esta ventana a la izquierda",
     "Ahora mismo.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh zona esta izquierda", "{ultima}"),

    ("ventana_derecha", "pon la ventana a la derecha|manda la ventana a la derecha|esta ventana a la derecha",
     "Ahora mismo.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh zona esta derecha", "{ultima}"),

    ("ventana_arriba", "pon la ventana arriba del todo|manda la ventana arriba",
     "Voy.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh zona esta arriba", "{ultima}"),

    ("ventana_abajo", "pon la ventana abajo del todo|manda la ventana abajo",
     "Voy.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh zona esta abajo", "{ultima}"),

    ("ventana_centrar", "centra la ventana activa|pon la ventana en el centro",
     "Enseguida.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh zona esta centro", "{ultima}"),

    ("ventana_maximizar", "maximiza la ventana activa|pon la ventana a pantalla completa",
     "Ahora mismo.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh maximiza esta", "{ultima}"),

    ("ventana_restaurar", "restaura la ventana activa|quita la pantalla completa",
     "Voy.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh restaura esta", "{ultima}"),

    ("ventana_minimizar", "minimiza la ventana activa|esconde la ventana activa",
     "Voy.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh minimiza esta", "{ultima}"),
]

# Estas capturan lo que se dice detras: el nombre de la pantalla, el numero de
# escritorio o el mote que se le quiera poner a un monitor.
VENTANAS_CAPTURA = [
    ("ventana_a_pantalla", "manda la ventana a la pantalla|pasa la ventana a la pantalla",
     "Ahora mismo.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh mover esta {argumento}", "{ultima}"),

    ("ventana_al_escritorio", "manda la ventana al escritorio|pasa la ventana al escritorio",
     "Voy.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh escritorio esta {argumento}", "{ultima}"),

    ("pantalla_nombrar", "a esta pantalla llamala|llama a esta pantalla",
     "Anotado.", "$HOME/COFRE/CODERS/JARVIS/config/ventanas.sh alias 1 {argumento}", "{ultima}"),
]


def fila(t, grupo, captura=False):
    ident, frases, acuse, comando, respuesta = t[:5]
    confirmar = len(t) > 5 and t[5]
    return {
        "id": ident,
        "grupo": grupo,
        "frases": frases.split("|"),
        "acuse": con_señor(acuse or "Voy."),
        "comando": comando,
        "host": True,
        "respuesta": con_señor(respuesta),
        "confirmar": bool(confirmar),
        "captura": captura,
    }


def main():
    acciones = [fila(t, "sistema") for t in SISTEMA]
    acciones += [fila(t, "sistema") for t in ESCRITORIO]
    acciones += [fila(t, "sistema", captura=True) for t in ESCRITORIO_CAPTURA]
    acciones += [fila(t, "ventanas") for t in VENTANAS]
    acciones += [fila(t, "ventanas", captura=True) for t in VENTANAS_CAPTURA]
    acciones += [fila(t, "ficheros", captura=True) for t in CAPTURA]
    acciones += [fila(t, "oasis") for t in OASIS]
    acciones += [fila(t, "pentest", captura=t[0] in PENTEST_CAPTURA) for t in PENTEST]

    # las de firefox viven en su propio fichero, que son otras cincuenta
    firefox = pathlib.Path(__file__).parent / "_firefox.py"
    if firefox.exists():
        import importlib.util
        spec = importlib.util.spec_from_file_location("firefox", firefox)
        mod = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(mod)
        acciones += mod.acciones()

    # Los alias se aplican aqui, sobre la lista completa: si se hicieran al
    # construir cada fila, las de firefox se quedarian fuera porque se generan
    # en otro fichero. Se aprende por las malas.
    for accion in acciones:
        extra = ALIAS.get(accion["id"])
        if extra:
            accion["frases"] += [f for f in extra if f not in accion["frases"]]

    salida = pathlib.Path(__file__).parent / "acciones.json"
    salida.write_text(json.dumps({"version": 1, "acciones": acciones},
                                 indent=2, ensure_ascii=False) + "\n")

    ids = [a["id"] for a in acciones]
    print(f"{len(acciones)} acciones escritas en {salida.name}")
    if len(ids) != len(set(ids)):
        repes = {i for i in ids if ids.count(i) > 1}
        print(f"CUIDADO, ids repetidos: {repes}")
    for grupo in sorted({a["grupo"] for a in acciones}):
        print(f"  {grupo:<10} {sum(1 for a in acciones if a['grupo'] == grupo)}")


if __name__ == "__main__":
    main()
