#!/usr/bin/env python3
"""Juez del curso: corre un programa con cada caso de una carpeta y compara la salida.

    juez.py PROGRAMA CARPETA [--tiempo 2] [--verificador PROGRAMA]

Cada caso son dos archivos, NN.in y NN.out. La salida se compara palabra por palabra, así
que los espacios y los saltos de línea de más no importan. Cuando un problema admite varias
respuestas correctas, un verificador decide: se llama con `verificador NN.in NN.out salida`
y termina con 0 si la salida es correcta.

Veredictos: AC correcto, WA respuesta incorrecta, TLE se pasó del tiempo, RE terminó con error.
Termina con 0 solo si todos los casos dan AC.
"""
import argparse
import os
import subprocess
import sys
import tempfile
import time
from pathlib import Path


def judge(program, case, limit, checker):
    """Corre un caso y devuelve (veredicto, segundos, detalle)."""
    start = time.monotonic()
    with case.open("rb") as stdin, tempfile.NamedTemporaryFile(suffix=".salida") as out:
        try:
            run = subprocess.run([program], stdin=stdin, stdout=out, stderr=subprocess.PIPE, timeout=limit)
        except subprocess.TimeoutExpired:
            return "TLE", limit, ""
        seconds = time.monotonic() - start
        if run.returncode != 0:
            return "RE", seconds, f"código de salida {run.returncode}"
        expected = case.with_suffix(".out")
        if checker:
            check = subprocess.run(
                [checker, str(case), str(expected), out.name],
                stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True,
            )
            return ("AC", seconds, "") if check.returncode == 0 else ("WA", seconds, check.stdout.strip())
        got = Path(out.name).read_bytes().split()
        want = expected.read_bytes().split()
        if got == want:
            return "AC", seconds, ""
        for i, (a, b) in enumerate(zip(got, want)):
            if a != b:
                return "WA", seconds, f"palabra {i + 1}: esperaba {b.decode(errors='replace')}, salió {a.decode(errors='replace')}"
        return "WA", seconds, f"esperaba {len(want)} palabras, salieron {len(got)}"


def main():
    parser = argparse.ArgumentParser(description="Corre un programa con los casos de una carpeta.")
    parser.add_argument("programa")
    parser.add_argument("carpeta", type=Path)
    parser.add_argument("--tiempo", type=float, default=2.0, help="segundos por caso")
    parser.add_argument("--verificador", help="programa que decide si una salida es correcta")
    args = parser.parse_args()

    program = os.path.abspath(args.programa)
    checker = os.path.abspath(args.verificador) if args.verificador else None
    cases = sorted(args.carpeta.glob("*.in"))
    if not cases:
        sys.exit(f"No hay casos (*.in) en {args.carpeta}")

    passed = 0
    for case in cases:
        verdict, seconds, detail = judge(program, case, args.tiempo, checker)
        passed += verdict == "AC"
        print(f"{case.stem:<12} {verdict:<4} {seconds:6.2f} s" + (f"  {detail}" if detail else ""))
    print(f"{passed}/{len(cases)} casos correctos")
    sys.exit(0 if passed == len(cases) else 1)


if __name__ == "__main__":
    main()
