#!/usr/bin/env bash
# ayf -- AY Framework CLI
#
# Thin wrapper around bin/install.js (the canonical, cross-platform installer).
#   init / install / upgrade  -> delegate to node bin/install.js
#   status / doctor / add / scan -> native bash (no install.js equivalent)
#
# This kills the drift surface that broke the old bash CLI: install logic lives
# in exactly one place (install.js). status/doctor/add only READ the installed
# layout, so they stay here and were fixed to match the current template names
# (_template.md, skills/_template/, tracking in templates/tracking/).

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
FRAMEWORK_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
INSTALL_JS="$SCRIPT_DIR/install.js"
AYF_VERSION="$(node -p "require('$FRAMEWORK_DIR/package.json').version" 2>/dev/null || cat "$FRAMEWORK_DIR/VERSION" 2>/dev/null || echo "unknown")"
TEMPLATES_DIR="$FRAMEWORK_DIR/templates"
PROJECT_DIR="$(pwd)"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'

print_header() {
  echo ""
  echo -e "${BOLD}${CYAN}AY Framework${NC} v${AYF_VERSION}"
  echo -e "${CYAN}gstack agent swarm for Claude Code${NC}"
  echo ""
}

print_success() { echo -e "  ${GREEN}+${NC} $1"; }
print_info() { echo -e "  ${BLUE}>${NC} $1"; }
print_warn() { echo -e "  ${YELLOW}!${NC} $1"; }
print_error() { echo -e "  ${RED}x${NC} $1"; }

require_install_js() {
  if [[ ! -f "$INSTALL_JS" ]]; then
    print_error "Installer not found: $INSTALL_JS"
    exit 1
  fi
  if ! command -v node >/dev/null 2>&1; then
    print_error "node is required to run the installer. Install Node.js and retry."
    exit 1
  fi
}

# ─── INIT / INSTALL (delegates to install.js) ────────────────────────────────

cmd_init() {
  require_install_js
  # Pass through any flags (--local, --global, --yes, --runtime=...).
  # With no flags, install.js runs its interactive runtime/location picker.
  # Note: a positional project name is no longer used -- install.js injects an
  # "## AY Framework" section into the existing CLAUDE.md instead of templating one.
  exec node "$INSTALL_JS" "$@"
}

# ─── UPGRADE (delegates to install.js) ───────────────────────────────────────

cmd_upgrade() {
  require_install_js
  print_header
  print_info "Refreshing framework files via the installer..."
  print_info "Modes + commands are overwritten; tracking, agents, skills, and tasks are preserved."
  echo ""
  # Re-running the installer overwrites mode/command files, but skips existing
  # tracking files and only re-injects CLAUDE.md if the marker is missing --
  # exactly the "upgrade" contract. --local targets the current project.
  exec node "$INSTALL_JS" --local --yes
}

# ─── ADD AGENT ──────────────────────────────────────────────────────────────

cmd_add_agent() {
  local name="${1:-}"
  local scope="${2:-}"

  if [[ -z "$name" ]]; then
    print_error "Usage: ayf add agent <name> <scope-directory>"
    echo "  Example: ayf add agent frontend-builder apps/web/"
    exit 1
  fi

  if [[ -z "$scope" ]]; then
    print_error "Scope directory required."
    echo "  Example: ayf add agent frontend-builder apps/web/"
    exit 1
  fi

  local tpl="$TEMPLATES_DIR/agents/_template.md"
  if [[ ! -f "$tpl" ]]; then
    print_error "Agent template missing: $tpl"
    exit 1
  fi

  local agent_file="$PROJECT_DIR/.claude/agents/${name}.md"
  mkdir -p "$PROJECT_DIR/.claude/agents"

  if [[ -f "$agent_file" ]]; then
    print_warn "Agent '$name' already exists. Overwrite? [y/N]"
    read -p "  " confirm
    if [[ ! "${confirm:-N}" =~ ^[Yy] ]]; then
      echo "  Aborted."
      exit 0
    fi
  fi

  # Substitute {{AGENT_NAME}}, then swap the two example owned-dirs for the
  # provided scope. If the template layout changes, the sed no-ops gracefully.
  sed -e "s/{{AGENT_NAME}}/$name/g" "$tpl" \
    | sed -e "s|^- \`src/{{module}}/\`|- \`${scope%/}/\`|" \
          -e "\|^- \`tests/{{module}}/\`$|d" \
    > "$agent_file"

  print_success "Agent created: .claude/agents/${name}.md"
  print_info "Edit the file to add skills and refine the scope."
}

# ─── ADD SKILL ──────────────────────────────────────────────────────────────

cmd_add_skill() {
  local name="${1:-}"

  if [[ -z "$name" ]]; then
    print_error "Usage: ayf add skill <name>"
    echo "  Example: ayf add skill supabase-expert"
    exit 1
  fi

  local tpl="$TEMPLATES_DIR/skills/_template/SKILL.md"
  if [[ ! -f "$tpl" ]]; then
    print_error "Skill template missing: $tpl"
    exit 1
  fi

  local skill_dir="$PROJECT_DIR/.claude/skills/${name}"
  if [[ -d "$skill_dir" ]]; then
    print_warn "Skill '$name' already exists."
    exit 1
  fi

  mkdir -p "$skill_dir"
  sed "s/{{SKILL_NAME}}/$name/g" "$tpl" > "$skill_dir/SKILL.md"

  print_success "Skill created: .claude/skills/${name}/SKILL.md"
  print_info "Add domain knowledge, API references, and patterns to the SKILL.md file."
}

# ─── ADD TASK (interactive wizard) ───────────────────────────────────────────
# Prompts for title, agent, blocked-by IDs, and scope dirs; scaffolds a task
# file from templates/tasks/_template.md with the number auto-incremented from
# the existing .ay/tasks/NN-*.md files; and appends a BOARD.md row to the last
# pipeline table (READY when nothing blocks it, BACKLOG when blocked-by is set).
# An optional title argument pre-fills the first prompt. Answers are read from
# stdin, so the wizard can be driven interactively or by piped input.

# Insert a table row after the last row of the last markdown pipeline table
# (a table whose header row contains the Title and Status columns). Falls back
# to appending at EOF when no such table is found.
board_append_row() {
  local board="$1" row="$2"
  local tmp="${board}.ayf.$$"
  awk -v row="$row" '
    { lines[NR]=$0 }
    END {
      last_end=0; i=1
      while (i<=NR) {
        if (lines[i] ~ /^\|/ && lines[i] ~ /Title/ && lines[i] ~ /Status/) {
          j=i+1
          if (j<=NR && lines[j] ~ /^\|/) {          # separator row present
            end=j; k=j+1
            while (k<=NR && lines[k] ~ /^\|/) { end=k; k++ }
            last_end=end; i=k; continue
          }
        }
        i++
      }
      for (n=1; n<=NR; n++) {
        print lines[n]
        if (n==last_end) print row
      }
      if (last_end==0) print row
    }
  ' "$board" > "$tmp" && mv "$tmp" "$board"
}

cmd_add_task() {
  local tpl="$TEMPLATES_DIR/tasks/_template.md"
  if [[ ! -f "$tpl" ]]; then
    print_error "Task template missing: $tpl"
    exit 1
  fi

  local tasks_dir="$PROJECT_DIR/.ay/tasks"
  local board="$PROJECT_DIR/.ay/tracking/BOARD.md"
  mkdir -p "$tasks_dir"

  print_header
  echo -e "  ${BOLD}New task wizard${NC}"
  echo "  ────────────────────────────"

  # Title (an argument pre-fills it and skips the prompt).
  local title="${1:-}"
  while [[ -z "${title// }" ]]; do
    read -r -p "  Task title: " title || { print_error "No title provided."; exit 1; }
  done

  local agent blocked scope
  read -r -p "  Agent [-]: " agent || true
  [[ -z "${agent// }" ]] && agent="-"
  read -r -p "  Blocked by (task IDs, space/comma separated) [none]: " blocked || true
  read -r -p "  Scope directories (space/comma separated) [none]: " scope || true

  # Auto-increment the task number from existing NN-*.md files.
  local max=0 f base n
  for f in "$tasks_dir"/*.md; do
    [[ -e "$f" ]] || continue
    base="$(basename "$f")"
    [[ "$base" == "_template.md" ]] && continue
    n="${base%%-*}"
    if [[ "$n" =~ ^[0-9]+$ ]]; then
      n=$((10#$n))
      (( n > max )) && max=$n
    fi
  done
  local padded; padded="$(printf '%02d' "$((max + 1))")"

  # Slug from title: lowercase, non-alphanumerics -> single dash, trimmed.
  local slug
  slug="$(printf '%s' "$title" | tr '[:upper:]' '[:lower:]' | tr -cs 'a-z0-9' '-')"
  slug="${slug#-}"; slug="${slug%-}"
  [[ -z "$slug" ]] && slug="task"

  local task_file="$tasks_dir/${padded}-${slug}.md"
  [[ -e "$task_file" ]] && task_file="$tasks_dir/${padded}-${slug}-${padded}.md"

  # Normalise list inputs (commas -> spaces).
  local blocked_ids scope_dirs
  blocked_ids="$(printf '%s' "$blocked" | tr ',' ' ')"
  scope_dirs="$(printf '%s' "$scope" | tr ',' ' ')"

  # Build the Dependencies + Files-to-Create blocks and derive the board status.
  local deps_block files_block status blocked_col id d
  if [[ -n "${blocked_ids// }" ]]; then
    deps_block=""
    for id in $blocked_ids; do deps_block+="- [ ] \`tasks/${id}.md\`"$'\n'; done
    deps_block="${deps_block%$'\n'}"
    status="BACKLOG"
    blocked_col="$(printf '%s' "$blocked_ids" | xargs | tr ' ' ',')"
  else
    deps_block="- none"
    status="READY"
    blocked_col="-"
  fi

  if [[ -n "${scope_dirs// }" ]]; then
    files_block=""
    for d in $scope_dirs; do files_block+="- \`${d}\`"$'\n'; done
    files_block="${files_block%$'\n'}"
  else
    files_block="- (define the files this task will create)"
  fi

  # Fill the template: substitute the title, swap the Dependencies and
  # Files-to-Create bullet blocks, and keep every section heading intact.
  # Done in pure bash (not awk) so multi-line blocks are portable -- BWK awk on
  # macOS rejects newlines passed through -v.
  local dep_done=0 file_done=0 line
  {
    while IFS= read -r line || [[ -n "$line" ]]; do
      line="${line//\{\{TASK_TITLE\}\}/$title}"
      if [[ "$line" == *'{{dependency-'* ]]; then
        (( dep_done )) || { printf '%s\n' "$deps_block"; dep_done=1; }
        continue
      fi
      if [[ "$line" == '- `'* && "$line" == *'{{module}}'* ]]; then
        (( file_done )) || { printf '%s\n' "$files_block"; file_done=1; }
        continue
      fi
      printf '%s\n' "$line"
    done < "$tpl"
  } > "$task_file"

  # Append the row to BOARD.md.
  if [[ -f "$board" ]]; then
    board_append_row "$board" "| $padded | $title | $agent | $status | $blocked_col |"
    print_success "BOARD.md updated (status $status)"
  else
    print_warn "No BOARD.md found -- created the task file only."
  fi

  print_success "Task created: .ay/tasks/$(basename "$task_file")"
  print_info "Number $padded auto-assigned; agent '$agent'; status $status."
}

# ─── STATUS ─────────────────────────────────────────────────────────────────

cmd_status() {
  print_header

  if [[ ! -f "$PROJECT_DIR/CLAUDE.md" ]]; then
    print_error "No CLAUDE.md found. Run 'ayf init' first."
    exit 1
  fi

  # Count components (exclude templates: _template.md / _template/)
  local agents=0 skills=0 tasks=0 commands=0 modes=0 locked=0

  [[ -d "$PROJECT_DIR/.claude/agents" ]] && agents=$(find "$PROJECT_DIR/.claude/agents" -name "*.md" ! -name "_template.md" 2>/dev/null | wc -l | tr -d ' ')
  [[ -d "$PROJECT_DIR/.claude/skills" ]] && skills=$(find "$PROJECT_DIR/.claude/skills" -mindepth 1 -maxdepth 1 -type d ! -name "_template" 2>/dev/null | wc -l | tr -d ' ')
  [[ -d "$PROJECT_DIR/.ay/tasks" ]] && tasks=$(find "$PROJECT_DIR/.ay/tasks" -name "*.md" ! -name "_template.md" 2>/dev/null | wc -l | tr -d ' ')
  [[ -d "$PROJECT_DIR/.claude/commands" ]] && commands=$(find "$PROJECT_DIR/.claude/commands" -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
  [[ -d "$PROJECT_DIR/.claude" ]] && modes=$(find "$PROJECT_DIR/.claude" -name "CLAUDE-*.md" 2>/dev/null | wc -l | tr -d ' ')
  [[ -d "$PROJECT_DIR/.ay/tracking/locks" ]] && locked=$(find "$PROJECT_DIR/.ay/tracking/locks" -name "*.lock" 2>/dev/null | wc -l | tr -d ' ')

  echo -e "  ${BOLD}Components${NC}"
  echo "  ────────────────────────────"
  echo -e "  Modes:    ${BOLD}$modes${NC} / 6"
  echo -e "  Agents:   ${BOLD}$agents${NC}"
  echo -e "  Skills:   ${BOLD}$skills${NC}"
  echo -e "  Tasks:    ${BOLD}$tasks${NC}"
  echo -e "  Commands: ${BOLD}$commands${NC}"
  echo -e "  Locked:   ${BOLD}$locked${NC}"
  echo ""

  # Show BOARD summary if exists
  local board="$PROJECT_DIR/.ay/tracking/BOARD.md"
  if [[ -f "$board" ]]; then
    local backlog ready in_progress done
    backlog=$(grep -c "BACKLOG" "$board" 2>/dev/null || echo 0)
    ready=$(grep -c "READY" "$board" 2>/dev/null || echo 0)
    in_progress=$(grep -c "IN PROGRESS" "$board" 2>/dev/null || echo 0)
    done=$(grep -c "| DONE" "$board" 2>/dev/null || echo 0)

    echo -e "  ${BOLD}Pipeline${NC}"
    echo "  ────────────────────────────"
    echo -e "  BACKLOG:     $backlog"
    echo -e "  READY:       $ready"
    echo -e "  IN PROGRESS: $in_progress"
    echo -e "  DONE:        $done"
    echo ""
  fi

  # Show active locks
  if [[ "$locked" -gt 0 ]]; then
    echo -e "  ${BOLD}Active Locks${NC}"
    echo "  ────────────────────────────"
    for lock in "$PROJECT_DIR/.ay/tracking/locks/"*.lock; do
      [[ -f "$lock" ]] || continue
      local task_id
      task_id=$(basename "$lock" .lock)
      echo -e "  ${YELLOW}LOCKED${NC} $task_id"
    done
    echo ""
  fi

  # Show agents
  if [[ "$agents" -gt 0 ]]; then
    echo -e "  ${BOLD}Agents${NC}"
    echo "  ────────────────────────────"
    for agent in "$PROJECT_DIR/.claude/agents/"*.md; do
      [[ -f "$agent" ]] || continue
      [[ "$(basename "$agent")" == "_template.md" ]] && continue
      local aname
      aname=$(basename "$agent" .md)
      echo -e "  ${GREEN}*${NC} $aname"
    done
    echo ""
  fi
}

# ─── DOCTOR ─────────────────────────────────────────────────────────────────

cmd_doctor() {
  print_header
  echo -e "  ${BOLD}Health Check${NC}"
  echo "  ────────────────────────────"

  local issues=0

  # Check CLAUDE.md
  if [[ -f "$PROJECT_DIR/CLAUDE.md" ]]; then
    print_success "CLAUDE.md exists"
  else
    print_error "CLAUDE.md missing -- run 'ayf init'"
    issues=$((issues + 1))
  fi

  # Check modes
  for mode in PLAN BUILD REVIEW QA SHIP RETRO; do
    if [[ -f "$PROJECT_DIR/.claude/CLAUDE-${mode}.md" ]]; then
      print_success "Mode: $mode"
    else
      print_error "Mode missing: CLAUDE-${mode}.md"
      issues=$((issues + 1))
    fi
  done

  # Check tracking
  for track in BOARD.md HANDOFFS.md AGENTS-LOG.md; do
    if [[ -f "$PROJECT_DIR/.ay/tracking/$track" ]]; then
      print_success "Tracking: $track"
    else
      print_error "Tracking missing: $track"
      issues=$((issues + 1))
    fi
  done

  # Check locks dir
  if [[ -d "$PROJECT_DIR/.ay/tracking/locks" ]]; then
    print_success "Locks directory"
  else
    print_error "Locks directory missing"
    issues=$((issues + 1))
  fi

  # Check for stale locks (>24h)
  if [[ -d "$PROJECT_DIR/.ay/tracking/locks" ]]; then
    local stale
    stale=$(find "$PROJECT_DIR/.ay/tracking/locks" -name "*.lock" -mmin +1440 2>/dev/null | wc -l | tr -d ' ')
    if [[ "$stale" -gt 0 ]]; then
      print_warn "$stale stale lock(s) older than 24h"
    fi
  fi

  # Check agents
  local agent_count
  agent_count=$(find "$PROJECT_DIR/.claude/agents" -name "*.md" ! -name "_template.md" 2>/dev/null | wc -l | tr -d ' ')
  if [[ "$agent_count" -gt 0 ]]; then
    print_success "$agent_count agent(s) defined"
  else
    print_warn "No agents defined -- run 'ayf add agent <name> <scope>'"
  fi

  # Check tasks
  local task_count
  task_count=$(find "$PROJECT_DIR/.ay/tasks" -name "*.md" ! -name "_template.md" 2>/dev/null | wc -l | tr -d ' ')
  if [[ "$task_count" -gt 0 ]]; then
    print_success "$task_count task(s) defined"
  else
    print_warn "No tasks defined -- run 'ayf add task'"
  fi

  echo ""
  if [[ "$issues" -eq 0 ]]; then
    echo -e "  ${GREEN}${BOLD}All checks passed.${NC}"
  else
    echo -e "  ${RED}${BOLD}$issues issue(s) found.${NC}"
  fi
  echo ""
}

# ─── RELEASE ────────────────────────────────────────────────────────────────

cmd_release() {
  local bump="${1:-}"

  if [[ -z "$bump" ]]; then
    print_error "Usage: ayf release <patch|minor|major|x.y.z> [--dry-run]"
    echo "  Example: ayf release patch          # 1.5.0 -> 1.5.1"
    echo "           ayf release minor          # 1.5.0 -> 1.6.0"
    echo "           ayf release major          # 1.5.0 -> 2.0.0"
    echo "           ayf release patch --dry-run"
    exit 1
  fi

  case "$bump" in
    patch|minor|major) : ;;                       # semver bump keyword
    [0-9]*.[0-9]*.[0-9]*) : ;;                     # explicit x.y.z
    *) print_error "Invalid bump: '$bump' (use patch, minor, major, or x.y.z)"; exit 1 ;;
  esac

  local script="$SCRIPT_DIR/release.sh"
  [[ -f "$script" ]] || { print_error "release.sh not found at $script"; exit 1; }

  # Forward the bump keyword plus any flags (e.g. --dry-run) straight through.
  bash "$script" "$@"
}

# ─── SERVER ─────────────────────────────────────────────────────────────────

cmd_server() {
  local server="$SCRIPT_DIR/ayf-server.py"
  if [[ ! -f "$server" ]]; then
    print_error "ayf-server.py not found at $server"
    exit 1
  fi

  local py=""
  if command -v python3 >/dev/null 2>&1; then
    py="python3"
  elif command -v python >/dev/null 2>&1; then
    py="python"
  else
    print_error "python3 is required to run ayf-server. Install Python 3.7+ and retry."
    exit 1
  fi

  # Run from the project root so .ay/reviews and .ay/retros resolve correctly.
  # All args (e.g. --port, --reviews-dir) pass straight through.
  exec "$py" "$server" "$@"
}

# ─── HELP ───────────────────────────────────────────────────────────────────

cmd_help() {
  print_header
  echo "  Usage: ayf <command> [args]"
  echo ""
  echo "  Commands:"
  echo "    init [flags]             Install framework (delegates to install.js)"
  echo "                             flags: --local --global --yes --runtime=<rt>"
  echo "    scan                     Scan project: stack, tools, config, suggestions"
  echo "    add agent <name> <scope> Add a new agent definition"
  echo "    add skill <name>         Add a new skill pack"
  echo "    add task [title]         Interactive wizard: scaffold a task + BOARD row"
  echo "    status                   Show framework status and pipeline"
  echo "    doctor                   Health check -- find missing components"
  echo "    upgrade                  Refresh modes + commands (delegates to install.js)"
  echo "    server [--port N]        Start the HTTP feedback server (review packs <-> verdicts)"
  echo "    release <bump>           Cut a release: bump, changelog, smoke, tag, publish"
  echo "                             bump: patch | minor | major | x.y.z  [--dry-run]"
  echo "    version                  Show version"
  echo "    help                     Show this help"
  echo ""
  echo "  Examples:"
  echo "    cd my-project && ayf init --local --yes"
  echo "    ayf add agent api-builder packages/api/"
  echo "    ayf add skill nextjs-patterns"
  echo "    ayf add task 'Foundation and Setup'"
  echo "    ayf status"
  echo ""
}

# ─── MAIN ───────────────────────────────────────────────────────────────────

main() {
  local cmd="${1:-help}"
  shift || true

  case "$cmd" in
    init|install) cmd_init "$@" ;;
    add)
      local sub="${1:-}"
      shift || true
      case "$sub" in
        agent) cmd_add_agent "$@" ;;
        skill) cmd_add_skill "$@" ;;
        task)  cmd_add_task "$@" ;;
        *)     print_error "Unknown: ayf add $sub"; echo "  Use: agent, skill, or task"; exit 1 ;;
      esac
      ;;
    scan)     bash "$SCRIPT_DIR/ayf-scan" "$PROJECT_DIR" "${1:-human}" ;;
    status)   cmd_status ;;
    doctor)   cmd_doctor ;;
    upgrade)  cmd_upgrade ;;
    server)   cmd_server "$@" ;;
    release)  cmd_release "$@" ;;
    version)  echo "ayf $AYF_VERSION" ;;
    help|--help|-h) cmd_help ;;
    *)        print_error "Unknown command: $cmd"; cmd_help; exit 1 ;;
  esac
}

main "$@"
