#!/bin/bash
#
# Nexus Memory - Beads Sync Hook
# Bidirectional sync between local bd (beads) and Nexus GraphRAG
#
# Attribution:
#   Beads (bd) is created by Steve Yegge
#   Repository: https://github.com/steveyegge/beads
#   License: See the beads repository for license terms.
#
#   This hook provides GraphRAG sync capabilities for the beads issue tracker,
#   enabling cross-device sync and semantic search of issues/tasks.
#
# Usage:
#   bead-sync.sh install       # Download bd binary if not present
#   bead-sync.sh sync          # Full bidirectional sync
#   bead-sync.sh push          # Push local beads to GraphRAG
#   bead-sync.sh pull          # Pull beads from GraphRAG to local
#   bead-sync.sh sync-latest   # Sync only recently changed beads
#   bead-sync.sh query "text"  # Query beads from GraphRAG
#
# Environment Variables:
#   NEXUS_API_KEY        - API key for authentication (REQUIRED)
#   NEXUS_API_URL        - API endpoint (default: https://api.adverant.ai)
#   NEXUS_VERBOSE        - Set to 1 for debug output
#   BD_VERSION           - Beads version to install (default: latest)
#

set -o pipefail

# Source the API key helper for interactive prompting
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/api-key-helper.sh" 2>/dev/null || true

# Configuration
NEXUS_API_KEY="${NEXUS_API_KEY:-}"
NEXUS_API_URL="${NEXUS_API_URL:-https://api.adverant.ai}"
VERBOSE="${NEXUS_VERBOSE:-0}"
BD_VERSION="${BD_VERSION:-latest}"

# Paths
BD_BIN="${HOME}/.local/bin/bd"
STATE_DIR="${HOME}/.claude/session-env"
LAST_BEAD_SYNC="${STATE_DIR}/last_bead_sync"
LAST_BEAD_ID="${STATE_DIR}/last_bead_id"

# Logging
log() {
  if [[ "$VERBOSE" == "1" ]]; then
    echo "[bead-sync] $1" >&2
  fi
}

log_error() {
  echo "[bead-sync] ERROR: $1" >&2
}

log_info() {
  echo "[bead-sync] $1" >&2
}

# Ensure state directory exists
mkdir -p "$STATE_DIR"
mkdir -p "$(dirname "$BD_BIN")"

# Check for API key - with interactive prompt support
check_api_key() {
  # Try loading from saved file first
  if type load_api_key &>/dev/null; then
    load_api_key
  fi

  if [[ -z "$NEXUS_API_KEY" ]]; then
    # If interactive terminal, prompt for key
    if [[ -t 0 ]] && type require_api_key &>/dev/null; then
      require_api_key --interactive || return 1
    else
      log_error "NEXUS_API_KEY environment variable is required but not set."
      log_error "Get your API key from: https://dashboard.adverant.ai/dashboard/api-keys"
      return 1
    fi
  fi
  return 0
}

# Check dependencies
check_deps() {
  if ! command -v jq &> /dev/null; then
    log_error "jq is required but not installed. Install with: brew install jq"
    return 1
  fi
  if ! command -v curl &> /dev/null; then
    log_error "curl is required but not installed."
    return 1
  fi
  return 0
}

# Detect platform for binary download
detect_platform() {
  local os=$(uname -s | tr '[:upper:]' '[:lower:]')
  local arch=$(uname -m)

  # Normalize architecture
  case "$arch" in
    x86_64)  arch="amd64" ;;
    aarch64) arch="arm64" ;;
    arm64)   arch="arm64" ;;
  esac

  echo "${os}_${arch}"
}

# Install bd binary if not present
install_bd() {
  # Check if already installed and working
  if [[ -f "$BD_BIN" ]] && [[ -x "$BD_BIN" ]]; then
    if "$BD_BIN" --version &>/dev/null; then
      log "bd binary already installed and working at $BD_BIN"
      "$BD_BIN" --version 2>/dev/null || true
      return 0
    else
      log "bd binary exists but not working, reinstalling..."
      rm -f "$BD_BIN"
    fi
  fi

  log_info "Installing beads (bd) binary..."

  local platform=$(detect_platform)
  local download_url
  local temp_dir=$(mktemp -d)

  if [[ "$BD_VERSION" == "latest" ]]; then
    # Get latest release URL from GitHub API
    # The releases are .tar.gz archives, not raw binaries
    download_url=$(curl -s "https://api.github.com/repos/steveyegge/beads/releases/latest" | \
      jq -r ".assets[] | select(.name | test(\"${platform}\")) | select(.name | endswith(\".tar.gz\")) | .browser_download_url" | head -1)
  else
    download_url="https://github.com/steveyegge/beads/releases/download/${BD_VERSION}/beads_${BD_VERSION}_${platform}.tar.gz"
  fi

  if [[ -z "$download_url" ]] || [[ "$download_url" == "null" ]]; then
    log_error "Could not find bd binary for platform: $platform"
    log_error "Try installing manually: go install github.com/steveyegge/beads/cmd/bd@latest"
    rm -rf "$temp_dir"
    return 1
  fi

  log "Downloading from: $download_url"

  # Download and extract the tarball
  local tarball="${temp_dir}/beads.tar.gz"
  if curl -sL "$download_url" -o "$tarball"; then
    # Extract to temp directory
    if tar -xzf "$tarball" -C "$temp_dir" 2>/dev/null; then
      # Find the bd binary in the extracted files
      local extracted_bd=$(find "$temp_dir" -name "bd" -type f -perm +111 2>/dev/null | head -1)
      if [[ -z "$extracted_bd" ]]; then
        # Try without execute permission check
        extracted_bd=$(find "$temp_dir" -name "bd" -type f 2>/dev/null | head -1)
      fi

      if [[ -n "$extracted_bd" ]] && [[ -f "$extracted_bd" ]]; then
        cp "$extracted_bd" "$BD_BIN"
        chmod +x "$BD_BIN"
        rm -rf "$temp_dir"

        if "$BD_BIN" --version &>/dev/null; then
          log_info "Successfully installed bd to $BD_BIN"
          "$BD_BIN" --version 2>/dev/null || true
          return 0
        else
          log_error "bd binary installed but not executable"
          rm -f "$BD_BIN"
          return 1
        fi
      else
        log_error "Could not find bd binary in archive"
        log "Archive contents:"
        tar -tzf "$tarball" 2>/dev/null | head -10 >&2
        rm -rf "$temp_dir"
        return 1
      fi
    else
      log_error "Failed to extract tarball"
      rm -rf "$temp_dir"
      return 1
    fi
  else
    log_error "Failed to download bd binary"
    rm -rf "$temp_dir"
    return 1
  fi
}

# Check if bd is available
ensure_bd() {
  if [[ ! -x "$BD_BIN" ]] && ! command -v bd &> /dev/null; then
    install_bd || return 1
  fi
  return 0
}

# Get the bd command (prefer local install)
get_bd_cmd() {
  if [[ -x "$BD_BIN" ]]; then
    echo "$BD_BIN"
  elif command -v bd &> /dev/null; then
    echo "bd"
  else
    echo ""
  fi
}

# Get current project info
get_project_info() {
  local project_dir=$(pwd)
  local project_name=$(basename "$project_dir")
  local git_remote=$(git remote get-url origin 2>/dev/null || echo "local")

  echo "{\"dir\": \"$project_dir\", \"name\": \"$project_name\", \"remote\": \"$git_remote\"}"
}

# Push a single bead to GraphRAG
push_bead_to_graphrag() {
  local bead_json="$1"
  local project_info="$2"

  local bead_id=$(echo "$bead_json" | jq -r '.id // .ID // empty')
  local title=$(echo "$bead_json" | jq -r '.title // .Title // empty')
  local description=$(echo "$bead_json" | jq -r '.description // .Description // ""')
  local status=$(echo "$bead_json" | jq -r '.status // .Status // "open"')
  local priority=$(echo "$bead_json" | jq -r '.priority // .Priority // "P2"')
  local issue_type=$(echo "$bead_json" | jq -r '.issueType // .type // "task"')
  local created_at=$(echo "$bead_json" | jq -r '.createdAt // .created_at // empty')
  local deps=$(echo "$bead_json" | jq -c '.dependencies // []')

  local project_name=$(echo "$project_info" | jq -r '.name')
  local project_dir=$(echo "$project_info" | jq -r '.dir')

  if [[ -z "$bead_id" ]]; then
    log "Skipping bead with no ID"
    return 0
  fi

  log "Pushing bead $bead_id: $title"

  # Build content for memory storage
  local content="[Bead $bead_id] $title

Status: $status
Priority: $priority
Type: $issue_type
Project: $project_name

$description"

  # Build tags array
  local tags=$(jq -n \
    --arg id "$bead_id" \
    --arg status "$status" \
    --arg priority "$priority" \
    --arg project "$project_name" \
    --arg type "$issue_type" \
    '["bead", "type:bead", ("bead-id:" + $id), ("status:" + $status), ("priority:" + $priority), ("project:" + $project), ("issue-type:" + $type)]')

  # Build metadata
  local metadata=$(jq -n \
    --arg bead_id "$bead_id" \
    --arg title "$title" \
    --arg status "$status" \
    --arg priority "$priority" \
    --arg issue_type "$issue_type" \
    --arg project "$project_name" \
    --arg project_dir "$project_dir" \
    --arg created_at "$created_at" \
    --argjson deps "$deps" \
    '{
      bead_id: $bead_id,
      title: $title,
      status: $status,
      priority: $priority,
      issue_type: $issue_type,
      project: $project,
      project_dir: $project_dir,
      created_at: $created_at,
      dependencies: $deps
    }')

  # Build full payload
  local payload=$(jq -n \
    --arg content "$content" \
    --argjson tags "$tags" \
    --argjson metadata "$metadata" \
    --arg bead_id "$bead_id" \
    '{
      content: $content,
      event_type: "bead",
      tags: $tags,
      metadata: $metadata,
      extract_entities: true,
      entity_types: ["bead", "task", "feature", "bug", "project", "file", "function"],
      domain: "code",
      create_relationships: true,
      episodic: {
        type: "bead",
        interaction_id: $bead_id
      }
    }')

  # Send to GraphRAG
  local response=$(curl -s -X POST "${NEXUS_API_URL}/api/memory/store" \
    -H "Authorization: Bearer ${NEXUS_API_KEY}" \
    -H "Content-Type: application/json" \
    -d "$payload" \
    --max-time 10)

  local success=$(echo "$response" | jq -r '.success // false')

  if [[ "$success" == "true" ]]; then
    log "Successfully pushed bead $bead_id"
    echo "$bead_id" > "$LAST_BEAD_ID"
    return 0
  else
    log_error "Failed to push bead $bead_id: $(echo "$response" | jq -r '.message // .error // "unknown error"')"
    return 1
  fi
}

# Push all local beads to GraphRAG
push_beads() {
  check_api_key || return 1
  ensure_bd || return 1

  local bd_cmd=$(get_bd_cmd)
  if [[ -z "$bd_cmd" ]]; then
    log_error "bd command not available"
    return 1
  fi

  # Check if beads is initialized in this repo
  if [[ ! -d ".beads" ]] && ! "$bd_cmd" list &>/dev/null; then
    log "No beads found in this repository. Run 'bd init' to initialize."
    return 0
  fi

  local project_info=$(get_project_info)
  local beads=$("$bd_cmd" list --json 2>/dev/null || echo "[]")
  local count=$(echo "$beads" | jq 'length')

  log_info "Pushing $count beads to GraphRAG..."

  local pushed=0
  local failed=0

  echo "$beads" | jq -c '.[]' 2>/dev/null | while read -r bead; do
    if push_bead_to_graphrag "$bead" "$project_info"; then
      ((pushed++)) || true
    else
      ((failed++)) || true
    fi
  done

  # Record sync timestamp
  date -u +"%Y-%m-%dT%H:%M:%SZ" > "$LAST_BEAD_SYNC"

  log_info "Push complete. Pushed: $pushed, Failed: $failed"
  return 0
}

# Pull beads from GraphRAG and create locally if missing
pull_beads() {
  check_api_key || return 1
  ensure_bd || return 1

  local bd_cmd=$(get_bd_cmd)
  if [[ -z "$bd_cmd" ]]; then
    log_error "bd command not available"
    return 1
  fi

  local project_info=$(get_project_info)
  local project_name=$(echo "$project_info" | jq -r '.name')

  log_info "Pulling beads from GraphRAG for project: $project_name"

  # Query GraphRAG for beads from this project
  local query_payload=$(jq -n \
    --arg project "$project_name" \
    '{
      query: "beads tasks issues",
      filters: {
        tags: ["type:bead", ("project:" + $project)]
      },
      limit: 100,
      includeEpisodic: true
    }')

  local response=$(curl -s -X POST "${NEXUS_API_URL}/api/retrieve/enhanced" \
    -H "Authorization: Bearer ${NEXUS_API_KEY}" \
    -H "Content-Type: application/json" \
    -d "$query_payload" \
    --max-time 15)

  local memories=$(echo "$response" | jq -c '.data.memories // .memories // []')
  local count=$(echo "$memories" | jq 'length')

  log "Found $count beads in GraphRAG"

  if [[ "$count" == "0" ]]; then
    log "No beads to pull from GraphRAG"
    return 0
  fi

  # Check if beads is initialized
  if [[ ! -d ".beads" ]]; then
    log "Initializing beads in this repository..."
    "$bd_cmd" init 2>/dev/null || true
  fi

  local created=0
  local skipped=0

  # For each bead in GraphRAG, check if it exists locally
  echo "$memories" | jq -c '.[]' 2>/dev/null | while read -r memory; do
    local bead_id=$(echo "$memory" | jq -r '.metadata.bead_id // empty')
    local title=$(echo "$memory" | jq -r '.metadata.title // empty')
    local status=$(echo "$memory" | jq -r '.metadata.status // "open"')
    local priority=$(echo "$memory" | jq -r '.metadata.priority // "P2"')

    if [[ -z "$bead_id" ]]; then
      continue
    fi

    # Check if bead exists locally
    if "$bd_cmd" show "$bead_id" &>/dev/null; then
      log "Bead $bead_id already exists locally, skipping"
      ((skipped++)) || true
      continue
    fi

    # Create bead locally
    log "Creating bead $bead_id: $title"

    # Note: bd create may not support --id flag, this is a best-effort attempt
    # The actual bd CLI might have different flags
    if "$bd_cmd" create "$title" --priority "$priority" 2>/dev/null; then
      ((created++)) || true
    else
      log "Could not create bead locally (may already exist with different ID)"
    fi
  done

  log_info "Pull complete. Created: $created, Skipped: $skipped"
  return 0
}

# Full bidirectional sync
sync_bidirectional() {
  log_info "Starting bidirectional sync..."

  push_beads
  pull_beads

  log_info "Bidirectional sync complete"
}

# Sync only recently changed beads (since last sync)
sync_latest() {
  check_api_key || return 1
  ensure_bd || return 1

  local bd_cmd=$(get_bd_cmd)
  if [[ -z "$bd_cmd" ]]; then
    log_error "bd command not available"
    return 1
  fi

  local last_sync=""
  if [[ -f "$LAST_BEAD_SYNC" ]]; then
    last_sync=$(cat "$LAST_BEAD_SYNC")
  fi

  log "Syncing beads changed since: ${last_sync:-never}"

  # For now, just do a full push (bd doesn't have --since flag)
  # In the future, we could track which beads have changed
  push_beads
}

# Query beads from GraphRAG
query_beads() {
  local query_text="$1"

  check_api_key || return 1

  if [[ -z "$query_text" ]]; then
    query_text="beads tasks issues"
  fi

  local project_info=$(get_project_info)
  local project_name=$(echo "$project_info" | jq -r '.name')

  log "Querying GraphRAG for: $query_text"

  local query_payload=$(jq -n \
    --arg query "$query_text" \
    --arg project "$project_name" \
    '{
      query: $query,
      filters: {
        tags: ["type:bead"]
      },
      limit: 20,
      includeEpisodic: true
    }')

  local response=$(curl -s -X POST "${NEXUS_API_URL}/api/retrieve/enhanced" \
    -H "Authorization: Bearer ${NEXUS_API_KEY}" \
    -H "Content-Type: application/json" \
    -d "$query_payload" \
    --max-time 15)

  # Pretty print results
  echo "$response" | jq -r '.data.memories // .memories // [] | .[] |
    "[\(.metadata.bead_id // "?")] \(.metadata.title // .content[0:50]) (\(.metadata.status // "?"))"'
}

# Show help
show_help() {
  cat << 'EOF'
Beads Sync - Bidirectional sync between local bd and Nexus GraphRAG

Attribution:
  Beads (bd) is created by Steve Yegge
  https://github.com/steveyegge/beads

Usage:
  bead-sync.sh <command> [args]

Commands:
  install       Download and install bd binary if not present
  sync          Full bidirectional sync (push local, pull remote)
  push          Push all local beads to GraphRAG
  pull          Pull beads from GraphRAG to local repository
  sync-latest   Sync only recently changed beads
  query "text"  Search for beads in GraphRAG
  help          Show this help message

Environment Variables:
  NEXUS_API_KEY   API key for authentication (required)
  NEXUS_API_URL   API endpoint (default: https://api.adverant.ai)
  NEXUS_VERBOSE   Set to 1 for debug output
  BD_VERSION      Beads version to install (default: latest)

Examples:
  # Install bd binary
  bead-sync.sh install

  # Full sync
  bead-sync.sh sync

  # Push local beads to GraphRAG
  bead-sync.sh push

  # Search for authentication-related beads
  bead-sync.sh query "authentication login"
EOF
}

# Main command dispatcher
main() {
  local command="${1:-help}"
  shift || true

  check_deps || exit 1

  case "$command" in
    install)
      install_bd
      ;;
    sync)
      sync_bidirectional
      ;;
    push)
      push_beads
      ;;
    pull)
      pull_beads
      ;;
    sync-latest)
      sync_latest
      ;;
    query)
      query_beads "$1"
      ;;
    help|--help|-h)
      show_help
      ;;
    *)
      log_error "Unknown command: $command"
      show_help
      exit 1
      ;;
  esac
}

# Run main
main "$@"