#!/usr/bin/env bash
TEST_CATEGORY=core

level=$1
if [ -z "$level" ]; then
  level=1
fi
echo "starting: ${BASH_SOURCE[@]##*/} <LOG_LEVEL=$level>"

source this
source test.suite
source $OOSH_DIR/ng/c2

log.level $level

# ============================================================================
# Universal Completion Audit
# ============================================================================
# Iterates every user-facing OOSH script, extracts public methods with
# parameters, and verifies a completion function exists for each non-exempt
# parameter.  A completion function is either:
#   scriptName.method.completion()               (method-level catch-all)
#   scriptName.method.completion.paramName()      (method+param specific)
#   scriptName.parameter.completion.paramName()   (shared across methods)

# Parameter names exempt from completion (free-text / numeric / not completable)
# IMPORTANT: must be a single line (no newlines) for space-delimited matching
EXEMPT_PARAMS=" prompt message description text command keys template query args content subject body comment reason note prefix lines count width height percentage size subcommand options mode value new newName newClassName newScript ENV_PREFIX interval seconds timeout threshold amount errorCount number condition format data title hook channel pattern portMap sshPort port portOrOffset url githubUrl uid gid log load cmd1 cmd2 key key1 key2 filename filepath fromPath toPath from alwaysReturnId TestName turns tools reset task name newName newName roles featureBranchPrefix startCommit worktree_base sourceSshDir idName "

# In-scope user-facing scripts
IN_SCOPE_SCRIPTS="otmux claudeCode claudeFlow hiveMind ossh odocker state
  config backup context scrumMaster user log agentRoom disk promote oo path
  certificates check index osx mycmd share snet myId otest test.suite c2"

# Counters
AUDIT_TOTAL=0
AUDIT_COVERED=0
AUDIT_EXEMPT=0
AUDIT_MISSING=0

# Helper: check if a word is in an exempt list string
private.audit.isExempt() {
  [[ "$EXEMPT_PARAMS" == *" $1 "* ]]
}

# Cache for method-level completions (avoid repeated type -t calls)
declare -A METHOD_COMPLETION_CACHE

# Helper: check if method-level completion exists (catches all params)
private.audit.hasMethodCompletion() {
  local key="$1.$2.completion"
  if [ -z "${METHOD_COMPLETION_CACHE[$key]+x}" ]; then
    if [ "$(type -t "$key" 2>/dev/null)" = "function" ]; then
      METHOD_COMPLETION_CACHE[$key]=1
    else
      METHOD_COMPLETION_CACHE[$key]=0
    fi
  fi
  [ "${METHOD_COMPLETION_CACHE[$key]}" -eq 1 ]
}

# ============================================================================
# Main audit loop
# ============================================================================

for scriptName in $IN_SCOPE_SCRIPTS; do

  # Resolve script path
  scriptPath="$OOSH_DIR/$scriptName"
  if [ ! -f "$scriptPath" ]; then
    scriptPath="$OOSH_DIR/ng/$scriptName"
  fi
  if [ ! -f "$scriptPath" ]; then
    continue
  fi

  # Source script to load its completion functions (suppress all output)
  source "$scriptPath" completion.discover 2>/dev/null

  # Grep function signatures directly from the file
  while IFS= read -r sigLine; do
    [ -z "$sigLine" ] && continue

    # Extract full method name (everything before the parentheses)
    methodFull="${sigLine%%\(\)*}"
    methodFull="${methodFull## }"
    method="${methodFull#${scriptName}.}"

    # Skip private, completion, start, usage methods
    case "$method" in
      private*|*completion*|start|usage) continue ;;
    esac

    # Extract parameter names using bash regex
    paramSection="${sigLine#*\# }"
    params=""
    while [[ "$paramSection" =~ \<\??([a-zA-Z][a-zA-Z0-9_]*) ]]; do
      params="$params ${BASH_REMATCH[1]}"
      paramSection="${paramSection#*${BASH_REMATCH[0]}}"
    done

    for param in $params; do
      AUDIT_TOTAL=$((AUDIT_TOTAL + 1))

      # Skip exempt (free-text) params
      if private.audit.isExempt "$param"; then
        AUDIT_EXEMPT=$((AUDIT_EXEMPT + 1))
        continue
      fi

      # Check method-level catch-all: scriptName.method.completion()
      if private.audit.hasMethodCompletion "$scriptName" "$method"; then
        AUDIT_COVERED=$((AUDIT_COVERED + 1))
        continue
      fi

      # Check method+param specific: scriptName.method.completion.param()
      if [ "$(type -t "${scriptName}.${method}.completion.${param}" 2>/dev/null)" = "function" ]; then
        AUDIT_COVERED=$((AUDIT_COVERED + 1))
        continue
      fi

      # Check shared parameter: scriptName.parameter.completion.param()
      if [ "$(type -t "${scriptName}.parameter.completion.${param}" 2>/dev/null)" = "function" ]; then
        AUDIT_COVERED=$((AUDIT_COVERED + 1))
        continue
      fi

      # Missing completion
      AUDIT_MISSING=$((AUDIT_MISSING + 1))
      test.case $level "${scriptName}.${method}: missing completion for <${param}>" \
        echo "missing"
      expect.fail "${scriptName} needs completion for '${param}' (method: ${method})"
    done
  done < <(grep -E "^${scriptName}\.[a-zA-Z].*\(\) +#.+<" "$scriptPath" 2>/dev/null \
           | grep -v '\.completion\.' | grep -v '^private\.')
done

# ============================================================================
# Summary
# ============================================================================

test.case $level "Completion audit: ${AUDIT_TOTAL} params, ${AUDIT_COVERED} covered, ${AUDIT_EXEMPT} exempt, ${AUDIT_MISSING} missing" \
  echo "audit complete"

if [ "$AUDIT_MISSING" -eq 0 ]; then
  expect.pass "All completable parameters have completion functions"
else
  expect.fail "${AUDIT_MISSING} of ${AUDIT_TOTAL} parameters need completion functions"
fi

test.suite.save.results
