#!/usr/bin/env bash
# frontier.sh — list the workable tickets in a file-mode PMOS kit (E9).
#
# "Work the frontier": a ticket is workable when it is NOT done and EVERY ticket it is blocked_by is
# done. This is the plain-file equivalent of a board's dependency structure — one file per ticket in
# state/tickets/NNN-slug.md, numbered in dependency order, blocking edges as text.
#
#   scripts/frontier.sh              # from the kit root; lists workable tickets
#   scripts/frontier.sh state/tickets   # explicit tickets dir
#
# Dependency-free and portable: POSIX-ish bash, no associative arrays (runs on bash 3.2, e.g. stock
# macOS). No network, no board. Reads only the YAML frontmatter fields id / status / blocked_by.
set -euo pipefail

DIR="${1:-state/tickets}"
[ -d "$DIR" ] || { echo "frontier: no tickets dir at '$DIR' (nothing to do)"; exit 0; }

# --- field readers (frontmatter only: stop at the closing --- fence) ----------------------------
fm() { awk 'NR>1 && /^---[[:space:]]*$/{exit} {print}' "$1"; }   # lines between the opening and closing ---
field() { printf '%s\n' "$1" | sed -n "s/^$2:[[:space:]]*//p" | head -1 | sed 's/^["'\'']//;s/["'\'']$//;s/[[:space:]]*$//'; }
# blocked_by is an inline list "[\"002\", \"005\"]"; extract the bare ids in order. Ids are NOT
# assumed numeric — idof below derives slug ids (an explicit `id:` field, or a digitless filename),
# and a digits-only extraction silently DROPPED every such blocker, listing a blocked ticket as
# workable (fail-open). Every non-empty token survives instead: a token that matches no done ticket
# keeps the ticket off the frontier, so a malformed or unknown blocker fails CLOSED.
blockers() { printf '%s\n' "$1" | sed -n 's/^blocked_by:[[:space:]]*//p' | head -1 | sed 's/[][]//g' | tr ',' '\n' | sed 's/["'\'']//g' | tr -d ' \t\r' | grep -v '^$' || true; }
# derive an id: explicit `id:` field, else the leading NNN of the filename.
idof() { local i; i="$(field "$1" id)"; [ -n "$i" ] && { printf '%s' "$i"; return; }; basename "$2" | grep -oE '^[0-9]+' || basename "$2" .md; }

shopt -s nullglob
files=("$DIR"/*.md)
[ ${#files[@]} -eq 0 ] && { echo "frontier: no tickets in '$DIR' (nothing to do)"; exit 0; }

# --- pass 1: collect the done ids (newline-delimited; no associative arrays for bash-3.2 portability)
DONE_IDS=""
OPEN=0
for f in "${files[@]}"; do
  meta="$(fm "$f")"; id="$(idof "$meta" "$f")"; st="$(field "$meta" status)"
  if [ "$st" = "done" ]; then DONE_IDS="$DONE_IDS$id
"; else OPEN=$((OPEN+1)); fi
done
is_done() { printf '%s' "$DONE_IDS" | grep -qx "$1"; }

# --- pass 2: a ticket is on the frontier when not done and every blocker is done -----------------
found=0
for f in "${files[@]}"; do
  meta="$(fm "$f")"; id="$(idof "$meta" "$f")"; st="$(field "$meta" status)"; st="${st:-todo}"
  [ "$st" = "done" ] && continue
  workable=1
  while IFS= read -r b; do
    [ -z "$b" ] && continue
    is_done "$b" || workable=0
  done < <(blockers "$meta")
  if [ "$workable" -eq 1 ]; then
    printf '  ▸ %-5s %-8s %s\n' "$id" "[$st]" "$(field "$meta" title)"
    found=$((found+1))
  fi
done

if [ "$found" -eq 0 ]; then
  # distinguish "all done" from "everything is blocked" — a stuck frontier is a real signal.
  if [ "$OPEN" -eq 0 ]; then echo "frontier: all tickets done ✓"
  else echo "frontier: $OPEN open ticket(s), NONE workable — the frontier is blocked (check for a cycle or an undone blocker)."; fi
fi
