#!/usr/bin/env bash
# extract.sh 从 Cursor 安装目录安全提取 Proto 文件。
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
INSTALLED_CURSOR_DEFAULT="/Applications/Cursor.app"
INPUT_DEFAULT="$INSTALLED_CURSOR_DEFAULT"
OUTPUT_DEFAULT="$PROJECT_DIR/proto"

INPUT_ROOT="${1:-$INPUT_DEFAULT}"
OUTPUT_DIR="${2:-$OUTPUT_DEFAULT}"

canonicalize_path() {
  local path="$1"
  local parent
  local base
  if [[ -d "$path" ]]; then
    (cd "$path" && pwd -P)
    return
  fi
  parent="$(dirname "$path")"
  base="$(basename "$path")"
  if [[ ! -d "$parent" ]]; then
    echo "Parent directory does not exist: $parent" >&2
    return 1
  fi
  printf '%s/%s\n' "$(cd "$parent" && pwd -P)" "$base"
}

# 文件输入只提取自身；目录输入扫描工作台、扩展宿主和扩展产物。
INPUT_PATHS=()

add_input() {
  local candidate="$1"
  local existing
  if [[ ! -f "$candidate" ]]; then
    return 0
  fi
  for existing in "${INPUT_PATHS[@]-}"; do
    [[ "$existing" == "$candidate" ]] && return
  done
  INPUT_PATHS+=("$candidate")
}

if [[ -f "$INPUT_ROOT" ]]; then
  add_input "$INPUT_ROOT"
elif [[ -d "$INPUT_ROOT" ]]; then
  CANDIDATES=(
    "$INPUT_ROOT/Contents/Resources/app/out/vs/workbench/workbench.desktop.main.js"
    "$INPUT_ROOT/Resources/app/out/vs/workbench/workbench.desktop.main.js"
    "$INPUT_ROOT/out/vs/workbench/workbench.desktop.main.js"
    "$INPUT_ROOT/workbench.desktop.main.js"
    "$INPUT_ROOT/Contents/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js"
    "$INPUT_ROOT/Resources/app/out/vs/workbench/api/node/extensionHostProcess.js"
    "$INPUT_ROOT/out/vs/workbench/api/node/extensionHostProcess.js"
    "$INPUT_ROOT/extensionHostProcess.js"
    "$INPUT_ROOT/Contents/Resources/app/extensions/cursor-always-local/dist/main.js"
    "$INPUT_ROOT/Resources/app/extensions/cursor-always-local/dist/main.js"
    "$INPUT_ROOT/extensions/cursor-always-local/dist/main.js"
    "$INPUT_ROOT/cursor-always-local/dist/main.js"
  )
  for CANDIDATE in "${CANDIDATES[@]}"; do
    add_input "$CANDIDATE"
  done
  while IFS= read -r JS_FILE; do
    add_input "$JS_FILE"
  done < <(find "$INPUT_ROOT" -type f ! -path "*/node_modules/*" \( -name "workbench.desktop.main.js" -o -name "extensionHostProcess.js" -o -path "*/extensions/*/dist/main.js" \) | sort)
fi

if [[ -z "${INPUT_PATHS[*]-}" ]]; then
  echo "No supported Cursor JS bundle found under: $INPUT_ROOT" >&2
  echo "Install/update Cursor, or pass an explicit input bundle:" >&2
  echo "  $0 /path/to/Cursor.app [output-dir]" >&2
  exit 1
fi

for INDEX in "${!INPUT_PATHS[@]}"; do
  INPUT_PATHS[$INDEX]="$(canonicalize_path "${INPUT_PATHS[$INDEX]}")"
done
OUTPUT_DIR="$(canonicalize_path "$OUTPUT_DIR")"
CURRENT_DIR="$(pwd -P)"

case "$OUTPUT_DIR" in
  "/"|"$HOME"|"$PROJECT_DIR"|"$SCRIPT_DIR"|"$CURRENT_DIR")
    echo "Refusing unsafe output directory: $OUTPUT_DIR" >&2
    exit 1
    ;;
esac
for INPUT_PATH in "${INPUT_PATHS[@]}"; do
  case "$INPUT_PATH" in
    "$OUTPUT_DIR"|"$OUTPUT_DIR"/*)
      echo "Refusing output directory that contains an input bundle: $OUTPUT_DIR" >&2
      exit 1
      ;;
  esac
done

OUTPUT_PARENT="$(dirname "$OUTPUT_DIR")"
OUTPUT_BASENAME="$(basename "$OUTPUT_DIR")"
TEMP_DIR="$(mktemp -d "$OUTPUT_PARENT/.${OUTPUT_BASENAME}.tmp.XXXXXX")"
BACKUP_DIR=""

cleanup() {
  if [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]]; then
    rm -rf "$TEMP_DIR"
  fi
  if [[ -n "$BACKUP_DIR" && -e "$BACKUP_DIR" ]]; then
    if [[ ! -e "$OUTPUT_DIR" ]]; then
      mv "$BACKUP_DIR" "$OUTPUT_DIR"
    else
      rm -rf "$BACKUP_DIR"
    fi
  fi
}
trap cleanup EXIT

EXTRACT_ARGS=()
for INPUT_PATH in "${INPUT_PATHS[@]}"; do
  EXTRACT_ARGS+=( -input "$INPUT_PATH" )
done

(
  cd "$PROJECT_DIR"
  go run ./extractor \
    "${EXTRACT_ARGS[@]}" \
    -output "$TEMP_DIR" \
    -skip-format \
    -strict
)

if [[ -e "$OUTPUT_DIR" ]]; then
  BACKUP_DIR="$(mktemp -d "$OUTPUT_PARENT/.${OUTPUT_BASENAME}.backup.XXXXXX")"
  rmdir "$BACKUP_DIR"
  mv "$OUTPUT_DIR" "$BACKUP_DIR"
fi
mv "$TEMP_DIR" "$OUTPUT_DIR"
TEMP_DIR=""
if [[ -n "$BACKUP_DIR" ]]; then
  rm -rf "$BACKUP_DIR"
  BACKUP_DIR=""
fi
