#!/usr/bin/env python3
"""Generate ``docs/command-flows.md`` — the Flows primary view (road-to-6.1.0 Step 9).

Turns the flat command list into a FLOW-organized view: commands are flow
members, not an undifferentiated list. Renders the layered model
``Profile → Pack → Flow → Command → Skill → Rule`` from two validated sources:

- ``src/flows/surface-map.yaml`` — the complete command→flow classification
  (validated by ``scripts/lint_command_flow_coverage.py``).
- ``src/flows/<flow>.yaml`` — the rich user-work flow definitions
  (summary / entry_points / default_path / skills; validated by
  ``scripts/lint_flows.py``).

Command titles + descriptions come from each command's
``src/domains/<pack>/**/command.md`` frontmatter.

Idempotent. Safe to re-run.

Usage:
    python3 scripts/generate_command_flows.py
    python3 scripts/generate_command_flows.py --check   # fail if out of date
"""
from __future__ import annotations

import argparse
import re
import sys
from pathlib import Path

import yaml  # type: ignore

sys.path.insert(0, str(Path(__file__).resolve().parent))
from _lib.agent_src import _iter_domains_commands  # noqa: E402

ROOT = Path(__file__).resolve().parents[2]
SURFACE_MAP = ROOT / "src" / "flows" / "surface-map.yaml"
FLOWS_DIR = ROOT / "src" / "flows"
OUT = ROOT / "docs" / "command-flows.md"

FRONTMATTER_RE = re.compile(r"^---\s*\n(.*?)\n---", re.DOTALL)


def _frontmatter(path: Path) -> dict:
    m = FRONTMATTER_RE.match(path.read_text(encoding="utf-8"))
    if not m:
        return {}
    try:
        return yaml.safe_load(m.group(1)) or {}
    except yaml.YAMLError:
        return {}


def _command_index() -> dict[str, Path]:
    return {
        logical[len("commands/") : -len(".md")]: path
        for path, logical in _iter_domains_commands()
    }


# Human-facing section order + headings. User-work flows first (the developer
# story), then product features, then the platform + domain surfaces.
USER_WORK = ["discovery", "implementation", "review", "delivery"]
SURFACE_HEADINGS = {
    "product": ("Product-surface features", "Deliberately-started features — not daily flow members, never buried as skills."),
    "agent-admin": ("Platform surface — `agent-admin`", "The system/admin surface (NOT a flow): memory · analytics · governance · config · introspection, reached on demand."),
    "media": ("Domain flow — media", "AI image + video pipeline."),
    "marketing": ("Domain flow — marketing", "Ghostwriter + post-as voice pipeline."),
    "fun": ("Domain flow — fun", "Recreational packs."),
    "session": ("Session surface", "Handoff / status / mode — conversation lifecycle."),
}


def _cmd_line(ref: str, idx: dict[str, Path]) -> str:
    path = idx.get(ref)
    fm = _frontmatter(path) if path else {}
    name = fm.get("name") or ref
    desc = (fm.get("description") or "").strip()
    desc = re.sub(r"\s+", " ", desc)
    if len(desc) > 110:
        desc = desc[:107].rstrip() + "…"
    return f"- `{name}` — {desc}" if desc else f"- `{name}`"


def _load_flow_yaml(flow: str) -> dict:
    f = FLOWS_DIR / f"{flow}.yaml"
    if not f.is_file():
        return {}
    return yaml.safe_load(f.read_text(encoding="utf-8")) or {}


def render() -> str:
    data = yaml.safe_load(SURFACE_MAP.read_text(encoding="utf-8")) or {}
    buckets = data.get("commands") or {}
    idx = _command_index()

    out: list[str] = []
    out.append("<!-- GENERATED by scripts/generate_command_flows.py — do not edit by hand. -->")
    out.append("<!-- Source: src/flows/surface-map.yaml + src/flows/<flow>.yaml. Re-run: task generate-command-flows. -->")
    out.append("")
    out.append("# Command flows — the primary view")
    out.append("")
    out.append(
        "> road-to-6.1.0 Step 9. Commands are **flow members**, not a flat list. "
        "The layered model is `Profile → Pack → Flow → Command → Skill → Rule`; "
        "this view answers *\"what am I trying to do?\"* before *\"which command do I type?\"*."
    )
    out.append("")
    out.append(
        "> Every command is classified in `src/flows/surface-map.yaml` "
        "(coverage enforced by `scripts/lint_command_flow_coverage.py`). The four "
        "user-work flows carry rich definitions in `src/flows/<flow>.yaml`."
    )
    out.append("")

    total = sum(len(v or []) for v in buckets.values())
    out.append(f"**{total} commands** across **{len(buckets)} surfaces** "
               f"({len(USER_WORK)} user-work flows + {len(buckets) - len(USER_WORK)} platform/domain surfaces).")
    out.append("")

    # --- User-work flows -----------------------------------------------------
    out.append("## User-work flows")
    out.append("")
    out.append("The developer story: **discovery → implementation → review → delivery**.")
    out.append("")
    for flow in USER_WORK:
        fy = _load_flow_yaml(flow)
        title = fy.get("title", flow.title())
        summary = re.sub(r"\s+", " ", (fy.get("summary") or "").strip())
        out.append(f"### {title}")
        out.append("")
        if summary:
            out.append(f"_{summary}_")
            out.append("")
        eps = fy.get("entry_points") or []
        dp = fy.get("default_path") or []
        if eps:
            out.append(f"**Entry points:** {' · '.join(f'`{e}`' for e in eps)}")
            out.append("")
        if dp:
            out.append(f"**Default path:** {' → '.join(f'`{e}`' for e in dp)}")
            out.append("")
        for ref in sorted(buckets.get(flow, []) or []):
            out.append(_cmd_line(ref, idx))
        out.append("")

    # --- Platform + domain surfaces -----------------------------------------
    out.append("## Platform & domain surfaces")
    out.append("")
    for surface in ["product", "agent-admin", "media", "marketing", "fun", "session"]:
        refs = buckets.get(surface)
        if not refs:
            continue
        heading, blurb = SURFACE_HEADINGS.get(surface, (surface.title(), ""))
        out.append(f"### {heading}")
        out.append("")
        if blurb:
            out.append(f"_{blurb}_")
            out.append("")
        for ref in sorted(refs):
            out.append(_cmd_line(ref, idx))
        out.append("")

    return "\n".join(out).rstrip() + "\n"


def main() -> int:
    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--check", action="store_true", help="fail if the file is out of date")
    args = ap.parse_args()

    content = render()
    if args.check:
        current = OUT.read_text(encoding="utf-8") if OUT.is_file() else ""
        if current != content:
            print(
                "generate_command_flows: docs/command-flows.md is stale — "
                "run `python3 scripts/generate_command_flows.py`",
                file=sys.stderr,
            )
            return 1
        print("generate_command_flows: OK — docs/command-flows.md up to date")
        return 0

    OUT.parent.mkdir(parents=True, exist_ok=True)
    OUT.write_text(content, encoding="utf-8")
    print(f"generate_command_flows: wrote {OUT.relative_to(ROOT)}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
