#!/usr/bin/env bash
# ────────────────────────────────────────────────────────────────────────────────
# Qovery Remote Development Environment — Install Script
#
# Replaces the need to `FROM codercom/code-server:4.118.0`.
# Add one line to any Debian/Ubuntu-based Dockerfile:
#
#   RUN curl -fsSL https://rde.qovery.com/install.sh | bash
#
# Optionally place a .config.rde.qovery.yml in the build context (or set
# $RDE_CONFIG) to customise which components are installed.
# If no config file is found, EVERYTHING is installed with sensible defaults.
# ────────────────────────────────────────────────────────────────────────────────
set -euo pipefail

# Prevent apt/debconf from prompting interactively (e.g., tzdata geographic
# area selection). When this script is piped via `curl ... | bash`, an
# interactive debconf prompt would consume the remaining script lines from
# stdin, silently truncating execution.
export DEBIAN_FRONTEND=noninteractive

# ── Helpers ──────────────────────────────────────────────────────────────────

log() {
  local msg="$1"
  printf '[rde-setup] %s\n' "$msg"
}

log_error() { printf '[rde-setup] [ERROR] %s\n' "$1" >&2; }
log_warn()  { printf '[rde-setup] [WARN] %s\n' "$1" >&2; }
log_skip()  { printf '[rde-setup] [SKIP] %s\n' "$1"; }

STEP_CURRENT=0
STEP_TOTAL=0
_step_start=0

log_step() {
  STEP_CURRENT=$((STEP_CURRENT + 1))
  _step_start=$SECONDS
  printf '[rde-setup] [%d/%d] %s\n' "$STEP_CURRENT" "$STEP_TOTAL" "$1"
}

log_done() {
  local elapsed=$(( SECONDS - _step_start ))
  printf '[rde-setup] %s (%ds)\n' "$1" "$elapsed"
}

# Run a command with its stdout/stderr prefixed
log_cmd() {
  "$@" 2>&1 | while IFS= read -r line; do
    printf '[rde-setup]   | %s\n' "$line"
  done
  return "${PIPESTATUS[0]}"
}

# Minimal YAML reader — no external deps, handles key: value (scalars only).
# Usage: cfg <key> [default]
cfg() {
  local key="$1" default="${2:-true}"
  if [[ -f "$CONFIG_FILE" ]]; then
    local val
    val=$(grep -E "^${key}:" "$CONFIG_FILE" 2>/dev/null | head -1 \
      | sed 's/^[^:]*:[[:space:]]*//' | sed 's/^["'"'"']//' | sed 's/["'"'"']$//' | xargs 2>/dev/null || true)
    [[ -n "$val" ]] && echo "$val" || echo "$default"
  else
    echo "$default"
  fi
}

# Returns success (0) unless the config value is literally "false".
is_enabled() {
  local val
  val=$(cfg "$1" "${2:-true}")
  [[ "$val" != "false" ]]
}

# If the config value is "true" (or missing), return the supplied default
# version. Otherwise return the explicit version string from the config.
get_version() {
  local val
  val=$(cfg "$1" "${2:-true}")
  if [[ "$val" == "true" ]]; then
    echo "$2"
  else
    echo "$val"
  fi
}

# Resolve SHA256 checksum for a tool+version+arch.
# Priority: user-provided config → hardcoded default (if version matches) → fail.
resolve_checksum() {
  local tool="$1" version="$2" arch="$3" default_version="$4" default_checksum="$5"

  # 1. User-provided checksum in config
  local config_key="${tool}_sha256_${arch}"
  local user_checksum
  user_checksum=$(cfg "$config_key" "")
  if [[ -n "$user_checksum" && "$user_checksum" != "true" ]]; then
    echo "$user_checksum"
    return 0
  fi

  # 2. Hardcoded default (only when version matches the script's pinned default)
  if [[ "$version" == "$default_version" && -n "$default_checksum" ]]; then
    echo "$default_checksum"
    return 0
  fi

  # 3. No checksum — fail secure
  log_error "No SHA256 checksum found for ${tool} ${version} (${arch})."
  log_error "You are using a custom version. Add the checksum to your .config.rde.qovery.yml:"
  log_error "  ${config_key}: \"<sha256-hash>\""
  log_error "Or use the default version: ${default_version}"
  return 1
}

# ── Pre-flight checks ───────────────────────────────────────────────────────

if [[ "$(id -u)" -ne 0 ]]; then
  log_error "This script must run as root (e.g. inside a Dockerfile RUN instruction)."
  exit 1
fi

if ! command -v apt-get &>/dev/null; then
  log_error "apt-get not found. This script requires a Debian/Ubuntu-based image."
  exit 1
fi

# ── Config file discovery ────────────────────────────────────────────────────

CONFIG_FILE="${RDE_CONFIG:-.config.rde.qovery.yml}"
if [[ -f "$CONFIG_FILE" ]]; then
  log "Using config file: $CONFIG_FILE"
else
  log "No config file found — installing everything with defaults."
  CONFIG_FILE=""   # empty string ⇒ cfg() always returns the default
fi

# ── User configuration ───────────────────────────────────────────────────────

if [[ -n "${RDE_USER:-}" ]]; then
  INSTALL_USER="$RDE_USER"
elif [[ -n "$CONFIG_FILE" ]]; then
  INSTALL_USER=$(cfg "rde_user" "coder")
  [[ "$INSTALL_USER" == "true" ]] && INSTALL_USER="coder"
else
  INSTALL_USER="coder"
fi

if id -u "$INSTALL_USER" &>/dev/null; then
  INSTALL_HOME=$(eval echo "~$INSTALL_USER")
  log "User $INSTALL_USER already exists (home: $INSTALL_HOME)"
else
  INSTALL_HOME="/home/${INSTALL_USER}"
fi
log "Target user: $INSTALL_USER (home: $INSTALL_HOME)"

# ── Count enabled steps for progress indicator ──────────────────────────────

STEP_TOTAL=1  # system deps always installed
is_enabled "nodejs"   && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "python"   && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "ruby"     && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "go"       && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "gh"       && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "zellij"   && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "ttyd"     && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "qovery_cli"  && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "code_server" && STEP_TOTAL=$((STEP_TOTAL + 1))
{ is_enabled "claude_code" || is_enabled "opencode" || is_enabled "codex"; } \
  && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "cursor"   && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "rtk"      && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "entrypoint"  && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "resources"   && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "builder_startup_extension" && is_enabled "code_server" \
  && STEP_TOTAL=$((STEP_TOTAL + 1))
is_enabled "qovery_skills" && STEP_TOTAL=$((STEP_TOTAL + 1))
BUILD_START=$SECONDS

# ── Architecture detection ───────────────────────────────────────────────────

ARCH=$(dpkg --print-architecture 2>/dev/null || uname -m)
case "$ARCH" in
  amd64|x86_64)  ARCH="amd64"; GO_ARCH="amd64"; ZELLIJ_ARCH="x86_64"; TTYD_ARCH="x86_64"; CURSOR_ARCH="x64" ;;
  arm64|aarch64) ARCH="arm64"; GO_ARCH="arm64"; ZELLIJ_ARCH="aarch64"; TTYD_ARCH="aarch64"; CURSOR_ARCH="arm64" ;;
  *) log_error "Unsupported architecture: $ARCH"; exit 1 ;;
esac
log "Detected architecture: $ARCH"

# ── Remote resource base URL ────────────────────────────────────────────────

REMOTE_BASE="https://raw.githubusercontent.com/Qovery/remote-dev-env-template/main"

# ── Create workspace user if missing ─────────────────────────────────────────

if ! id -u "$INSTALL_USER" &>/dev/null; then
  log "Creating user $INSTALL_USER..."
  useradd -m -s /bin/bash "$INSTALL_USER"
fi

# ── 1. System dependencies (always installed) ───────────────────────────────

log_step "Installing system dependencies..."
log_cmd apt-get update -qq

# Core (always)
SYSTEM_PKGS="curl git jq make unzip xz-utils ca-certificates"

# Optional system tools
is_enabled "ripgrep"  && SYSTEM_PKGS="$SYSTEM_PKGS ripgrep"
is_enabled "fzf"      && SYSTEM_PKGS="$SYSTEM_PKGS fzf"
is_enabled "htop"     && SYSTEM_PKGS="$SYSTEM_PKGS htop"
is_enabled "wget"     && SYSTEM_PKGS="$SYSTEM_PKGS wget"
is_enabled "tree"     && SYSTEM_PKGS="$SYSTEM_PKGS tree"

# shellcheck disable=SC2086
log_cmd apt-get install -y --no-install-recommends $SYSTEM_PKGS
log_done "System dependencies installed."

# ── 2. Node.js ───────────────────────────────────────────────────────────────

if is_enabled "nodejs"; then
  NODE_MAJOR=$(get_version "nodejs" "22")
  log_step "Installing Node.js ${NODE_MAJOR}.x (GPG-verified apt source)..."
  # Use GPG keyring-based apt setup instead of piping nodesource setup script into bash.
  # This avoids executing an unverified remote script; apt itself verifies package signatures.
  mkdir -p /etc/apt/keyrings
  curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
    | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg 2>/dev/null
  echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_${NODE_MAJOR}.x nodistro main" \
    > /etc/apt/sources.list.d/nodesource.list
  log_cmd apt-get update -qq
  log_cmd apt-get install -y --no-install-recommends nodejs
  log_done "Node.js $(node --version) installed."
else
  log_skip "Node.js (disabled)"
fi

# ── 3. Python ────────────────────────────────────────────────────────────────

if is_enabled "python"; then
  log_step "Installing Python 3 + pip + venv..."
  log_cmd apt-get install -y --no-install-recommends python3 python3-pip python3-venv
  log_done "Python $(python3 --version 2>&1 | awk '{print $2}') installed."
else
  log_skip "Python (disabled)"
fi

# ── 4. Ruby ──────────────────────────────────────────────────────────────────

if is_enabled "ruby"; then
  log_step "Installing Ruby + Bundler..."
  log_cmd apt-get install -y --no-install-recommends ruby ruby-dev build-essential
  log_cmd gem install bundler --no-document
  log_done "Ruby $(ruby --version | awk '{print $2}') installed."
else
  log_skip "Ruby (disabled)"
fi

# ── Clean apt caches ─────────────────────────────────────────────────────────

rm -rf /var/lib/apt/lists/*

# ── 5. Go ────────────────────────────────────────────────────────────────────

if is_enabled "go"; then
  GO_VERSION=$(get_version "go" "1.26.4")
  # SHA256 checksums for integrity verification (pinned per architecture)
  GO_DEFAULT_SHA256_amd64="1153d3d50e0ac764b447adfe05c2bcf08e889d42a02e0fe0259bd47f6733ad7f"
  GO_DEFAULT_SHA256_arm64="ef758ae7c6cf9267c9c0ef080b8965f453d89ab2d25d9eb22de4405925238768"
  GO_DEFAULT_SHA_VAR="GO_DEFAULT_SHA256_${ARCH}"
  GO_SHA256=$(resolve_checksum "go" "$GO_VERSION" "$ARCH" "1.26.4" "${!GO_DEFAULT_SHA_VAR}") \
    || exit 1
  log_step "Installing Go ${GO_VERSION}..."
  curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GO_ARCH}.tar.gz" -o /tmp/go.tar.gz
  echo "${GO_SHA256}  /tmp/go.tar.gz" | sha256sum -c - || { log_error "Go checksum mismatch!"; exit 1; }
  tar -C /usr/local -xzf /tmp/go.tar.gz
  rm /tmp/go.tar.gz

  # Make Go available system-wide
  cat > /etc/profile.d/go.sh <<GOEOF
export PATH="/usr/local/go/bin:${INSTALL_HOME}/go/bin:\${PATH}"
GOEOF
  chmod +x /etc/profile.d/go.sh

  # Also write to /etc/environment so non-login shells pick it up
  if ! grep -q '/usr/local/go/bin' /etc/environment 2>/dev/null; then
    CURRENT_PATH=$(grep '^PATH=' /etc/environment 2>/dev/null | head -1 | sed 's/^PATH="//' | sed 's/"$//' || echo "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin")
    echo "PATH=\"/usr/local/go/bin:${INSTALL_HOME}/go/bin:${CURRENT_PATH}\"" > /etc/environment
  fi

  export PATH="/usr/local/go/bin:${INSTALL_HOME}/go/bin:${PATH}"
  log_done "Go $(go version | awk '{print $3}') installed."
else
  log_skip "Go (disabled)"
fi

# ── 6. GitHub CLI ────────────────────────────────────────────────────────────

if is_enabled "gh"; then
  GH_VERSION=$(get_version "gh" "2.74.1")
  GH_DEFAULT_SHA256_amd64="d62406233a42e0dc577dcead8d7bafabcc4c548d9c3a6da761c6709bc8f4b373"
  GH_DEFAULT_SHA256_arm64="34c2059f9b947a1df40f39e060b76a5f6734d73a5e233a6e5466d2b417d400f4"
  GH_DEFAULT_SHA_VAR="GH_DEFAULT_SHA256_${ARCH}"
  GH_SHA256=$(resolve_checksum "gh" "$GH_VERSION" "$ARCH" "2.74.1" "${!GH_DEFAULT_SHA_VAR}") \
    || exit 1
  log_step "Installing GitHub CLI ${GH_VERSION}..."
  cd /tmp
  curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_${ARCH}.tar.gz" -o gh.tar.gz
  echo "${GH_SHA256}  gh.tar.gz" | sha256sum -c - || { log_error "GitHub CLI checksum mismatch!"; exit 1; }
  tar -xzf gh.tar.gz
  install "gh_${GH_VERSION}_linux_${ARCH}/bin/gh" /usr/local/bin/gh
  rm -rf gh.tar.gz "gh_${GH_VERSION}_linux_${ARCH}"
  log_done "GitHub CLI $(gh --version | head -1 | awk '{print $3}') installed."
else
  log_skip "GitHub CLI (disabled)"
fi

# ── 7. Zellij ────────────────────────────────────────────────────────────────

if is_enabled "zellij"; then
  ZELLIJ_VERSION=$(get_version "zellij" "0.44.3")
  ZELLIJ_DEFAULT_SHA256_amd64="0f7c346788627f506c0a28296517768633cff24fc822a739f8264b640ecad751"
  ZELLIJ_DEFAULT_SHA256_arm64="d2d473c636bc79a992e9f46e5070c96d2aa3082e3a80d96cd7b8b32c2c0d8035"
  ZELLIJ_DEFAULT_SHA_VAR="ZELLIJ_DEFAULT_SHA256_${ARCH}"
  ZELLIJ_SHA256=$(resolve_checksum "zellij" "$ZELLIJ_VERSION" "$ARCH" "0.44.3" "${!ZELLIJ_DEFAULT_SHA_VAR}") \
    || exit 1
  log_step "Installing Zellij ${ZELLIJ_VERSION}..."
  cd /tmp
  curl -fsSL "https://github.com/zellij-org/zellij/releases/download/v${ZELLIJ_VERSION}/zellij-${ZELLIJ_ARCH}-unknown-linux-musl.tar.gz" -o zellij.tar.gz
  echo "${ZELLIJ_SHA256}  zellij.tar.gz" | sha256sum -c - || { log_error "Zellij checksum mismatch!"; exit 1; }
  tar -xzf zellij.tar.gz
  install -m 755 zellij /usr/local/bin/zellij
  rm -rf zellij zellij.tar.gz

  # Zellij config — transparent mode (no status bar, no pane frames, no UI chrome)
  mkdir -p /etc/zellij
  cat > /etc/zellij/config.kdl <<'ZELLIJEOF'
simplified_ui true
pane_frames false
default_layout "compact"
default_shell "/bin/bash"
show_release_notes false
show_startup_tips false
mouse_mode false
copy_on_select true
copy_clipboard "system"
ZELLIJEOF

  # Set config path system-wide
  cat > /etc/profile.d/zellij.sh <<'ZELJSHEOF'
export ZELLIJ_CONFIG_FILE=/etc/zellij/config.kdl
ZELJSHEOF
  chmod +x /etc/profile.d/zellij.sh

  log_done "Zellij ${ZELLIJ_VERSION} installed."
else
  log_skip "Zellij (disabled)"
fi

# ── 8. ttyd ──────────────────────────────────────────────────────────────────

if is_enabled "ttyd"; then
  TTYD_VERSION=$(get_version "ttyd" "1.7.7")
  TTYD_DEFAULT_SHA256_amd64="8a217c968aba172e0dbf3f34447218dc015bc4d5e59bf51db2f2cd12b7be4f55"
  TTYD_DEFAULT_SHA256_arm64="b0327f0b1f8b01c98a7d4351e45c0776e4bdf1309e325996ab2cbd252386b7d1"
  TTYD_DEFAULT_SHA_VAR="TTYD_DEFAULT_SHA256_${ARCH}"
  TTYD_SHA256=$(resolve_checksum "ttyd" "$TTYD_VERSION" "$ARCH" "1.7.7" "${!TTYD_DEFAULT_SHA_VAR}") \
    || exit 1
  log_step "Installing ttyd ${TTYD_VERSION}..."
  curl -fsSL "https://github.com/tsl0922/ttyd/releases/download/${TTYD_VERSION}/ttyd.${TTYD_ARCH}" -o /usr/local/bin/ttyd
  echo "${TTYD_SHA256}  /usr/local/bin/ttyd" | sha256sum -c - || { log_error "ttyd checksum mismatch!"; exit 1; }
  chmod +x /usr/local/bin/ttyd
  log_done "ttyd ${TTYD_VERSION} installed."
else
  log_skip "ttyd (disabled)"
fi

# ── 9. Qovery CLI ───────────────────────────────────────────────────────────

if is_enabled "qovery_cli"; then
  log_step "Installing Qovery CLI..."
  curl -s https://get.qovery.com | bash 2>&1 | while IFS= read -r line; do
    printf '[rde-setup]   | %s\n' "$line"
  done
  log_done "Qovery CLI installed."
else
  log_skip "Qovery CLI (disabled)"
fi

# ── 10. code-server ─────────────────────────────────────────────────────────

if is_enabled "code_server"; then
  CS_VERSION=$(get_version "code_server" "4.118.0")
  log_step "Installing code-server ${CS_VERSION}..."
  curl -fsSL https://code-server.dev/install.sh | sh -s -- --version="${CS_VERSION}" 2>&1 | while IFS= read -r line; do
    printf '[rde-setup]   | %s\n' "$line"
  done
  log "code-server ${CS_VERSION} installed."

  # Configure Microsoft VS Code Marketplace (required for Claude Code, Copilot, etc.)
  MARKETPLACE_JSON='{"serviceUrl":"https://marketplace.visualstudio.com/_apis/public/gallery","itemUrl":"https://marketplace.visualstudio.com/items","cacheUrl":"https://vscode.blob.core.windows.net/gallery/index","controlUrl":"","recommendationsUrl":""}'

  cat > /etc/profile.d/code-server.sh <<CSEOF
export EXTENSIONS_GALLERY='${MARKETPLACE_JSON}'
export CS_DISABLE_GETTING_STARTED_OVERRIDE=1
CSEOF
  chmod +x /etc/profile.d/code-server.sh

  # Export now so extension installs below can use the marketplace
  export EXTENSIONS_GALLERY="${MARKETPLACE_JSON}"
  export CS_DISABLE_GETTING_STARTED_OVERRIDE=1

  # VS Code extensions
  EXTENSIONS_DEFAULT="anthropic.claude-code,github.copilot,ms-vscode.live-server,ms-python.python,bradlc.vscode-tailwindcss,esbenp.prettier-vscode"
  EXTENSIONS_LIST=$(cfg "extensions" "$EXTENSIONS_DEFAULT")

  log "Installing VS Code extensions..."
  IFS=',' read -ra EXT_ARRAY <<< "$EXTENSIONS_LIST"
  for ext in "${EXT_ARRAY[@]}"; do
    ext=$(echo "$ext" | xargs)   # trim whitespace
    if [[ -n "$ext" ]]; then
      log "  Installing extension: $ext"
      code-server --user-data-dir "${INSTALL_HOME}/.local/share/code-server" \
        --install-extension "$ext" 2>/dev/null || true
    fi
  done

  # Navigator polyfill patch — prevents PendingMigrationError on Node 22
  EXTENSION_HOST_JS="/usr/lib/code-server/lib/vscode/out/vs/workbench/api/node/extensionHostProcess.js"
  if [[ -f "$EXTENSION_HOST_JS" ]]; then
    sed -i 's/Xy.supportGlobalNavigator||/true||/' "$EXTENSION_HOST_JS"
    log "Navigator polyfill patch applied."
  fi

  # code-server config (no auth — Qovery handles access control)
  mkdir -p "${INSTALL_HOME}/.config/code-server"
  cat > "${INSTALL_HOME}/.config/code-server/config.yaml" <<'CSYAML'
bind-addr: 0.0.0.0:8080
auth: none
cert: false
app-name: Builder Workspace
CSYAML

  # VS Code settings — clean, dark, non-tech-friendly defaults
  mkdir -p "${INSTALL_HOME}/.local/share/code-server/User"
  cat > "${INSTALL_HOME}/.local/share/code-server/User/settings.json" <<'VSCJSON'
{
  "workbench.startupEditor": "none",
  "workbench.tips.enabled": false,
  "workbench.welcomePage.walkthroughs.openOnInstall": false,
  "workbench.colorTheme": "Default Dark Modern",
  "workbench.editor.showTabs": "none",
  "workbench.statusBar.visible": true,
  "editor.fontSize": 14,
  "editor.wordWrap": "on",
  "editor.minimap.enabled": false,
  "terminal.integrated.defaultProfile.linux": "bash",
  "terminal.integrated.profiles.linux": {
    "bash": { "path": "/bin/bash", "args": [] }
  },
  "terminal.integrated.fontSize": 14,
  "extensions.autoUpdate": false,
  "telemetry.telemetryLevel": "off",
  "task.allowAutomaticTasks": "on",
  "livePreview.portNumber": 3100,
  "livePreview.openPreviewTarget": "internalBrowser",
  "remote.autoForwardPorts": true,
  "remote.autoForwardPortsSource": "process",
  "claudeCode.preferredLocation": "sidebar",
  "claudeCode.hideOnboarding": true
}
VSCJSON

  log_done "code-server configured."
else
  log_skip "code-server (disabled)"
fi

# ── 11. AI coding agents ────────────────────────────────────────────────────

if is_enabled "claude_code" || is_enabled "opencode" || is_enabled "codex"; then
  # Need Node.js for npm — verify it's available
  if ! command -v npm &>/dev/null; then
    log_step "Installing AI coding agents..."
    log_warn "npm not found. Skipping AI agent installs (requires Node.js)."
    log_done "AI coding agents skipped (no npm)."
  else
    NPM_PKGS=""

    if is_enabled "claude_code"; then
      CC_VERSION=$(get_version "claude_code" "2.1.129")
      NPM_PKGS="$NPM_PKGS @anthropic-ai/claude-code@${CC_VERSION}"
    fi

    if is_enabled "opencode"; then
      NPM_PKGS="$NPM_PKGS opencode-ai"
    fi

    if is_enabled "codex"; then
      NPM_PKGS="$NPM_PKGS @openai/codex"
    fi

    if [[ -n "$NPM_PKGS" ]]; then
      log_step "Installing AI coding agents:${NPM_PKGS}..."
      # shellcheck disable=SC2086
      log_cmd npm install -g $NPM_PKGS
      log_cmd npm cache clean --force
      log_done "AI coding agents installed."
    fi
  fi
else
  log_skip "AI coding agents (disabled)"
fi

# ── 11b. Cursor CLI ──────────────────────────────────────────────────────────

if is_enabled "cursor"; then
  CURSOR_VERSION=$(get_version "cursor" "2026.06.04-5fd875e")
  CURSOR_DEFAULT_SHA256_amd64="5425aab29f8a01d377de3dc7f7455739ad59829e0879ea0c4296bd9eec520300"
  CURSOR_DEFAULT_SHA256_arm64="f38d2251428bb7576ea2ad4b6f1205830b519af4e76bb03a39f8b8c1b9042a42"
  CURSOR_DEFAULT_SHA_VAR="CURSOR_DEFAULT_SHA256_${ARCH}"
  CURSOR_SHA256=$(resolve_checksum "cursor" "$CURSOR_VERSION" "$ARCH" "2026.06.04-5fd875e" "${!CURSOR_DEFAULT_SHA_VAR}") \
    || exit 1
  log_step "Installing Cursor CLI ${CURSOR_VERSION}..."
  cd /tmp
  curl -fsSL "https://downloads.cursor.com/lab/${CURSOR_VERSION}/linux/${CURSOR_ARCH}/agent-cli-package.tar.gz" -o cursor-cli.tar.gz
  echo "${CURSOR_SHA256}  cursor-cli.tar.gz" | sha256sum -c - || { log_error "Cursor CLI checksum mismatch!"; exit 1; }

  # Extract the self-contained package (contains cursor-agent binary + supporting files)
  mkdir -p /opt/cursor-agent
  tar --strip-components=1 -xzf cursor-cli.tar.gz -C /opt/cursor-agent
  rm -f cursor-cli.tar.gz

  # Create symlinks in /usr/local/bin
  ln -sf /opt/cursor-agent/cursor-agent /usr/local/bin/cursor-agent
  ln -sf /opt/cursor-agent/cursor-agent /usr/local/bin/cursor
  ln -sf /opt/cursor-agent/cursor-agent /usr/local/bin/agent

  log_done "Cursor CLI ${CURSOR_VERSION} installed."
else
  log_skip "Cursor CLI (disabled)"
fi

# ── 12. RTK (Reduces LLM token consumption by 60-90%) ───────────────────────

if is_enabled "rtk"; then
  RTK_VERSION=$(get_version "rtk" "0.42.0")
  # Direct binary download with SHA256 verification instead of piping master/install.sh into sh
  RTK_DEFAULT_SHA256_amd64="cdd4f87ac97ce958f71b53a991880d6adcc41cc5bca1044175a64630980152be"
  RTK_DEFAULT_SHA256_arm64="62bb749df1ed64f09149998c31de864932f047a1be4e0f882a8ceada849e0871"
  RTK_DEFAULT_SHA_VAR="RTK_DEFAULT_SHA256_${ARCH}"
  RTK_SHA256=$(resolve_checksum "rtk" "$RTK_VERSION" "$ARCH" "0.42.0" "${!RTK_DEFAULT_SHA_VAR}") \
    || exit 1
  # amd64 uses musl static binary; arm64 uses gnu
  if [[ "$ARCH" == "amd64" ]]; then
    RTK_ARTIFACT="rtk-x86_64-unknown-linux-musl.tar.gz"
  else
    RTK_ARTIFACT="rtk-aarch64-unknown-linux-gnu.tar.gz"
  fi
  log_step "Installing RTK ${RTK_VERSION}..."
  cd /tmp
  curl -fsSL "https://github.com/rtk-ai/rtk/releases/download/v${RTK_VERSION}/${RTK_ARTIFACT}" -o rtk.tar.gz
  echo "${RTK_SHA256}  rtk.tar.gz" | sha256sum -c - || { log_error "RTK checksum mismatch!"; exit 1; }
  tar -xzf rtk.tar.gz
  install -m 755 rtk /usr/local/bin/rtk
  rm -rf rtk rtk.tar.gz
  log_done "RTK ${RTK_VERSION} installed."
else
  log_skip "RTK (disabled)"
fi

# ── 12b. Proxy launcher (synchronous proxy start for AAG) ────────────────────

log "Installing proxy launcher script..."
cat > /usr/local/bin/rde-start-proxy.sh << 'PROXY_LAUNCHER_EOF'
#!/bin/bash
# RDE Agent Governance Proxy — Synchronous Launcher
# Sourced by entrypoint.sh before any network operations.
# Decodes the pre-staged proxy script from env vars, starts it,
# waits for readiness, then exports HTTP_PROXY so subsequent
# commands (git clone, npm install, etc.) route through the proxy.
#
# If the proxy fails to start, this script does NOT set HTTP_PROXY
# and logs a warning — startup proceeds without proxy (graceful degradation).

rde_start_proxy() {
  # Configurable data directory — defaults to /opt/rde (pre-created by install.sh
  # with workspace user ownership). Override with RDE_DATA_DIR if /opt/rde is not
  # writable (e.g., custom base images that don't use install.sh).
  local rde_dir="${RDE_DATA_DIR:-/opt/rde}"

  # Backward compatibility: if HTTP_PROXY is set (from an older deployment)
  # pointing at localhost:8877 but the proxy isn't running, unset it so
  # startup operations don't fail against a dead proxy.
  if [[ "${HTTP_PROXY:-}" == *"localhost:8877"* ]]; then
    if command -v node &>/dev/null; then
      if ! node -e "const n=require('net');const s=n.createConnection(8877,'127.0.0.1');s.once('connect',()=>{process.exit(0)});s.once('error',()=>process.exit(1));setTimeout(()=>process.exit(1),1000)" 2>/dev/null; then
        echo "[rde-proxy] HTTP_PROXY is set but proxy not running — unsetting for startup"
        unset HTTP_PROXY HTTPS_PROXY NO_PROXY NODE_EXTRA_CA_CERTS SSL_CERT_FILE REQUESTS_CA_BUNDLE
      fi
    fi
  fi

  # Skip if AAG is not enabled
  if [[ "${RDE_AAG_MODE:-off}" == "off" ]]; then
    return 0
  fi

  # Skip if no pre-staged proxy script (older image or AAG not configured)
  if [[ -z "${RDE_PROXY_SCRIPT_GZ_B64:-}" ]]; then
    return 0
  fi

  # Skip if node is not available
  if ! command -v node &>/dev/null; then
    echo "[rde-proxy] node not found, skipping proxy start"
    return 0
  fi

  # Skip if proxy is already running (container restart)
  if node -e "const n=require('net');const s=n.createConnection(8877,'127.0.0.1');s.once('connect',()=>{process.exit(0)});s.once('error',()=>process.exit(1));setTimeout(()=>process.exit(1),1000)" 2>/dev/null; then
    echo "[rde-proxy] Proxy already running on port 8877"
    export HTTP_PROXY=http://localhost:8877
    export HTTPS_PROXY=http://localhost:8877
    export NO_PROXY=localhost,127.0.0.1,::1
    if [[ -f "${rde_dir}/ca.crt" ]]; then
      export NODE_EXTRA_CA_CERTS="${rde_dir}/ca.crt"
      export SSL_CERT_FILE="${rde_dir}/ca.crt"
      export REQUESTS_CA_BUNDLE="${rde_dir}/ca.crt"
    fi
    return 0
  fi

  echo "[rde-proxy] Starting agent governance proxy synchronously..."

  # Ensure the data directory exists and is writable. /opt/rde is pre-created
  # by install.sh with workspace user ownership. If it's not writable (custom
  # image), fall back to $HOME/.rde.
  mkdir -p "$rde_dir" 2>/dev/null
  if ! touch "${rde_dir}/.rde-write-test" 2>/dev/null; then
    echo "[rde-proxy] ${rde_dir} is not writable, falling back to \$HOME/.rde"
    rde_dir="${HOME:-.}/.rde"
    mkdir -p "$rde_dir"
  else
    rm -f "${rde_dir}/.rde-write-test"
  fi

  # Decode and write the proxy script
  if ! echo "$RDE_PROXY_SCRIPT_GZ_B64" | base64 -d | gunzip > "${rde_dir}/agent-proxy.js" 2>/dev/null; then
    echo "[rde-proxy] Failed to decode proxy script, skipping"
    return 0
  fi

  # Decode and write CA cert + key
  if [[ -n "${RDE_CA_CERT_B64:-}" ]]; then
    echo "$RDE_CA_CERT_B64" | base64 -d > "${rde_dir}/ca.crt" 2>/dev/null || true
  fi
  if [[ -n "${RDE_CA_KEY_B64:-}" ]]; then
    echo "$RDE_CA_KEY_B64" | base64 -d > "${rde_dir}/ca.key" 2>/dev/null && chmod 600 "${rde_dir}/ca.key" || true
  fi

  # Start the proxy in background
  nohup node "${rde_dir}/agent-proxy.js" > /tmp/rde-agent-proxy.log 2>&1 &
  local proxy_pid=$!

  # Wait for the proxy to be ready (up to 10 seconds)
  local start_time=$SECONDS
  for i in $(seq 1 20); do
    if node -e "const n=require('net');const s=n.createConnection(8877,'127.0.0.1');s.once('connect',()=>{process.exit(0)});s.once('error',()=>process.exit(1));setTimeout(()=>process.exit(1),500)" 2>/dev/null; then
      local elapsed=$(( SECONDS - start_time ))
      echo "[rde-proxy] Proxy ready on port 8877 (${elapsed}s) [data: ${rde_dir}]"
      export HTTP_PROXY=http://localhost:8877
      export HTTPS_PROXY=http://localhost:8877
      export NO_PROXY=localhost,127.0.0.1,::1
      if [[ -f "${rde_dir}/ca.crt" ]]; then
        export NODE_EXTRA_CA_CERTS="${rde_dir}/ca.crt"
        export SSL_CERT_FILE="${rde_dir}/ca.crt"
        export REQUESTS_CA_BUNDLE="${rde_dir}/ca.crt"
      fi

      # Write profile.d script so interactive shells also get the proxy vars
      mkdir -p /etc/profile.d 2>/dev/null || true
      cat > /etc/profile.d/rde-proxy.sh 2>/dev/null << PROFILED || true
#!/bin/sh
# Set by RDE proxy launcher — proxy is running on localhost:8877
export HTTP_PROXY=http://localhost:8877
export HTTPS_PROXY=http://localhost:8877
export NO_PROXY=localhost,127.0.0.1,::1
if [ -f "${rde_dir}/ca.crt" ]; then
  export NODE_EXTRA_CA_CERTS="${rde_dir}/ca.crt"
  export SSL_CERT_FILE="${rde_dir}/ca.crt"
  export REQUESTS_CA_BUNDLE="${rde_dir}/ca.crt"
fi
PROFILED
      chmod +x /etc/profile.d/rde-proxy.sh 2>/dev/null || true
      return 0
    fi
    sleep 0.5
  done

  # Proxy failed to start — check if process is still alive
  if ! kill -0 "$proxy_pid" 2>/dev/null; then
    echo "[rde-proxy] Proxy process exited. Log:"
    tail -5 /tmp/rde-agent-proxy.log 2>/dev/null || true
  else
    echo "[rde-proxy] Proxy process running but port 8877 not reachable after 10s"
  fi

  echo "[rde-proxy] Continuing without proxy (graceful degradation)"
  return 0
}

# Run the launcher — errors never propagate to the sourcing script
rde_start_proxy || true
PROXY_LAUNCHER_EOF
chmod +x /usr/local/bin/rde-start-proxy.sh
log "Proxy launcher installed."

# ── 13. Entrypoint + resources ───────────────────────────────────────────────

if is_enabled "entrypoint"; then
  log_step "Downloading entrypoint.sh..."
  curl -fsSL --noproxy '*' "${REMOTE_BASE}/entrypoint.sh" -o /usr/local/bin/entrypoint.sh
  chmod +x /usr/local/bin/entrypoint.sh
  log_done "entrypoint.sh installed."
else
  log_skip "Entrypoint (disabled)"
fi

if is_enabled "resources"; then
  log_step "Downloading resources (CLAUDE.md, SKILL.md, WELCOME.md, welcome.html)..."
  mkdir -p /opt/resources
  for f in CLAUDE.md SKILL.md WELCOME.md welcome.html; do
    curl -fsSL --noproxy '*' "${REMOTE_BASE}/resources/${f}" -o "/opt/resources/${f}" 2>/dev/null || true
  done
  log_done "Resources downloaded."
else
  log_skip "Resources (disabled)"
fi

# ── 14. Builder startup extension ────────────────────────────────────────────

if is_enabled "builder_startup_extension" && is_enabled "code_server"; then
  log_step "Installing Builder Startup extension..."
  EXT_DIR="${INSTALL_HOME}/.local/share/code-server/extensions/qovery.builder-startup-0.0.1"
  mkdir -p "$EXT_DIR"

  for f in package.json extension.js; do
    curl -fsSL --noproxy '*' "${REMOTE_BASE}/builder-startup-extension/${f}" -o "${EXT_DIR}/${f}" 2>/dev/null || true
  done

  # Register extension in extensions.json
  EXTENSIONS_JSON="${INSTALL_HOME}/.local/share/code-server/extensions/extensions.json"
  if [[ -f "$EXTENSIONS_JSON" ]]; then
    jq --arg ext_path "$EXT_DIR" '. += [{"identifier":{"id":"qovery.builder-startup"},"version":"0.0.1","location":{"$mid":1,"path":$ext_path,"scheme":"file"},"relativeLocation":"qovery.builder-startup-0.0.1"}]' \
      "$EXTENSIONS_JSON" > /tmp/ext.json
    mv /tmp/ext.json "$EXTENSIONS_JSON"
  else
    cat > "$EXTENSIONS_JSON" <<EXTJSON
[{"identifier":{"id":"qovery.builder-startup"},"version":"0.0.1","location":{"\$mid":1,"path":"${EXT_DIR}","scheme":"file"},"relativeLocation":"qovery.builder-startup-0.0.1"}]
EXTJSON
  fi

  log_done "Builder Startup extension installed."
else
  log_skip "Builder Startup extension (disabled)"
fi

# ── 15. Qovery Skills ───────────────────────────────────────────────────────

if is_enabled "qovery_skills"; then
  log_step "Installing Qovery Skills (as $INSTALL_USER)..."
  # Pre-create skill directories
  mkdir -p "${INSTALL_HOME}/.config/opencode/skills" \
           "${INSTALL_HOME}/.config/opencode/commands" \
           "${INSTALL_HOME}/.claude/skills"

  chown -R "${INSTALL_USER}:${INSTALL_USER}" "$INSTALL_HOME"
  su - "$INSTALL_USER" -c 'curl -fsSL https://skill.qovery.com/install.sh | bash' 2>&1 | while IFS= read -r line; do
    printf '[rde-setup]   | %s\n' "$line"
  done || true
  log_done "Qovery Skills installed."
else
  log_skip "Qovery Skills (disabled)"
fi

# ── 16. RTK hooks (Claude Code + OpenCode integration) ───────────────────────

if is_enabled "rtk" && command -v rtk &>/dev/null; then
  log "Initializing RTK hooks..."
  chown -R "${INSTALL_USER}:${INSTALL_USER}" "$INSTALL_HOME"
  su - "$INSTALL_USER" -c 'rtk init -g 2>/dev/null || true'
  su - "$INSTALL_USER" -c 'rtk init -g --opencode 2>/dev/null || true'
  log "RTK hooks initialized."
fi

# ── Final ownership fix ─────────────────────────────────────────────────────

log "Fixing ${INSTALL_HOME} ownership..."
chown -R "${INSTALL_USER}:${INSTALL_USER}" "$INSTALL_HOME"

# ── Persist user config for entrypoint.sh ────────────────────────────────────

mkdir -p /etc/rde
cat > /etc/rde/env <<RDEEOF
RDE_USER="${INSTALL_USER}"
RDE_HOME="${INSTALL_HOME}"
RDEEOF

# ── Proxy data directory ─────────────────────────────────────────────────────
# Pre-create /opt/rde for the agent governance proxy. The proxy launcher
# (rde-start-proxy.sh) and the BFF bootstrap both write files here.
# Must be writable by the workspace user since the entrypoint re-execs as
# that user before the proxy launcher runs.

mkdir -p /opt/rde
chown "${INSTALL_USER}:${INSTALL_USER}" /opt/rde

# ── Working directory ────────────────────────────────────────────────────────

mkdir -p "${INSTALL_HOME}/project"
chown "${INSTALL_USER}:${INSTALL_USER}" "${INSTALL_HOME}/project"

# ── Summary ──────────────────────────────────────────────────────────────────

log "=============================================="
log " Qovery RDE setup complete! ($(( SECONDS - BUILD_START ))s)"
log "=============================================="
log ""
log "Next steps for your Dockerfile:"
log ""
log "  1. Set the entrypoint:"
log "     ENTRYPOINT [\"/usr/local/bin/entrypoint.sh\"]"
log ""
log "  2. Set the working directory:"
log "     WORKDIR ${INSTALL_HOME}/project"
log ""
log "  3. Expose the required ports:"
log "     EXPOSE 8080 9100"
log ""
log "  4. Set environment variables (at runtime):"
log "     - ANTHROPIC_API_KEY  (for Claude Code)"
log "     - OPENAI_API_KEY     (for Codex)"
log "     - GIT_REPO_URL       (auto-clone on startup)"
log "     - GIT_TOKEN           (for private repos)"
log ""
log "  Full example:"
log "     FROM ubuntu:24.04"
log "     RUN curl -fsSL https://rde.qovery.com/install.sh | bash"
log "     WORKDIR ${INSTALL_HOME}/project"
log "     EXPOSE 8080 9100"
log "     ENTRYPOINT [\"/usr/local/bin/entrypoint.sh\"]"
log ""
