#!/usr/bin/env python3
"""
build_site.py  --  one command to build the whole site from content/.

Implements U4 of docs/plans/2026-08-08-001-feat-no-code-cms-plan.md. Runs the
existing tools in dependency order so a single content edit propagates to every
derived page. This is what CI runs before deploying (U5).

Order matters:
  1. build_content   content/ front-matter + body  ->  detail pages
  2. build_listings  detail pages  ->  News hub + homepage "Latest news"
  3. seo_inject      inject SEO block + rebuild sitemap.xml / robots.txt
  4. build_botlinks  regenerate /links-for-bots/ + llms.txt from the pages
  5. build_search_index  rebuild the offline assistant index

Each step is a separate process (build_search_index chdir's at import, and this
keeps a failing step from corrupting later ones). Any non-zero exit aborts the
build so a broken step never reaches deploy.

Usage:
    python tools/build_site.py             # full build (writes pages)
    python tools/build_site.py --images    # also optimise images -> WebP first
    python tools/build_site.py --check     # non-destructive: verify pages match content/
"""
import argparse
import os
import subprocess
import sys
import time

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# (description, argv relative to ROOT)
BUILD_STEPS = [
    ("Render detail pages from content/", ["tools/build_content.py", "--write"]),
    ("Rebuild News hub + homepage listings", ["tools/build_listings.py"]),
    ("Inject SEO + rebuild sitemap/robots", ["tools/seo_inject.py", "--all", "--sitemap"]),
    ("Rebuild bot/AI discovery files", ["tools/build_botlinks.py"]),
    ("Rebuild offline assistant search index", ["tools/build_search_index.py"]),
]
IMAGES_STEP = ("Optimise images to WebP + update refs", ["optimize_images.py"])
CHECK_STEP = ("Verify rendered pages match content/", ["tools/build_content.py", "--check"])


def _run(desc, argv):
    print(f"\n>>> {desc}\n    $ python {' '.join(argv)}")
    env = {**os.environ, "PYTHONIOENCODING": "utf-8"}
    t0 = time.time()
    result = subprocess.run([sys.executable, *argv], cwd=ROOT, env=env)
    dt = time.time() - t0
    if result.returncode != 0:
        raise SystemExit(
            f"\n!!! Step failed ({result.returncode}): {desc}\n"
            f"    Build aborted — nothing downstream ran, so no partial output is deployed.")
    print(f"    done ({dt:.1f}s)")


def build(images=False):
    steps = list(BUILD_STEPS)
    if images:
        steps.insert(1, IMAGES_STEP)  # after pages exist, before refs are consumed
    print(f"Building site from content/  ({len(steps)} steps)")
    t0 = time.time()
    for desc, argv in steps:
        _run(desc, argv)
    print(f"\nBuild complete in {time.time() - t0:.1f}s. "
          f"Preview locally with: python -m http.server 8000  (see docs/local-preview.md)")


def check():
    print("Verifying the site matches content/ (no files written)...")
    _run(*CHECK_STEP)


if __name__ == "__main__":
    ap = argparse.ArgumentParser(description=__doc__,
                                 formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("--images", action="store_true",
                    help="also run optimize_images.py (WebP conversion + ref rewrite)")
    ap.add_argument("--check", action="store_true",
                    help="verify rendered pages match content/ without writing")
    args = ap.parse_args()
    if args.check:
        check()
    else:
        build(images=args.images)
