#!/usr/bin/env python3
"""session_scaffold.py -- scaffold 1 logic-session folder + _context skeleton.

SE PORT of AI_INFRA scripts/session_scaffold.py (adopted S148, 2026-07-24, owner
"doi ung dung chinh xac nhu hub"). Near-verbatim: the logic is repo-agnostic; only
the template CONTENT differs (SE STOCK-map rows). Kept stdlib-only + same CLI so a
future hub change can be re-pulled by diff instead of re-derived.

Tach LOGIC-session khoi VAT-LY window: tao `.claude/sessions/session-<N>/` +
`_context-s-<N>.md` tu template `.claude/templates/session-context-template.md`.

Design pins:
  - path HARD-PIN: `.claude/sessions/session-<N>/` (KHONG configurable -- uniform cross-repo).
  - ts-moc = `git log -1 --format=%cI` (committer ISO-8601 cua HEAD), KHONG datetime.now
    tuy-tien; nguon-ts ghi thang vao file (audit). Fallback datetime.now CHI khi git down,
    tag ro "FALLBACK".
  - Idempotent: folder ton tai -> bao + KHONG de `_context` (chi tao neu thieu).
  - Cap so <N> KHONG phai viec cua script nay -- caller truyen vao. Doc-quyen cap so =
    /session-start (session-start.md BUOC 0.8); /pause va /tiep chi tao folder cho <N> dang mo.

stdlib-only . Python 3.11+ . UTF-8 no-BOM . LF.
"""
from __future__ import annotations

import argparse
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path

SESSIONS_SUBPATH: tuple[str, ...] = (".claude", "sessions")
TEMPLATE_SUBPATH: tuple[str, ...] = (".claude", "templates", "session-context-template.md")


def repo_root() -> Path:
    """Repo root via `git rev-parse --show-toplevel`; fallback = parent-of-scripts/."""
    try:
        out = subprocess.run(
            ["git", "rev-parse", "--show-toplevel"],
            capture_output=True, text=True, check=True,
        )
        root = out.stdout.strip()
        if root:
            return Path(root)
    except (subprocess.CalledProcessError, FileNotFoundError, OSError):
        pass
    return Path(__file__).resolve().parent.parent


def head_ts() -> tuple[str, str]:
    """Return (ts, source). Prefer HEAD committer ISO-8601, tagged with short-sha for audit."""
    try:
        out = subprocess.run(
            ["git", "log", "-1", "--format=%cI%x09%h"],
            capture_output=True, text=True, check=True,
        ).stdout.strip()
        if out:
            ts, _, sha = out.partition("\t")
            ts, sha = ts.strip(), sha.strip()
            if ts:
                return ts, f"git log -1 --format=%cI @ HEAD {sha}"
    except (subprocess.CalledProcessError, FileNotFoundError, OSError):
        pass
    return (
        datetime.now(timezone.utc).isoformat(timespec="seconds"),
        "datetime.now(UTC) FALLBACK (git unavailable -- non-deterministic)",
    )


def render(template_text: str, n: int, ts: str, ts_source: str) -> str:
    """Fill placeholders with str.replace (NOT str.format) so literal `{`/`}` in the
    template's JSON schema example survive untouched."""
    return (
        template_text
        .replace("{{N}}", str(n))
        .replace("{{TS}}", ts)
        .replace("{{TS_SOURCE}}", ts_source)
    )


def _rel(p: Path, root: Path) -> str:
    try:
        return p.relative_to(root).as_posix()
    except ValueError:
        return str(p)


def main(argv: list[str] | None = None) -> int:
    ap = argparse.ArgumentParser(
        prog="session_scaffold.py",
        description="Scaffold 1 logic-session folder + _context skeleton "
                    "(.claude/sessions/session-<N>/). Idempotent; safe to re-run.",
    )
    ap.add_argument("--session", type=int, required=True, metavar="N",
                    help="logic-session ID N (folder = session-<N>, regex ^session-\\d+$)")
    ap.add_argument("--dry-run", action="store_true", help="print planned actions, write NOTHING")
    args = ap.parse_args(argv)

    n: int = args.session
    if n < 0:
        print(f"ERROR: --session must be a non-negative integer (got {n})", file=sys.stderr)
        return 2

    root = repo_root()
    session_dir = root.joinpath(*SESSIONS_SUBPATH) / f"session-{n}"
    context_path = session_dir / f"_context-s-{n}.md"
    template_path = root.joinpath(*TEMPLATE_SUBPATH)

    if not template_path.is_file():
        print(f"ERROR: template not found: {template_path}", file=sys.stderr)
        return 2

    ts, ts_source = head_ts()
    folder_exists = session_dir.exists()
    context_exists = context_path.exists()

    if args.dry_run:
        print(f"[dry-run] session N      = {n}")
        print(f"[dry-run] ts (moc)       = {ts}")
        print(f"[dry-run] ts-source      = {ts_source}")
        print(f"[dry-run] template       = {_rel(template_path, root)}")
        print(f"[dry-run] session folder = {_rel(session_dir, root)}  "
              f"({'EXISTS' if folder_exists else 'would mkdir'})")
        print(f"[dry-run] context file   = {_rel(context_path, root)}  "
              f"({'EXISTS -> would SKIP (idempotent)' if context_exists else 'would write'})")
        print("[dry-run] NOTHING written.")
        return 0

    created_folder = False
    if not folder_exists:
        session_dir.mkdir(parents=True, exist_ok=True)
        created_folder = True

    if context_exists:
        print(f"[idempotent] {_rel(context_path, root)} EXISTS -- NOT overwriting.")
        if created_folder:
            print(f"[note] folder {_rel(session_dir, root)} was missing -> created "
                  f"(unusual: context present without folder).")
        return 0

    template_text = template_path.read_text(encoding="utf-8")
    rendered = render(template_text, n, ts, ts_source)
    with open(context_path, "w", encoding="utf-8", newline="\n") as fh:
        fh.write(rendered)

    print(f"[created] folder  = {_rel(session_dir, root)}  ({'new' if created_folder else 'existed'})")
    print(f"[created] context = {_rel(context_path, root)}")
    print(f"[created] ts(moc) = {ts}  (source: {ts_source})")
    print("[note] NOT committed (commit theo nghi-thuc /pause | /snapshot | /session-end).")
    return 0


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