#!/usr/bin/env python3
"""build_index.py — generate OKF index.md files for progressive disclosure.

Walks a bundle and, for every directory, writes (or checks) an `index.md` that
lists the concepts and subdirectories it contains. Entries use each concept's
frontmatter `title` and `description` (SPEC §6). Index files contain no
frontmatter, except the bundle root which may declare `okf_version`.

Usage:
    python build_index.py <bundle_dir> [--write] [--okf-version 0.1]

Without --write, prints what *would* change (dry run / check mode) and exits
non-zero if any index is missing or stale.
"""

from __future__ import annotations

import argparse
import os
import sys

from okf_common import AGENT_DOC_FILENAMES, is_reserved, read_document, slugify

RESERVED = {"index.md", "log.md"}
NON_CONCEPT = RESERVED | AGENT_DOC_FILENAMES


def _title_for(abs_path: str, fallback_name: str) -> tuple[str, str]:
    """Return (title, description) for a concept file."""
    try:
        fm, _body, _raw = read_document(abs_path)
    except (OSError, UnicodeDecodeError):
        fm = None
    title = ""
    desc = ""
    if fm:
        title = str(fm.get("title", "")).strip()
        desc = str(fm.get("description", "")).strip()
    if not title:
        title = fallback_name.replace("-", " ").replace("_", " ").title()
    return title, desc


def build_index_for_dir(dirpath: str, bundle_root: str, okf_version: str | None) -> str:
    entries_concepts: list[str] = []
    entries_dirs: list[str] = []

    for name in sorted(os.listdir(dirpath)):
        full = os.path.join(dirpath, name)
        if os.path.isdir(full):
            if name.startswith(".") or name in {"__pycache__"}:
                continue
            sub_title = name.replace("-", " ").replace("_", " ").title()
            sub_desc = _dir_description(full)
            suffix = f" - {sub_desc}" if sub_desc else ""
            entries_dirs.append(f"* [{sub_title}]({name}/){suffix}")
        elif name.lower().endswith(".md") and name.lower() not in NON_CONCEPT:
            title, desc = _title_for(full, os.path.splitext(name)[0])
            suffix = f" - {desc}" if desc else ""
            entries_concepts.append(f"* [{title}]({name}){suffix}")

    is_root = os.path.abspath(dirpath) == os.path.abspath(bundle_root)
    lines: list[str] = []
    if is_root and okf_version:
        lines.append("---")
        lines.append(f'okf_version: "{okf_version}"')
        lines.append("---")
        lines.append("")
    heading = os.path.basename(os.path.abspath(dirpath)).replace("-", " ").replace("_", " ").title() or "Bundle"
    lines.append(f"# {heading}")
    lines.append("")
    if entries_dirs:
        lines.append("## Sections")
        lines.append("")
        lines.extend(entries_dirs)
        lines.append("")
    if entries_concepts:
        lines.append("## Concepts")
        lines.append("")
        lines.extend(entries_concepts)
        lines.append("")
    return "\n".join(lines).rstrip() + "\n"


def _dir_description(dirpath: str) -> str:
    """Best-effort one-line *prose* description for a subdirectory from its index.md.

    Only a genuine prose line counts — headings, list items, tables, blockquotes,
    and the frontmatter block are skipped. Tool-generated index files contain no
    prose line, so this returns "" for them, keeping index generation idempotent.
    """
    idx = os.path.join(dirpath, "index.md")
    if not os.path.isfile(idx):
        return ""
    skip_prefixes = ("#", "---", "*", "-", "|", ">", "<!--")
    try:
        with open(idx, "r", encoding="utf-8") as fh:
            lines = fh.read().splitlines()
    except OSError:
        return ""
    i = 0
    if lines and lines[0].strip() == "---":  # skip frontmatter
        for j in range(1, len(lines)):
            if lines[j].strip() == "---":
                i = j + 1
                break
    for line in lines[i:]:
        stripped = line.strip()
        if stripped and not stripped.startswith(skip_prefixes):
            return stripped[:120]
    return ""


def main() -> int:
    parser = argparse.ArgumentParser(description="Generate OKF index.md files.")
    parser.add_argument("bundle", help="Path to the OKF bundle directory.")
    parser.add_argument("--write", action="store_true", help="Write files (default: dry run).")
    parser.add_argument("--okf-version", default="0.1", help="okf_version for the root index.")
    args = parser.parse_args()

    if not os.path.isdir(args.bundle):
        print(f"Bundle directory not found: {args.bundle}", file=sys.stderr)
        return 1

    changed = 0
    for dirpath, dirnames, _filenames in os.walk(args.bundle):
        dirnames[:] = [d for d in dirnames if not d.startswith(".") and d != "__pycache__"]
        new_content = build_index_for_dir(dirpath, args.bundle, args.okf_version)
        index_path = os.path.join(dirpath, "index.md")
        old_content = ""
        if os.path.isfile(index_path):
            with open(index_path, "r", encoding="utf-8") as fh:
                old_content = fh.read()
        rel = os.path.relpath(index_path, args.bundle).replace(os.sep, "/")
        if new_content != old_content:
            changed += 1
            if args.write:
                with open(index_path, "w", encoding="utf-8") as fh:
                    fh.write(new_content)
                print(f"  wrote {rel}")
            else:
                print(f"  stale  {rel}")
        else:
            print(f"  ok     {rel}")

    if args.write:
        print(f"\nUpdated {changed} index file(s).")
        return 0
    if changed:
        print(f"\n{changed} index file(s) missing or stale. Re-run with --write.")
        return 1
    print("\nAll index files current.")
    return 0


if __name__ == "__main__":
    sys.exit(main())
