#!/usr/bin/env python3
# Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
"""Generate the example index in docs/EXAMPLES.md from the examples/ directory.

The prose (intro, build-and-run, troubleshooting) is hand-maintained; the per-group
lists of examples and the total count are generated by scanning `examples/`, so they
can never drift from the tree (they had - the Foundation group silently lost 16
entries before this existed). Each OSI-layer group dir is listed in order, its
`NN.Name` example dirs sorted by number, as `·`-separated links.

Run from the repo root:
    python docs/utilities/gen_examples.py            # rewrite the index
    python docs/utilities/gen_examples.py --check    # CI: fail if stale
"""

import os
import re
import sys

ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
EXAMPLES_DIR = os.path.join(ROOT, "examples")
EXAMPLES_MD = os.path.join(ROOT, "docs", "EXAMPLES.md")

BEGIN = "<!-- BEGIN GENERATED EXAMPLE INDEX (docs/utilities/gen_examples.py) -->"
END = "<!-- END GENERATED EXAMPLE INDEX -->"

# Ordered OSI-layer groups: (directory name, heading, one-line description).
GROUPS = [
    (
        "Foundation",
        "Foundation",
        "Start here: the core tutorial path (01-05), then the v5 hardware-integration "
        "examples - DMA, the preempting queue, forwarding, radio gateways, and capture:",
    ),
    ("L4-Transport", "L4 Transport", "Connections, encryption, and flood defense:"),
    ("L5-Session", "L5 Session", "Interactive consoles:"),
    ("L6-Presentation", "L6 Presentation", "Parsing, codecs, auth, WebSocket/SSE:"),
    ("L7-Application", "L7 Application", "Routing, protocols, services, and clients:"),
]


def examples_in(group_dir):
    """Return the `NN.Name` example dirs under a group, sorted by the numeric prefix."""
    path = os.path.join(EXAMPLES_DIR, group_dir)
    names = [n for n in os.listdir(path) if os.path.isdir(os.path.join(path, n)) and re.match(r"^\d+\.", n)]
    return sorted(names, key=lambda n: int(n.split(".", 1)[0]))


def build_index():
    """Return (marked block string, total example count)."""
    parts = [BEGIN, ""]
    total = 0
    for group_dir, heading, desc in GROUPS:
        names = examples_in(group_dir)
        total += len(names)
        links = [f"[{n}](../examples/{group_dir}/{n})" for n in names]
        parts += [f"## {heading}", "", desc, "", " ·\n".join(links), ""]
    parts.append(END)
    return "\n".join(parts), total


def apply_to(path, check):
    with open(path, "r", encoding="utf-8", newline="") as f:
        text = f.read()
    nl = "\r\n" if "\r\n" in text else "\n"
    flat = text.replace("\r\n", "\n")
    if BEGIN not in flat or END not in flat:
        raise SystemExit(f"{path}: missing the {BEGIN!r} / {END!r} markers")
    block, total = build_index()
    i = flat.index(BEGIN)
    j = flat.index(END) + len(END)
    new = flat[:i] + block + flat[j:]
    new = re.sub(r"ships \d+ runnable examples", f"ships {total} runnable examples", new, count=1)
    new = new.replace("\n", nl)
    if check:
        return new == text
    if new != text:
        with open(path, "w", encoding="utf-8", newline="") as f:
            f.write(new)
        print(f"updated {os.path.relpath(path, ROOT)} ({total} examples)")
    else:
        print(f"unchanged {os.path.relpath(path, ROOT)}")
    return True


def main():
    check = "--check" in sys.argv[1:]
    if not apply_to(EXAMPLES_MD, check) and check:
        print("STALE: docs/EXAMPLES.md (run gen_examples.py)")
        sys.exit(1)


if __name__ == "__main__":
    main()
