"""Ayudas de la semana 14: levantar la topología y correr comandos en sus nodos."""
import os
import subprocess
import time
import unittest

HERE = os.path.dirname(os.path.abspath(__file__))
# La solución no se publica. Sin ella, se usa lo que ustedes escriben en lab/.
ANSWERS = os.path.join(HERE, "solution")
if not os.path.isdir(ANSWERS):
    ANSWERS = os.path.join(HERE, "lab")

# scenarios.py lo prende para mostrar cada comando que corre.
ECHO = False


def answer_path(name):
    """Devuelve la ruta de un archivo del laboratorio, o salta si sigue con el TODO."""
    path = os.path.join(ANSWERS, name)
    with open(path, encoding="utf-8") as file:
        if "TODO" in file.read():
            raise unittest.SkipTest(f"falta completar lab/{name}")
    return path


def run(args, timeout=240, stdin=None):
    return subprocess.run(args, capture_output=True, text=True, timeout=timeout, stdin=stdin)


class Lab:
    """Una topología de Containerlab que se levanta y se destruye desde las pruebas."""

    def __init__(self, folder, name, network):
        self.folder = os.path.join(HERE, folder)
        self.topology = f"{folder}.clab.yml"
        self.prefix = f"clab-{name}-"
        self.network = network

    def deploy(self):
        done = subprocess.run(
            ["containerlab", "deploy", "-t", self.topology, "--reconfigure"],
            cwd=self.folder, capture_output=True, text=True, timeout=400)
        if done.returncode != 0:
            raise RuntimeError(done.stdout + done.stderr)

    def destroy(self):
        subprocess.run(["containerlab", "destroy", "-t", self.topology, "--cleanup"],
                       cwd=self.folder, capture_output=True, text=True, timeout=400)
        subprocess.run(["docker", "network", "rm", self.network], capture_output=True, text=True)

    def sh(self, node, *command, timeout=90):
        """Corre un comando en un nodo y devuelve el resultado completo."""
        if ECHO:
            print(f"  {node}# {' '.join(command)}", flush=True)
        return run(["docker", "exec", self.prefix + node, *command], timeout=timeout)

    def out(self, node, *command, timeout=90):
        """Corre un comando en un nodo y devuelve stdout y stderr juntos."""
        done = self.sh(node, *command, timeout=timeout)
        return done.stdout + done.stderr

    def daemon(self, node, command):
        """Deja un comando corriendo en segundo plano dentro de un nodo."""
        if ECHO:
            print(f"  {node}# {command} &", flush=True)
        run(["docker", "exec", "-d", self.prefix + node, "sh", "-c", command])


def wait_until(predicate, seconds=30, step=0.5):
    """Espera a que una condición se cumpla; devuelve si se cumplió."""
    deadline = time.monotonic() + seconds
    while time.monotonic() < deadline:
        if predicate():
            return True
        time.sleep(step)
    return predicate()
