#!/usr/bin/env bash
# Two catalog-vocabulary guards over the docs, in the spirit of the drift `ssotize`
# looks for — applied to cross-skill names rather than a full resolver:
#   1. Typo heuristic — a backticked, hyphenated, skill-shaped token sitting one
#      affix away from a shipped name (e.g. `re0-git-log` near `re0-git`) but not
#      resolving is flagged as a likely typo. Deliberately narrow: it ignores
#      non-hyphenated tokens and any token whose segments don't touch a real skill
#      name, so it does NOT catch every stale reference — that would mean flagging
#      the many backticked non-skill identifiers the docs legitimately carry.
#   2. Orphan guard — every shipped skill must be reachable from README.md, by a
#      backticked mention or a link to its SKILL.md, or it is orphaned on disk.
set -uo pipefail
cd "$(dirname "$0")/.."

fail=0
err() { echo "::error::$*"; fail=1; }

# collect shipped skill names (directory basenames under skills/*/*/SKILL.md)
mapfile -t shipped < <(find skills -name SKILL.md -printf '%h\n' 2>/dev/null | xargs -n1 basename | sort -u)
if [ "${#shipped[@]}" -eq 0 ]; then
  err "no shipped SKILL.md found under skills/ — nothing to check against"
  echo "✗ skill reference check failed"; exit 1
fi
declare -A known
for s in "${shipped[@]}"; do known[$s]=1; done

# README.md is the authoritative catalog; without it check-2 would flag every skill.
if [ ! -f README.md ]; then
  err "README.md missing at repo root — cannot verify catalog reachability"
  echo "✗ skill reference check failed"; exit 1
fi

# scope of files to scan: docs, top-level *.md, and every SKILL.md
mapfile -t files < <(
  { find skills -name SKILL.md
    find docs -name '*.md' 2>/dev/null
    ls *.md 2>/dev/null
  } | sort -u
)

# check 1 — a backticked skill-shaped token near a known skill name but not resolving
# is almost certainly a typo (e.g. `re0-git-log` when only `re0-git` is shipped).
# The while-read loop is fed via process substitution so it runs in the current
# shell and `fail=1` set by `err` propagates. A piped `while read` runs in a
# subshell and the failure would be silently swallowed (exit 0 even with hits).
for f in "${files[@]}"; do
  while read -r tok; do
    if [[ ${known[$tok]-} != 1 ]]; then
      if [[ $tok == *-* ]]; then
        for s in "${shipped[@]}"; do
          if [[ $tok == "$s"-?* || $tok == ?*-"$s" ]]; then
            err "$f: backticked '\`$tok\`' looks skill-shaped and near a shipped skill '$s' but does not resolve — rename or remove"
            break
          fi
        done
      fi
    fi
  done < <(grep -oE '`[a-z][a-z0-9-]{1,}`' "$f" 2>/dev/null | tr -d '`' | sort -u)
done

# check 2 — every shipped skill must be reachable from README.md, either by name
# (backticked) or as a link path. Path form is authoritative because the catalog
# lives in a table of paths; a skill on disk that README never links to is orphaned.
for s in "${shipped[@]}"; do
  # accept a backticked mention OR any link whose target contains the skill dir
  if grep -qF "\`$s\`" README.md; then continue; fi
  if grep -qE "\]\([^)]*/${s}/SKILL\.md\)" README.md; then continue; fi
  err "README.md: shipped skill '$s' is neither mentioned by name nor linked — orphan on disk"
done

if [ "$fail" -eq 0 ]; then
  echo "✓ skill references resolve (${#shipped[@]} shipped names, ${#files[@]} scanned files)"
else
  echo "✗ skill reference check failed"; exit 1
fi
