#!/usr/bin/env python3
"""Generate a single Typst unit-test table for chapter 5."""
from __future__ import annotations

import re
from pathlib import Path

try:
    import yaml
except ImportError:
    yaml = None  # type: ignore


def typst_escape(s: str) -> str:
    return s.replace("\\", "\\\\").replace("`", "\\`")


def load_overrides(script_dir: Path) -> dict[str, str]:
    path = script_dir / "test_descriptions.yaml"
    if not path.is_file() or yaml is None:
        return {}
    data = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
    return {str(k): str(v) for k, v in data.items()}


def _split_camel(name: str) -> list[str]:
    parts: list[str] = []
    buf: list[str] = []
    for ch in name:
        if ch.isupper() and buf and buf[-1].islower():
            parts.append("".join(buf))
            buf = [ch]
        else:
            buf.append(ch)
    if buf:
        parts.append("".join(buf))
    return parts


def describe_method(name: str, overrides: dict[str, str]) -> str:
    if name in overrides:
        return overrides[name]
    low = name.lower()
    if "encryption" in low and "wrong" in low:
        return "дешифрование с неверным ключом завершается ошибкой"
    if "encryption" in low and "same" in low:
        return "симметрия шифрования и дешифрования при верном ключе"
    if "correct key" in low:
        return "верный ключ проходит проверку checkKey"
    if "incorrect key" in low:
        return "неверный ключ не проходит проверку checkKey"
    if name.startswith("maps"):
        return "исключение преобразуется в типизированную ошибку Wallenc"
    if name.startswith("syncGroup"):
        rest = _split_camel(name[9:])
        return "синхронизация группы: " + " ".join(w.lower() for w in rest)
    if name.startswith("sync"):
        return "сценарий синхронизации: " + " ".join(w.lower() for w in _split_camel(name[4:]))
    if "Totp" in name or "totp" in name or "Otp" in name or "otp" in name:
        return "корректность TOTP/OTP: " + " ".join(w.lower() for w in _split_camel(name))
    if name.endswith("Works") or "Crud" in name:
        return "CRUD-операции и сохранение данных"
    if "parses" in low or "rejects" in low:
        return "разбор и валидация входных данных"
    if "Route" in name or "Intent" in name or "mapsTo" in name:
        return "маршрутизация, deep link или подписи UI"
    if "enqueue" in low or "cancel" in low or "fail" in low or "progress" in low:
        return "жизненный цикл фоновой задачи"
    words = _split_camel(name)
    return " ".join(w.lower() for w in words)


def main() -> None:
    root = Path(__file__).resolve().parents[2]
    script_dir = Path(__file__).resolve().parent
    out = Path(__file__).resolve().parents[1] / "includes" / "ch05-tests-generated.typ"
    overrides = load_overrides(script_dir)
    rows: list[tuple[str, str, str, str]] = []

    for p in sorted(root.rglob("*.kt")):
        if "/src/test/" not in p.as_posix():
            continue
        text = p.read_text(encoding="utf-8", errors="replace")
        mod = p.parts[p.parts.index("src") - 1]
        for m in re.finditer(r"@Test[\s\S]*?fun\s+(?:`([^`]+)`|(\w+))\s*\(", text):
            name = m.group(1) or m.group(2)
            desc = describe_method(name, overrides)
            rows.append((mod, name, desc))

    rows.sort(key=lambda r: (r[0], r[1]))

    lines = [
        "// AUTO-GENERATED by gen_test_tables.py — include from ch05.typ\n",
        '#import "common.typ": pz-test-table\n\n',
    ]

    data_rows = []
    for i, (mod, name, desc) in enumerate(rows, start=1):
        data_rows.append([str(i), mod, name, desc])

    lines.append("#pz-test-table(\n")
    lines.append("  [Реестр модульных unit-тестов],\n")
    lines.append("  4,\n")
    lines.append("  table.header(\n")
    for h in ["№", "Модуль", "Метод", "Проверяемое поведение"]:
        lines.append(f"    [{typst_escape(h)}],\n")
    lines.append("  ),\n")
    for row in data_rows:
        cells = ", ".join(f"[{typst_escape(c)}]" for c in row)
        lines.append(f"  {cells},\n")
    lines.append(") <tbl-unit-all>\n\n")

    out.write_text("".join(lines), encoding="utf-8")
    print(f"Wrote {out} ({len(rows)} tests)", flush=True)


if __name__ == "__main__":
    main()
