#!/usr/bin/env bash
# Validate the skill catalog against the conventions in AGENTS.md + docs/invocation.md.
# The single source of truth for "is the catalog shippable" — called by release.yml
# (pre-publish), ci.yml (every push/PR), and runnable locally (e.g. from sip).
set -uo pipefail
cd "$(dirname "$0")/.."

fail=0
err() { echo "::error::$*"; fail=1; }   # GitHub annotation on CI; prints plainly off-CI

node_bin=${NODE:-}
if [ -n "$node_bin" ]; then
  case "$node_bin" in
    */*|*\\*) [ -x "$node_bin" ] || err "node runtime not found at NODE=$node_bin" ;;
    *) command -v "$node_bin" >/dev/null 2>&1 || err "node runtime not found at NODE=$node_bin" ;;
  esac
elif [ -z "$node_bin" ]; then
  if command -v node >/dev/null 2>&1; then
    node_bin=$(command -v node)
  elif command -v node.exe >/dev/null 2>&1; then
    node_bin=$(command -v node.exe)
  elif [ -x "/c/Program Files/nodejs/node.exe" ]; then
    node_bin="/c/Program Files/nodejs/node.exe"
  elif [ -x "/mnt/c/Program Files/nodejs/node.exe" ]; then
    node_bin="/mnt/c/Program Files/nodejs/node.exe"
  else
    err "node runtime not found — install Node.js or set NODE=/path/to/node"
  fi
fi

# plugin.json must be valid JSON
if [ -n "$node_bin" ] && [ "$fail" -eq 0 ]; then
  "$node_bin" -e "JSON.parse(require('fs').readFileSync('.claude-plugin/plugin.json','utf8'))" 2>/dev/null \
    || err "plugin.json is not valid JSON"
fi

# the brand one-liner is single-sourced: package.json and plugin.json must agree
if [ -n "$node_bin" ] && [ "$fail" -eq 0 ]; then
  pkg_desc=$("$node_bin" -p "require('./package.json').description" 2>/dev/null)
  plg_desc=$("$node_bin" -p "require('./.claude-plugin/plugin.json').description" 2>/dev/null)
  [ "$pkg_desc" = "$plg_desc" ] || err "package.json and plugin.json 'description' differ — keep the brand one-liner in sync"
fi

# structural section names required in every SKILL.md (empirically shared across the catalog)
required_sections=("Goal" "Workflow" "Rules" "Verification")
# soft cap on the frontmatter description; a longer one signals a paragraph, not a description
desc_max=800

while IFS= read -r f; do
  d=$(dirname "$f"); name=$(awk -F': *' '/^name:/{gsub(/\r$/, "", $2); print $2; exit}' "$f")
  grep -q '^description:' "$f"                       || err "$f: missing 'description'"
  [ -n "$name" ]                                     || err "$f: missing 'name'"
  [ "$name" = "$(basename "$d")" ]                   || err "$f: name '$name' != directory '$(basename "$d")'"
  grep -qF "\"./$d\"" .claude-plugin/plugin.json     || err "$d: not registered in plugin.json"
  grep -qF "$d/SKILL.md" README.md                   || err "$d: not listed in README.md"
  grep -q '\.\./' "$f"                               && err "$f: deep cross-file ref ('../') — compose by naming, not relative links"

  # frontmatter is parseable YAML (extract lines between the two --- delimiters)
  awk '/^---$/{c++; next} c==1' "$f" \
    | node -e "const y=require('fs').readFileSync(0,'utf8'); if(!/^[a-z_-]+:\s/mi.test(y)) process.exit(1)" 2>/dev/null \
    || err "$f: frontmatter block missing or malformed (expected 'key: value' lines between '---' delimiters)"

  # frontmatter description length cap — keeps it a description, not a paragraph
  desc_len=$(awk '/^description:/{sub(/^description: */,""); gsub(/^"|"$/,""); print length; exit}' "$f")
  [ -n "$desc_len" ] && [ "$desc_len" -le "$desc_max" ] \
    || err "$f: description length ${desc_len:-0} > $desc_max chars (tighten to a description, not a paragraph)"

  # required structural sections — verify every SKILL.md carries the shared skeleton
  for sec in "${required_sections[@]}"; do
    grep -qE "^## +${sec}\$" "$f" \
      || err "$f: missing required section '## ${sec}'"
  done
done < <(find skills -name SKILL.md)

# every plugin.json skill path resolves to a SKILL.md
while IFS= read -r p; do
  [ -f "$p/SKILL.md" ] || err "plugin.json: '$p' has no SKILL.md"
done < <(grep -oE '\./skills/[A-Za-z0-9/_-]+' .claude-plugin/plugin.json)

if [ "$fail" -eq 0 ]; then
  echo "✓ skill catalog valid ($(find skills -name SKILL.md | wc -l | tr -d ' ') skills)"
else
  echo "✗ catalog validation failed"; exit 1
fi
