#!/usr/bin/env python3
"""El candado: que tape, que rechace lo que no es, y que NO obedezca.

    ./venv/bin/python pruebas/prueba_candado.py

Lo que de verdad importa aqui no es que el panel se ponga negro —eso es
cosmetica— sino que con el candado puesto **no se ejecute nada**. El panel tapado
pero obedeciendo seria peor que no tener candado, porque da sensacion de
seguridad sin darla.

No se prueba con la contraseña de verdad: no la tenemos ni hace falta. Se
comprueba que una mala se rechaza y que mientras tanto no pasa ni una orden.
"""
import os
import sys
import threading
import time

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

from boca.voz import Boca                             # noqa: E402
from cara.panel import Panel                          # noqa: E402
from manos import Manos                               # noqa: E402

fallos = []


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


class CaraFalsa:
    """Se queda con lo que el panel manda, sin levantar ningun servidor."""

    def __init__(self):
        self.mensajes = []
        self.conectados = 1

    def manda(self, tipo, datos):
        self.mensajes.append((tipo, datos))

    def de(self, tipo):
        return [d for t, d in self.mensajes if t == tipo]


class BocaMuda(Boca):
    """Como la de verdad pero sin sonar: la prueba no tiene que hablar."""

    def __init__(self):
        super().__init__("davefx")
        self.dicho = []

    def di(self, texto):
        self.dicho.append(texto); return True

    def di_si_libre(self, texto):
        self.dicho.append(texto); return True


def main() -> int:
    manos = Manos()
    cara = CaraFalsa()
    boca = BocaMuda()
    p = Panel(cara, boca, manos, candado=True)

    print("\n  al abrir")
    p.arranca()
    time.sleep(0.2)
    afirma(not p.desbloqueado, "nace bloqueado")
    afirma(bool(cara.de("bloqueo")), "pide la contraseña nada mas abrir")

    print("\n  con el candado puesto NO obedece")
    antes = len(cara.mensajes)
    for orden in ("prueba", "voz:siguiente", "parar"):
        p.pulsado("accion", orden)
    time.sleep(1.0)
    afirma(not boca.dicho, f"no ha hablado ({boca.dicho})")
    # Desde que la tanda va por tareas, lo que se ve al ejecutar son mensajes
    # "tarea" (una tarjeta por orden), no "respuesta".
    hechas = [d for t, d in cara.mensajes[antes:] if t in ("tarea", "respuesta")]
    afirma(not hechas, f"no ha ejecutado nada ({len(hechas)} tareas o respuestas)")
    # Solo se miran las respuestas a lo pulsado: la telemetria sigue latiendo
    # en su hilo cada segundo y se cuela entre medias.
    contestaciones = [t for t, _ in cara.mensajes[antes:] if t != "telemetria"]
    afirma(contestaciones and all(t == "bloqueo" for t in contestaciones),
           f"a cada intento contesta pidiendo la contraseña ({contestaciones})")

    print("\n  una contraseña mala se rechaza")
    antes = len(cara.mensajes)
    p.pulsado("accion", "desbloquear:no-es-esta-contrasena-12345")
    time.sleep(0.5)
    afirma(not p.desbloqueado, "sigue bloqueado")
    ultimos = cara.de("bloqueo")[-1:] or [{}]
    afirma(ultimos[0].get("error") is True, "y lo dice como error")
    afirma(not cara.de("desbloqueado"), "no manda 'desbloqueado'")

    print("\n  una vacia ni se intenta")
    antes = len(cara.mensajes)
    p.pulsado("accion", "desbloquear:")
    afirma(len(cara.mensajes) == antes, "no molesta a sudo con una clave vacia")

    print("\n  sin candado, obedece")
    cara2, boca2 = CaraFalsa(), BocaMuda()
    p2 = Panel(cara2, boca2, manos, candado=False)
    p2.arranca()
    afirma(p2.desbloqueado, "nace desbloqueado")
    afirma(not cara2.de("bloqueo"), "no pide contraseña")
    p2.pulsado("accion", "prueba")
    time.sleep(3.0)
    tarjetas = {d["id"] for d in cara2.de("tarea")}
    afirma(bool(tarjetas), f"ejecuta de verdad ({len(tarjetas)} tarjetas)")
    afirma(any(d["estado"] == "hecha" for d in cara2.de("tarea")),
           "y las tareas llegan a terminar")

    print("\n  y se puede volver a echar")
    p2.bloquea()
    afirma(not p2.desbloqueado, "bloquea() cierra aunque arrancara sin candado")
    antes = len(cara2.mensajes)
    p2.pulsado("accion", "prueba")
    time.sleep(0.6)
    afirma(not [d for t, d in cara2.mensajes[antes:] if t in ("tarea", "respuesta")],
           "y despues de cerrar ya no obedece")

    p.para(); p2.para()
    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())
