#!/usr/bin/env python3
"""El panel: que lo que manda el nucleo sea lo que el HUD sabe leer.

    ./venv/bin/python pruebas/prueba_cara.py

## Por que existe esta prueba

Montando la telemetria, `ps` devolvia el uso de CPU como cadena y el nucleo la
mandaba tal cual. El HUD hace `p.cpu.toFixed(0)`, y una cadena no tiene
`toFixed`: la excepcion abortaba el manejador ENTERO y se llevaba por delante
todo lo que se pinta despues. En pantalla se veia que CPU, RAM y grafica
salian en "--", y el fallo no apuntaba a los procesos por ningun lado.

Ese es el problema de un puente sin contrato: un campo con el tipo equivocado
apaga media pantalla y no dice donde. Aqui se fija el contrato —que campo, de
que tipo y en que unidad— comprobando ademas que `hud.js` lo sigue usando asi,
para que enterarse no dependa de mirar la pantalla.
"""
import json
import os
import re
import sys

AQUI = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, AQUI)

from cara.panel import Telemetria                  # noqa: E402

HUD = os.path.join(AQUI, "cara", "hud")

fallos = []


def afirma(condicion, queja):
    print(f"  {'OK ' if condicion else 'MAL'} {queja}")
    if not condicion:
        fallos.append(queja)


def numero(v):
    return isinstance(v, (int, float)) and not isinstance(v, bool)


def main() -> int:
    print("\n  el puente esta puesto")
    with open(os.path.join(HUD, "index.html"), encoding="utf-8") as f:
        html = f.read()
    afirma("puente.js" in html, "index.html carga puente.js")
    # Se comparan las etiquetas <script>, no el texto suelto: el comentario que
    # hay encima menciona hud.js y una busqueda ingenua lo encuentra antes.
    scripts = re.findall(r'<script\s+src="([^"]+)"', html)
    afirma("puente.js" in scripts and "hud.js" in scripts
           and scripts.index("puente.js") < scripts.index("hud.js"),
           "y lo carga ANTES que hud.js, que pregunta por window.webkit al arrancar")
    with open(os.path.join(HUD, "puente.js"), encoding="utf-8") as f:
        puente = f.read()
    afirma("WebSocket" in puente, "el puente habla por WebSocket")
    afirma("messageHandlers" in puente,
           "suplanta window.webkit, asi hud.js no se toca")
    afirma("cola" in puente, "guarda lo pulsado mientras reconecta")

    print("\n  la telemetria: campos, tipos y unidades")
    d = Telemetria().lee()
    Telemetria().lee()          # el segundo tiene ya las diferencias de cpu

    afirma(numero(d.get("cpu")), "cpu es un numero")
    afirma(isinstance(d.get("nucleos"), list) and all(numero(x) for x in d["nucleos"]),
           f"nucleos es una lista de numeros ({len(d.get('nucleos', []))})")
    for clave in ("ram", "swap"):
        v = d.get(clave, {})
        afirma(numero(v.get("usada")) and numero(v.get("total")),
               f"{clave} lleva usada y total")
        afirma(0 < v.get("total", 0) < 1024,
               f"{clave} viene en GB, no en MB ({v.get('total', 0):.1f})")
    disco = d.get("disco", {})
    afirma(0 < disco.get("total", 0) < 200,
           f"disco viene en TB, no en GB ({disco.get('total', 0):.2f})")
    afirma(isinstance(d.get("carga"), list),
           "carga es una lista: el HUD hace carga.map()")
    afirma(d.get("uptime", 0) > 60,
           "uptime va en segundos: el HUD decide solo si pone minutos o dias")

    if "gpu" in d:
        g = d["gpu"]
        afirma(all(numero(g.get(k)) for k in ("uso", "vram_usada", "vram_total")),
               "la grafica manda uso, vram_usada y vram_total")
        afirma(0 < g.get("vram_total", 0) < 128,
               f"la vram viene en GB ({g.get('vram_total', 0):.1f})")

    print("\n  los procesos: el campo que apago media pantalla")
    procs = d.get("procesos", [])
    afirma(bool(procs), f"llegan procesos ({len(procs)})")
    for p in procs[:3]:
        afirma(numero(p.get("cpu")) and numero(p.get("mem")),
               f"{p.get('nombre','?')}: cpu y mem son numeros, no cadenas")
    afirma(all("pid" in p for p in procs), "todos traen pid, que el HUD pone en el title")

    print("\n  y el HUD sigue esperando eso mismo")
    with open(os.path.join(HUD, "hud.js"), encoding="utf-8") as f:
        js = f.read()
    # Si alguien cambia hud.js para leer otro campo, esta prueba no lo sabria
    # mirando solo al nucleo: se comprueba en el propio JavaScript.
    for campo in ("datos.cpu", "datos.ram", "datos.swap", "datos.disco",
                  "datos.nucleos", "datos.procesos"):
        afirma(campo in js, f"hud.js lee {campo}")
    afirma("p.cpu.toFixed" in js,
           "hud.js llama a toFixed sobre el cpu de cada proceso (por eso tiene que ser numero)")

    # Todo lo que se manda tiene que poder viajar por el socket.
    print("\n  serializable")
    try:
        json.dumps(d)
        afirma(True, "la telemetria entera cabe en JSON")
    except (TypeError, ValueError) as e:
        afirma(False, f"la telemetria no es JSON: {e}")

    print(f"\n  {'todo en orden' if not fallos else str(len(fallos)) + ' fallan'}\n")
    return 1 if fallos else 0


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