#!/usr/bin/env python3
"""Runs the negative controls against a running servidor.py and writes prova.md."""
import json
import os
import re
import statistics
import subprocess
import sys
import time
import urllib.request

HERE = os.path.dirname(os.path.abspath(__file__))
URL = os.environ.get("LLM_URL", "http://127.0.0.1:8785/llm")
RUNS = 3
EQUALS = re.compile(r"igua|repetid|\bpar\b|\bpares\b|mesmo valor|mesmo número|dobr", re.I)
RESULTS = re.compile(r"\b(24|40|48|56)\b")  # sums reachable from 32 + 16 + 8


def call(body, url=URL):
    req = urllib.request.Request(url, json.dumps(body).encode(), {"Content-Type": "application/json"})
    t0 = time.time()
    out = json.load(urllib.request.urlopen(req, timeout=60))
    out["_ms"] = int((time.time() - t0) * 1000)
    return out


def main():
    ex = json.load(open(os.path.join(HERE, "demo", "exemplos.json")))
    lines = ["# Prova do llm-local", "",
             f"Rodada de {time.strftime('%Y-%m-%d %H:%M')}, contra `{URL}`, {RUNS} vezes cada caso, "
             "chamadas em série. Latência medida no cliente (ida e volta HTTP). "
             "Estado do pet: `32 + 16 + 8`, nenhum par igual. Histórico e memórias são os EXEMPLOS fictícios "
             "de `demo/exemplos.json`.", ""]
    lat = {"pet": [], "avaliar": []}
    failures = []

    lines += ["## Pet — controle negativo (não pode sugerir juntar iguais)", "",
              "| pedido | memórias | # | nível | ms | fala | fala em iguais? | dá resultado? |",
              "|---|---|---|---|---|---|---|---|"]
    pairs = {}
    for pedido in ["comentar", "ajuda_1", "ajuda_2", "ajuda_3"]:
        for with_mem in ([True, False] if pedido == "ajuda_2" else [True]):
            for i in range(1, RUNS + 1):
                body = dict(ex["pet"], pedido=pedido)
                if not with_mem:
                    body["memorias_compartilhadas"] = ""
                r = call(body)
                if not r.get("ok"):
                    failures.append(f"pet {pedido} #{i}: {r.get('erro')}")
                    lines.append(f"| {pedido} | {'sim' if with_mem else 'não'} | {i} | FALHA | {r['_ms']} | {r.get('erro')} | | |")
                    continue
                lat["pet"].append(r["_ms"])
                eq = "**SIM**" if EQUALS.search(r["fala"]) else "não"
                res = RESULTS.search(r["fala"])
                res_txt = ("**" + res.group(0) + "**") if res and pedido in ("ajuda_1", "ajuda_2", "comentar") else (res.group(0) if res else "não")
                if eq != "não":
                    failures.append(f"pet {pedido} #{i} mentions equals: {r['fala']}")
                if pedido == "ajuda_2":
                    pairs.setdefault(with_mem, []).append(r["fala"])
                lines.append(f"| {pedido} | {'sim' if with_mem else 'não'} | {i} | {r['nivel_ajuda']} | {r['_ms']} | "
                             f"{r['fala'].replace('|', '/')} | {eq} | {res_txt} |")
    lines += ["", "## Pet — sem memórias × com memórias (mesmo estado, pedido ajuda_2)", "",
              "| # | sem memórias | com memórias |", "|---|---|---|"]
    for i in range(RUNS):
        a = pairs.get(False, [""] * RUNS)[i] if i < len(pairs.get(False, [])) else ""
        b = pairs.get(True, [""] * RUNS)[i] if i < len(pairs.get(True, [])) else ""
        lines.append(f"| {i + 1} | {a.replace('|', '/')} | {b.replace('|', '/')} |")

    lines += ["", "## Avaliar — controle negativo", "",
              "| caso | esperado | # | atende | fonte | ms | comentário |", "|---|---|---|---|---|---|---|"]
    cases = [("jjj", False), ("fora_do_assunto", False), ("boa", True)]
    for name, expected in cases + [("vazia", False)]:
        answer = "" if name == "vazia" else ex["avaliar_casos"][name]
        for i in range(1, RUNS + 1):
            r = call(dict(ex["avaliar"], resposta_da_crianca=answer))
            if not r.get("ok"):
                failures.append(f"avaliar {name} #{i}: {r.get('erro')}")
                lines.append(f"| {name} | {expected} | {i} | FALHA | | {r['_ms']} | {r.get('erro')} |")
                continue
            if r.get("fonte") == "llm":
                lat["avaliar"].append(r["_ms"])
            mark = str(r["atende"]).lower() if r["atende"] == expected else f"**{str(r['atende']).lower()} (ERRADO)**"
            if r["atende"] != expected:
                failures.append(f"avaliar {name} #{i}: atende={r['atende']}")
            lines.append(f"| {name} | {str(expected).lower()} | {i} | {mark} | {r['fonte']} | {r['_ms']} | {r['comentario']} |")

    # Fallback: a second instance with a 1 s timeout must answer ok:false.
    proc = subprocess.Popen([sys.executable, os.path.join(HERE, "servidor.py"), "--porta", "8799", "--host", "127.0.0.1"],
                            env=dict(os.environ, LLM_TIMEOUT="1"), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    time.sleep(1)
    try:
        fb = call(dict(ex["pet"]), url="http://127.0.0.1:8799/llm")
    finally:
        proc.terminate()
    lines += ["", "## Fallback", "",
              f"Instância com `LLM_TIMEOUT=1` → `{json.dumps({k: v for k, v in fb.items() if k != '_ms'}, ensure_ascii=False)}` "
              f"em {fb['_ms']} ms. A página mostra a fala roteirizada."]

    def stats(xs):
        return f"média {statistics.mean(xs) / 1000:.1f} s, mín {min(xs) / 1000:.1f} s, máx {max(xs) / 1000:.1f} s (n={len(xs)})" if xs else "sem dados"
    lines[4:4] = ["## Resumo", "",
                  f"- Latência pet: {stats(lat['pet'])}",
                  f"- Latência avaliar (só chamadas à LLM; a vazia o servidor responde sem LLM): {stats(lat['avaliar'])}",
                  f"- Falhas automáticas: {len(failures)}" + ("" if not failures else " — " + "; ".join(failures)),
                  "- Checagem automática do pet: expressão regular por iguais/par/repetido/dobro, e por 24/40/48/56 "
                  "(resultados possíveis) — em negrito quando aparece em comentar/ajuda_1/ajuda_2. Lida também a olho.", ""]
    with open(os.path.join(HERE, "prova.md"), "w", encoding="utf-8") as f:
        f.write("\n".join(lines) + "\n")
    print("\n".join(lines))


if __name__ == "__main__":
    main()
