#!/usr/bin/env bash
# bauplan — AI project build-kit generator
# https://github.com/LIVELUCKY/bauplan
#
# Install:  curl -fsSL https://raw.githubusercontent.com/LIVELUCKY/bauplan/main/install.sh | bash
#       or: npm install -g @0-nazmi/bauplan
#
# Usage:
#   bauplan install          install globally (adds to ~/.bauplan, ~/.claude, ~/.opencode, etc.)
#   bauplan new [idea]       generate a complete kit for a new project
#   bauplan init             add bauplan files to the current project
#   bauplan review [path]    run the gap audit on an existing kit
#   bauplan status           show current build board
#   bauplan doctor           check toolchain and config
#   bauplan upgrade          upgrade to latest version
#   bauplan --version        print version
#   bauplan --help           print this help

set -uo pipefail

BAUPLAN_VERSION="1.0.0"
BAUPLAN_HOME="${BAUPLAN_HOME:-$HOME/.bauplan}"
BAUPLAN_REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." 2>/dev/null && pwd || echo "$BAUPLAN_HOME")"

# ── colours ──────────────────────────────────────────────────────────────────
if [ -t 1 ]; then
  RED='\033[0;31m'; GRN='\033[0;32m'; YEL='\033[0;33m'
  CYN='\033[0;36m'; BLD='\033[1m'; DIM='\033[2m'; OFF='\033[0m'
else
  RED=''; GRN=''; YEL=''; CYN=''; BLD=''; DIM=''; OFF=''
fi

err()  { printf "${RED}[bauplan] error:${OFF} %s\n" "$*" >&2; exit 1; }
warn() { printf "${YEL}[bauplan]   ⚠  ${OFF}%s\n" "$*"; }
ok()   { printf "${GRN}[bauplan]   ✓  ${OFF}%s\n" "$*"; }
info() { printf "${CYN}[bauplan]   →  ${OFF}%s\n" "$*"; }
h1()   { printf "\n${BLD}%s${OFF}\n" "$*"; }
hr()   { printf "${DIM}%s${OFF}\n" "──────────────────────────────────────────────"; }

# ── helpers ──────────────────────────────────────────────────────────────────
need_source() {
  [ -d "$BAUPLAN_REPO/templates" ] && return
  [ -d "$BAUPLAN_HOME/templates" ] && { BAUPLAN_REPO="$BAUPLAN_HOME"; return; }
  err "bauplan source not found. Run: bauplan install"
}

fmt_t() { printf '%dm%02ds' "$(($1/60))" "$(($1%60))"; }

slug_from() {
  printf '%s' "$1" | tr '[:upper:]' '[:lower:]' \
    | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//' | sed 's/-$//' | cut -c1-40
}

# ── install ───────────────────────────────────────────────────────────────────
cmd_install() {
  h1 "Installing Bauplan ${BAUPLAN_VERSION}"
  hr

  # 1. Copy source to ~/.bauplan
  info "installing core to $BAUPLAN_HOME"
  mkdir -p "$BAUPLAN_HOME"
  if [ -d "$BAUPLAN_REPO/templates" ] && [ "$BAUPLAN_REPO" != "$BAUPLAN_HOME" ]; then
    cp -r "$BAUPLAN_REPO/templates" "$BAUPLAN_HOME/"
    cp -r "$BAUPLAN_REPO/prompts"   "$BAUPLAN_HOME/"
    cp    "$BAUPLAN_REPO/METHODOLOGY.md" "$BAUPLAN_HOME/" 2>/dev/null || true
    cp    "$BAUPLAN_REPO/AGENTS.md"      "$BAUPLAN_HOME/" 2>/dev/null || true
    ok    "core files → $BAUPLAN_HOME"
  fi

  # 2. Global Claude Code config (~/.claude)
  # Agents go to ~/.claude/agents/ (available in EVERY project automatically)
  # Commands are namespaced bauplan-* to avoid conflicts with other tools
  if command -v claude >/dev/null 2>&1 || [ -d "$HOME/.claude" ]; then
    mkdir -p "$HOME/.claude/agents" "$HOME/.claude/commands"
    info "installing Claude Code global config"

    # Agents (available in every project — no per-project setup needed)
    for f in "$BAUPLAN_REPO/templates/claude-config/agents/"*.md; do
      basename=$(basename "$f")
      cp "$f" "$HOME/.claude/agents/bauplan-${basename}"
    done
    ok "agents → ~/.claude/agents/bauplan-*.md"

    # Commands namespaced bauplan-* (user types /bauplan-new-project etc.)
    for f in "$BAUPLAN_REPO/templates/claude-config/commands/"*.md; do
      basename=$(basename "$f")
      # also ship top-level /new-project and /review-kit
      case "$basename" in
        next-story.md) target="bauplan-next-story.md" ;;
        status.md)     target="bauplan-status.md" ;;
        barrier.md)    target="bauplan-barrier.md" ;;
        lane.md)       target="bauplan-lane.md" ;;
        *)             target="bauplan-$basename" ;;
      esac
      cp "$f" "$HOME/.claude/commands/$target"
    done
    # Also install the top-level /new-project and /review-kit commands
    for f in "$BAUPLAN_REPO/claude-config/commands/"*.md; do
      cp "$f" "$HOME/.claude/commands/$(basename "$f")"
    done
    ok "commands → ~/.claude/commands/"

    # Hooks — install only if no hooks exist yet (don't clobber user hooks)
    mkdir -p "$HOME/.claude/hooks"
    for f in "$BAUPLAN_REPO/templates/claude-config/hooks/"*.sh; do
      target="$HOME/.claude/hooks/bauplan-$(basename "$f")"
      cp "$f" "$target" && chmod +x "$target"
    done
    ok "hooks → ~/.claude/hooks/bauplan-*.sh"

    # settings.local.json — merge bauplan hook entries (non-destructive)
    _install_claude_settings
  else
    warn "claude CLI not found — skipping Claude Code global config"
    warn "install it: npm i -g @anthropic-ai/claude-code  then rerun: bauplan install"
  fi

  # 3. OpenCode global config (~/.opencode/)
  if command -v opencode >/dev/null 2>&1 || [ -d "$HOME/.opencode" ]; then
    mkdir -p "$HOME/.opencode"
    _install_opencode_global
    ok "OpenCode global config → ~/.opencode/"
  else
    info "opencode not detected — skipping (run: bauplan install after installing opencode)"
  fi

  # 4. GitHub Copilot user-level instructions (optional, non-destructive)
  COPILOT_USER_DIR="$HOME/.config/github-copilot"
  mkdir -p "$COPILOT_USER_DIR" 2>/dev/null || true
  if [ -d "$COPILOT_USER_DIR" ]; then
    _install_copilot_global
    ok "Copilot user instructions → $COPILOT_USER_DIR/instructions.md"
  fi

  # 5. Make bauplan globally available (if not installed via npm)
  SELF="${BASH_SOURCE[0]}"
  if [ -f "$SELF" ] && [ "$(which bauplan 2>/dev/null)" != "$SELF" ]; then
    TARGET="/usr/local/bin/bauplan"
    if cp "$SELF" "$TARGET" 2>/dev/null && chmod +x "$TARGET"; then
      ok "bauplan CLI → $TARGET"
    else
      # Try ~/bin (common on Linux without sudo)
      mkdir -p "$HOME/bin"
      cp "$SELF" "$HOME/bin/bauplan" && chmod +x "$HOME/bin/bauplan"
      ok "bauplan CLI → ~/bin/bauplan (add ~/bin to PATH if not already)"
    fi
  fi

  hr
  printf "\n${BLD}Bauplan ${BAUPLAN_VERSION} installed.${OFF}\n\n"
  printf "  ${CYN}bauplan new \"Your project idea\"${OFF}    generate a kit\n"
  printf "  ${CYN}bauplan doctor${OFF}                       verify toolchain\n"
  printf "  ${CYN}claude → /new-project \"idea\"${OFF}        same, via Claude Code\n\n"
}

_install_claude_settings() {
  local settings="$HOME/.claude/settings.local.json"
  # Only add hooks if they reference bauplan scripts
  local hook_cmd_brief="bash \"\$HOME/.claude/hooks/bauplan-session-brief.sh\""
  local hook_cmd_guard="bash \"\$HOME/.claude/hooks/bauplan-guard.sh\""

  if [ ! -f "$settings" ]; then
    cat > "$settings" <<EOF
{
  "hooks": {
    "SessionStart": [{"hooks": [{"type": "command", "command": "$hook_cmd_brief", "timeout": 10}]}],
    "PostCompact":  [{"hooks": [{"type": "command", "command": "$hook_cmd_brief", "timeout": 10}]}],
    "PreToolUse": [{
      "matcher": "Edit|Write|MultiEdit|Bash",
      "hooks": [{"type": "command", "command": "$hook_cmd_guard", "timeout": 10}]
    }]
  }
}
EOF
    ok "created ~/.claude/settings.local.json with bauplan hooks"
  else
    info "~/.claude/settings.local.json already exists — not modified (merge hooks manually if needed)"
  fi
}

_install_opencode_global() {
  local cfg="$HOME/.opencode/bauplan.json"
  cat > "$cfg" <<'EOF'
{
  "instructions": "You have the Bauplan methodology available. When asked to generate a project kit or work on a Bauplan-generated kit: read AGENTS.md if present, otherwise read ~/.bauplan/AGENTS.md for the methodology. Follow the 6-phase workflow: clarify, research (verify versions live), write CANON yourself, parallel doc writers, gap audit, execution kit. One story per session. CANON.md is the source of truth — never invent versions."
}
EOF
}

_install_copilot_global() {
  local f="$COPILOT_USER_DIR/instructions.md"
  if [ ! -f "$f" ]; then
    cp "$BAUPLAN_REPO/.github/copilot-instructions.md" "$f" 2>/dev/null || \
    cp "$BAUPLAN_HOME/.github/copilot-instructions.md" "$f" 2>/dev/null || true
  fi
}

# ── new ───────────────────────────────────────────────────────────────────────
cmd_new() {
  local idea="$*"
  need_source
  command -v claude >/dev/null || err "claude CLI not found — install: npm i -g @anthropic-ai/claude-code"

  if [ -z "$idea" ]; then
    printf "${BLD}What would you like to build?${OFF} "
    read -r idea
    [ -z "$idea" ] && err "no idea provided"
  fi

  local slug; slug=$(slug_from "$idea")
  local kit_dir="$PWD/generated-kits/$slug"
  [ -d "$kit_dir" ] && err "kit already exists: $kit_dir\nDelete it or choose a different slug."

  mkdir -p "$kit_dir"
  info "generating kit for: \"$idea\""
  info "output: $kit_dir"
  hr

  local T0; T0=$(date +%s)
  local LOGF="$kit_dir/../.logs/${slug}-$(date +%Y%m%d-%H%M%S).log"
  mkdir -p "$(dirname "$LOGF")"

  local VERBOSE="${BAUPLAN_VERBOSE:-1}"
  local MODEL="${BAUPLAN_MODEL:-sonnet}"
  local MAX_TURNS="${BAUPLAN_MAX_TURNS:-200}"
  local PROMPT="/new-project $idea"

  if [ "$VERBOSE" = "1" ] && command -v python3 >/dev/null; then
    local FMTSCRIPT=""
    for d in "$BAUPLAN_REPO/templates/scripts" "$BAUPLAN_HOME/templates/scripts"; do
      [ -f "$d/stream-fmt.py" ] && { FMTSCRIPT="$d/stream-fmt.py"; break; }
    done
    if [ -n "$FMTSCRIPT" ]; then
      claude -p "$PROMPT" --model "$MODEL" --max-turns "$MAX_TURNS" \
        --permission-mode acceptEdits --verbose --output-format stream-json 2>&1 \
        | tee "$LOGF.jsonl" | python3 "$FMTSCRIPT" | tee "$LOGF"
    else
      claude -p "$PROMPT" --model "$MODEL" --max-turns "$MAX_TURNS" \
        --permission-mode acceptEdits --output-format text 2>&1 | tee "$LOGF"
    fi
  else
    claude -p "$PROMPT" --model "$MODEL" --max-turns "$MAX_TURNS" \
      --permission-mode acceptEdits --output-format text 2>&1 | tee "$LOGF"
  fi
  local RC=$?
  local ELAPSED=$(( $(date +%s) - T0 ))

  [ $RC -ne 0 ] && warn "claude exited $RC — kit may be partial. Check: $LOGF"

  # Zip the kit
  if [ -d "$kit_dir" ] && ls "$kit_dir" >/dev/null 2>&1; then
    python3 - "$slug" "$PWD/generated-kits" <<'PY' 2>/dev/null && ok "kit zipped: generated-kits/$slug.zip" || true
import zipfile, os, sys
slug, base = sys.argv[1], sys.argv[2]
src = os.path.join(base, slug)
dst = os.path.join(base, f'{slug}.zip')
with zipfile.ZipFile(dst, 'w', zipfile.ZIP_DEFLATED) as z:
    for r, _, fs in os.walk(src):
        for f in fs:
            p = os.path.join(r, f)
            z.write(p, os.path.relpath(p, base))
PY
  fi

  hr
  ok "done in $(fmt_t $ELAPSED)"
  printf "\n${BLD}Next steps:${OFF}\n"
  printf "  cd generated-kits/%s\n" "$slug"
  printf "  bash scripts/setup-claude.sh\n"
  printf "  claude          # then: /next-story\n\n"
}

# ── init ──────────────────────────────────────────────────────────────────────
cmd_init() {
  need_source
  h1 "Initialising Bauplan in $(basename "$PWD")"
  hr

  # Copy CLAUDE.md (orchestrator discipline) into current project
  if [ ! -f "CLAUDE.md" ]; then
    cp "$BAUPLAN_REPO/templates/CLAUDE.md" "CLAUDE.md" 2>/dev/null || \
    cp "$BAUPLAN_HOME/templates/CLAUDE.md" "CLAUDE.md" 2>/dev/null
    # Replace placeholder
    sed -i.bak "s/{{PROJECT_NAME}}/$(basename "$PWD")/g" "CLAUDE.md" 2>/dev/null && rm -f "CLAUDE.md.bak"
    ok "CLAUDE.md created"
  else
    info "CLAUDE.md already exists — not modified"
  fi

  # Copy LOOP.md (the build algorithm)
  if [ ! -f "LOOP.md" ]; then
    cp "$BAUPLAN_REPO/templates/LOOP.md" "LOOP.md" 2>/dev/null || \
    cp "$BAUPLAN_HOME/templates/LOOP.md" "LOOP.md" 2>/dev/null
    ok "LOOP.md created"
  else
    info "LOOP.md already exists — not modified"
  fi

  # Per-project .claude/ (hooks + local settings that override the global ones)
  mkdir -p .claude/hooks .claude/commands .claude/agents
  for f in "$BAUPLAN_REPO/templates/claude-config/hooks/"*.sh \
           "$BAUPLAN_HOME/templates/claude-config/hooks/"*.sh; do
    [ -f "$f" ] || continue
    dst=".claude/hooks/$(basename "$f")"
    [ -f "$dst" ] || { cp "$f" "$dst"; chmod +x "$dst"; ok "hook: $(basename "$f")"; }
    break
  done

  # Minimal local settings (hooks wired)
  if [ ! -f ".claude/settings.json" ]; then
    cat > ".claude/settings.json" <<'EOF'
{
  "hooks": {
    "SessionStart": [{"hooks": [{"type":"command","command":"bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-brief.sh\"","timeout":10}]}],
    "PostCompact":  [{"hooks": [{"type":"command","command":"bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/session-brief.sh\"","timeout":10}]}],
    "PreToolUse": [{"matcher":"Edit|Write|MultiEdit|Bash","hooks":[{"type":"command","command":"bash \"$CLAUDE_PROJECT_DIR/.claude/hooks/guard.sh\"","timeout":10}]}]
  }
}
EOF
    ok ".claude/settings.json created"
  fi

  hr
  ok "bauplan initialised in $(pwd)"
  printf "\n  ${CYN}claude → /new-project \"Your idea\"${OFF}   generate a kit here\n"
  printf "  ${CYN}claude → /review-kit${OFF}               audit an existing kit\n\n"
}

# ── review ────────────────────────────────────────────────────────────────────
cmd_review() {
  local path="${1:-.}"
  command -v claude >/dev/null || err "claude CLI not found"
  [ -d "$path" ] || err "not a directory: $path"
  info "running gap audit on: $path"
  claude -p "/review-kit $path" --permission-mode acceptEdits
}

# ── status ────────────────────────────────────────────────────────────────────
cmd_status() {
  local board="${1:-STATUS.md}"
  [ -f "$board" ] || err "STATUS.md not found in $(pwd) — run from a generated kit directory"
  local total done_n inprog blocked
  total=$(grep -cE '^\| S[0-9-]' "$board" 2>/dev/null || echo 0)
  done_n=$(grep -E '^\| S[0-9-]' "$board" 2>/dev/null | grep -c '| done |' || echo 0)
  inprog=$(grep -E '^\| S[0-9-]' "$board" 2>/dev/null | grep -c '| in-progress |' || echo 0)
  blocked=$(grep -E '^\| S[0-9-]' "$board" 2>/dev/null | grep -c '| blocked |' || echo 0)
  h1 "Build status"
  printf "  Progress:    ${GRN}%s${OFF} / %s done\n" "$done_n" "$total"
  [ "$inprog" -gt 0 ] && printf "  In progress: ${CYN}%s${OFF}\n" "$inprog"
  [ "$blocked" -gt 0 ] && printf "  Blocked:     ${RED}%s${OFF}\n" "$blocked"
  printf "\n  Next stories:\n"
  grep -E '^\| S[0-9-]' "$board" 2>/dev/null | grep '| todo |' | head -5 \
    | awk -F'|' '{printf "    %s — %s\n", $2, $3}'
}

# ── doctor ────────────────────────────────────────────────────────────────────
cmd_doctor() {
  h1 "Bauplan doctor"
  hr

  # Core tools
  command -v claude   >/dev/null && ok "claude CLI"     || warn "claude not found  →  npm i -g @anthropic-ai/claude-code"
  command -v python3  >/dev/null && ok "python3"        || warn "python3 not found  →  install Python 3.9+"
  command -v git      >/dev/null && ok "git"            || warn "git not found"
  command -v node     >/dev/null && ok "node"           || info "node not found  (optional — needed for Node.js project kits)"
  command -v opencode >/dev/null && ok "opencode"       || info "opencode not found  (optional)"
  command -v codex    >/dev/null && ok "codex (OpenAI)" || info "codex not found  (optional)"

  # Global config
  [ -d "$HOME/.claude/agents" ] \
    && n=$(ls "$HOME/.claude/agents/bauplan-"*.md 2>/dev/null | wc -l | xargs) \
    && [ "$n" -gt 0 ] \
    && ok "Claude Code global agents ($n bauplan agents installed)" \
    || warn "no bauplan agents in ~/.claude/agents/  →  run: bauplan install"

  [ -d "$HOME/.claude/commands" ] \
    && m=$(ls "$HOME/.claude/commands/bauplan-"*.md "$HOME/.claude/commands/new-project.md" 2>/dev/null | wc -l | xargs) \
    && [ "$m" -gt 0 ] \
    && ok "Claude Code global commands ($m commands)" \
    || warn "no bauplan commands in ~/.claude/commands/  →  run: bauplan install"

  [ -d "$BAUPLAN_HOME/templates" ] && ok "bauplan core at $BAUPLAN_HOME" \
    || warn "bauplan core not found at $BAUPLAN_HOME  →  run: bauplan install"

  hr
  ok "doctor done"
}

# ── upgrade ───────────────────────────────────────────────────────────────────
cmd_upgrade() {
  [ -d "$BAUPLAN_REPO/.git" ] || err "can only upgrade from a git checkout — not an npm install"
  info "pulling latest bauplan from origin…"
  git -C "$BAUPLAN_REPO" pull --ff-only origin main || \
  git -C "$BAUPLAN_REPO" pull --ff-only origin master || \
  err "git pull failed — resolve conflicts manually then re-run: bauplan install"
  ok "repo updated — re-running install…"
  "$BAUPLAN_REPO/bin/bauplan" install
}

# ── help ──────────────────────────────────────────────────────────────────────
cmd_help() {
  cat <<EOF

${BLD}bauplan${OFF} v${BAUPLAN_VERSION} — AI project build-kit generator

${BLD}SETUP${OFF}
  bauplan install          Install globally — agents+commands→~/.claude/, ~/.opencode/, etc.
  bauplan doctor           Verify toolchain and global config
  bauplan upgrade          Pull latest + re-install

${BLD}GENERATE${OFF}
  bauplan new [idea]       Generate a complete project kit (interactive if no idea given)
                           Env: BAUPLAN_MODEL=opus  BAUPLAN_MAX_TURNS=200  BAUPLAN_VERBOSE=0

${BLD}PER-PROJECT${OFF}
  bauplan init             Add CLAUDE.md + LOOP.md + .claude/ to the current directory
  bauplan review [path]    Run the Bauplan gap audit on a kit (default: current dir)
  bauplan status           Show build board from STATUS.md

${BLD}CLAUDE CODE COMMANDS${OFF} (available after ${CYN}bauplan install${OFF})
  /new-project "idea"      Generate a kit (orchestrates scouts → CANON → docs → kit)
  /review-kit [path]       Gap audit on an existing kit
  /bauplan-next-story      Implement the next story
  /bauplan-status          Board report
  /bauplan-barrier         Run the next integration gate

${BLD}DOCS${OFF}
  ~/.bauplan/METHODOLOGY.md   The full specification
  ~/.bauplan/AGENTS.md        Auto-loaded by Codex and OpenCode
  ~/.bauplan/prompts/         Integration guides for each tool

${BLD}INSTALL${OFF}
  curl -fsSL https://raw.githubusercontent.com/your-org/bauplan/main/install.sh | bash
  npm install -g bauplan
  brew install bauplan         (coming soon)

EOF
}

# ── dispatch ──────────────────────────────────────────────────────────────────
CMD="${1:-help}"
shift 2>/dev/null || true

case "$CMD" in
  install)         cmd_install "$@" ;;
  new)             cmd_new "$@" ;;
  init)            cmd_init "$@" ;;
  review)          cmd_review "$@" ;;
  status)          cmd_status "$@" ;;
  doctor)          cmd_doctor "$@" ;;
  upgrade)         cmd_upgrade "$@" ;;
  version|--version|-v) echo "bauplan $BAUPLAN_VERSION" ;;
  help|--help|-h)  cmd_help ;;
  *)               err "unknown command: $CMD\nRun 'bauplan --help' for usage." ;;
esac
