#!/usr/bin/env bash
# Provisions a fresh macOS (Apple Silicon) machine with:
#   - Xcode Command Line Tools
#   - Homebrew
#   - Tailscale (authenticated and confirmed up — done early so everything after can assume it)
#   - Node 22 + OpenClaw (gateway bound to Tailscale, all keys mirrored from reference machine)
#   - macOS Application Firewall + pf rules that block all non-Tailscale incoming
#
# Usage: ./setup-machine.sh
#   You will be prompted for the decryption password for the embedded credentials.

set -euo pipefail

# ── Encrypted credentials ─────────────────────────────────────────────────────
# Each value is AES-256-CBC + PBKDF2 ciphertext (base64). Decrypted at runtime
# with the password prompted below. To rotate keys or re-encrypt with a new
# password, see the helper block at the bottom of this file.

CRED_TAILSCALE="U2FsdGVkX18PprOIG13sdvziQzHWqEc0GNGOLmK5MACuXlZEnsMDbGZXDimXfMiH2Qz1vMeKhtVEIarvolux0Jpmne2ghYO49JOBAEnfHeQ="
CRED_ANTHROPIC="U2FsdGVkX18/X1DD5WnvVWfOL8ZHDrjAZhnMqSSVZwaflRsUuma2/nVjMgqiOCQdpKIIgf+uiuHxpsm0eRovTsSQqB5hFJ/e8J6MospAwannopi7eMCoF0fgzhhliabkMekwN6byWTY7jSyGFxTV/Jd5QPIPnC0Gv/jzc+/qRP0="
CRED_BRAVE="U2FsdGVkX1//ZX9fUsemffaA2YhRqvK4THfps47beywN0ujW12hbpMhpz1zDOk9Z"
CRED_GOOGLE_PLACES="U2FsdGVkX19bg4OqRi5Fuh5OFLTofl/s7uiWmW+BrhcStqLvSoh2oXjmZMFOufyXEd/pxr+2GMdhpP5N/tGJ8A=="
CRED_OPENAI="U2FsdGVkX193Z6KQZ4UbGe1uGL/gPJDAgdBnMswUWLOxOm3QhjLFQt9t8F8B9/0keoykm47OYXxn0YjfPk9GeER85b5ffxV8HXlpT+5xYmL49c5DuKQIfuSa4lNrxVwhoDod1WEjWaCU5v0rgfeVuVd0k88w1345UT0zVFR+6XTPf+IlPVoU78AeUka7FTVHrINb45IgrmS/ocdov7xCOvjeyPIPxhrZDb0tgMnkfdPPzfoA+Q9qFpMuoktgEdd0"
CRED_ELEVENLABS="U2FsdGVkX199ADqg7uieMPRNx0Yl8ZM1a4l/bGa1INlk/uYE3Abq0jeewxS+9Rm++eIoKEB48mb0FYLf3LtSqXaTG/4s+HnW1tWRj4F7N3Y="
CRED_GATEWAY_TOKEN="U2FsdGVkX1+EsbRgqT/tvXFAh0w9xi2IOdtDCKqtFVsP5pMIncK6OB7pdIFPBTBWxsl4ru+2RrJ1FfXROdb10iyNQ3LrA3xdqHHDUhx1ChU="

read -rsp "Decryption password: " OPENCLAW_DECRYPT_PASS
echo
if [[ -z "$OPENCLAW_DECRYPT_PASS" ]]; then
  echo "Error: password required."
  exit 1
fi
export OPENCLAW_DECRYPT_PASS

decrypt() {
  echo "$1" | openssl enc -aes-256-cbc -pbkdf2 -a -d -pass env:OPENCLAW_DECRYPT_PASS 2>/dev/null || true
}

# Probe with one credential first so a wrong password fails before any side effects.
TAILSCALE_AUTH_KEY=$(decrypt "$CRED_TAILSCALE")
if [[ -z "$TAILSCALE_AUTH_KEY" ]]; then
  unset OPENCLAW_DECRYPT_PASS
  echo "Error: decryption failed — wrong password."
  exit 1
fi

ANTHROPIC_API_KEY=$(decrypt "$CRED_ANTHROPIC")
BRAVE_API_KEY=$(decrypt "$CRED_BRAVE")
GOOGLE_PLACES_API_KEY=$(decrypt "$CRED_GOOGLE_PLACES")
OPENAI_API_KEY=$(decrypt "$CRED_OPENAI")
ELEVENLABS_API_KEY=$(decrypt "$CRED_ELEVENLABS")
GATEWAY_TOKEN=$(decrypt "$CRED_GATEWAY_TOKEN")

unset OPENCLAW_DECRYPT_PASS

if [[ -z "$ANTHROPIC_API_KEY" || -z "$BRAVE_API_KEY" || -z "$GOOGLE_PLACES_API_KEY" \
   || -z "$OPENAI_API_KEY" || -z "$ELEVENLABS_API_KEY" || -z "$GATEWAY_TOKEN" ]]; then
  echo "Error: a credential decrypted to empty — bundle may be corrupt."
  exit 1
fi

OPENCLAW_DIR="$HOME/.openclaw"
OPENCLAW_CONFIG="$OPENCLAW_DIR/openclaw.json"
OPENCLAW_PLIST="$HOME/Library/LaunchAgents/ai.openclaw.gateway.plist"
LOCAL_BIN="$HOME/.local/bin"
NODE_BIN="/opt/homebrew/opt/node@22/bin"
NPM="$NODE_BIN/npm"

step() { echo; echo "══ $* ══"; }

# ── Phase 1: Xcode Command Line Tools ─────────────────────────────────────────
# Non-interactive install: drop the sentinel file softwareupdate looks for, then
# resolve and install the latest "Command Line Tools" label. Avoids the GUI
# prompt that `xcode-select --install` triggers.
step "Xcode Command Line Tools"
if xcode-select -p &>/dev/null; then
  echo "✓ Already installed: $(xcode-select -p)"
else
  echo "→ Installing Xcode CLT non-interactively via softwareupdate..."
  CLT_SENTINEL=/tmp/.com.apple.dt.CommandLineTools.installondemand.in-progress
  sudo touch "$CLT_SENTINEL"
  CLT_PKG=$(softwareupdate -l 2>&1 \
    | grep -E "\*.*Command Line" \
    | sed -E 's/^.*Label: //' \
    | sort -V \
    | tail -1 || true)
  if [[ -z "$CLT_PKG" ]]; then
    sudo rm -f "$CLT_SENTINEL"
    echo "ERROR: no Command Line Tools package found in softwareupdate output."
    exit 1
  fi
  echo "  Selected: $CLT_PKG"
  sudo softwareupdate -i "$CLT_PKG" --verbose
  sudo rm -f "$CLT_SENTINEL"
  if ! xcode-select -p &>/dev/null; then
    echo "ERROR: CLT install reported success but xcode-select is still unset."
    exit 1
  fi
  echo "✓ Xcode CLT installed."
fi

# ── Phase 2: Homebrew ──────────────────────────────────────────────────────────
step "Homebrew"
if command -v brew &>/dev/null; then
  echo "✓ Homebrew already installed: $(brew --version | head -1)"
else
  echo "→ Installing Homebrew..."
  /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
  echo "✓ Homebrew installed."
fi
eval "$(/opt/homebrew/bin/brew shellenv)"

# ── Phase 2b: Oh My Zsh ────────────────────────────────────────────────────────
# Installed before any phase that writes ~/.zshrc, since the OMZ installer
# replaces zshrc on a fresh machine. RUNZSH=no/CHSH=no keeps the install
# unattended and prevents the shell-switch prompt at the end.
step "Oh My Zsh"
if [[ -d "$HOME/.oh-my-zsh" ]]; then
  echo "✓ Oh My Zsh already installed."
else
  echo "→ Installing Oh My Zsh (unattended)..."
  RUNZSH=no CHSH=no sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
  echo "✓ Oh My Zsh installed."
fi

# ── Phase 3: Tailscale — install, start, and confirm connected ────────────────
# Done before OpenClaw so the gateway can bind to the Tailscale interface on first launch.
step "Tailscale"
if command -v tailscale &>/dev/null; then
  echo "✓ Tailscale already installed: $(tailscale version 2>/dev/null | head -1)"
else
  echo "→ Installing Tailscale..."
  brew install tailscale
  echo "✓ Tailscale installed."
fi

if sudo brew services list 2>/dev/null | grep -q "^tailscale.*started"; then
  echo "✓ tailscaled already running."
else
  echo "→ Starting tailscaled (requires sudo)..."
  sudo brew services start tailscale
  sleep 3
fi

TS_IP=$(tailscale ip -4 2>/dev/null || true)
if [[ -n "$TS_IP" ]]; then
  echo "✓ Already authenticated to tailnet — skipping tailscale up. IP: $TS_IP"
else
  echo "→ Connecting to tailnet..."
  sudo tailscale up --auth-key="$TAILSCALE_AUTH_KEY" --accept-routes --accept-dns=true
  echo "✓ Tailscale up."

  echo "→ Waiting for Tailscale IP..."
  for i in $(seq 1 20); do
    TS_IP=$(tailscale ip -4 2>/dev/null || true)
    if [[ -n "$TS_IP" ]]; then break; fi
    sleep 2
  done
  if [[ -z "$TS_IP" ]]; then
    echo "ERROR: Tailscale did not get an IP after 40s — aborting to avoid misconfigured OpenClaw and firewall lockout."
    tailscale status
    exit 1
  fi
  echo "✓ Tailscale IP: $TS_IP"
fi
tailscale status

# ── Phase 4: Node 22 ───────────────────────────────────────────────────────────
step "Node 22"
if brew list node@22 &>/dev/null; then
  echo "✓ node@22 already installed."
else
  echo "→ Installing node@22..."
  brew install node@22
  echo "✓ node@22 installed."
fi
export PATH="$NODE_BIN:$PATH"
echo "  node $(node --version)  npm $(npm --version)"

# ── Phase 5: OpenClaw ──────────────────────────────────────────────────────────
step "OpenClaw"
if [[ -d "/opt/homebrew/lib/node_modules/openclaw" ]]; then
  echo "✓ OpenClaw already installed."
else
  echo "→ Installing openclaw globally..."
  "$NPM" install -g openclaw
  echo "✓ OpenClaw installed."
fi

mkdir -p "$LOCAL_BIN"
if [[ ! -e "$LOCAL_BIN/openclaw" ]]; then
  ln -sf "$NODE_BIN/openclaw" "$LOCAL_BIN/openclaw"
  echo "✓ Symlinked openclaw → $LOCAL_BIN/openclaw"
fi

# ── Phase 5b: Claude Code ─────────────────────────────────────────────────────
step "Claude Code"
if [[ -d "/opt/homebrew/lib/node_modules/@anthropic-ai/claude-code" ]]; then
  echo "✓ Claude Code already installed."
else
  echo "→ Installing @anthropic-ai/claude-code globally..."
  "$NPM" install -g @anthropic-ai/claude-code
  echo "✓ Claude Code installed."
fi

# Persist ANTHROPIC_API_KEY in the user's interactive shell so `claude` picks it
# up by default. Marker comment makes this idempotent across re-runs.
ZSHRC="$HOME/.zshrc"
touch "$ZSHRC"
if ! grep -q "OPENCLAW_PROVISIONED_ANTHROPIC_KEY" "$ZSHRC"; then
  cat >> "$ZSHRC" <<ZRCEOF

# OPENCLAW_PROVISIONED_ANTHROPIC_KEY (managed by setup-openclaw-machine.sh)
export ANTHROPIC_API_KEY="${ANTHROPIC_API_KEY}"
ZRCEOF
  echo "✓ ANTHROPIC_API_KEY exported in ~/.zshrc"
else
  echo "✓ ANTHROPIC_API_KEY already present in ~/.zshrc (leaving as-is)"
fi

# ── Phase 6: OpenClaw config ───────────────────────────────────────────────────
step "OpenClaw config"
mkdir -p "$OPENCLAW_DIR/logs" "$OPENCLAW_DIR/completions" "$OPENCLAW_DIR/workspace"

if [[ -f "$OPENCLAW_CONFIG" ]]; then
  cp "$OPENCLAW_CONFIG" "$OPENCLAW_CONFIG.bak.$(date +%s)"
  echo "  (backed up existing config)"
fi

cat > "$OPENCLAW_CONFIG" <<JSONEOF
{
  "agents": {
    "defaults": {
      "workspace": "${OPENCLAW_DIR}/workspace",
      "models": {
        "anthropic/claude-opus-4-7": {}
      },
      "model": {
        "primary": "anthropic/claude-opus-4-7"
      }
    }
  },
  "gateway": {
    "mode": "local",
    "auth": {
      "mode": "token",
      "token": "${GATEWAY_TOKEN}"
    },
    "port": 18789,
    "bind": "loopback",
    "tailscale": {
      "mode": "serve",
      "resetOnExit": false
    },
    "controlUi": {
      "allowInsecureAuth": true
    },
    "nodes": {
      "denyCommands": [
        "camera.snap",
        "camera.clip",
        "screen.record",
        "contacts.add",
        "calendar.add",
        "reminders.add",
        "sms.send",
        "sms.search"
      ]
    }
  },
  "session": {
    "dmScope": "per-channel-peer"
  },
  "tools": {
    "profile": "coding",
    "web": {
      "search": {
        "provider": "brave",
        "enabled": true
      }
    }
  },
  "auth": {
    "profiles": {
      "anthropic:default": {
        "provider": "anthropic",
        "mode": "api_key"
      }
    }
  },
  "channels": {
    "whatsapp": {
      "selfChatMode": true,
      "dmPolicy": "allowlist",
      "allowFrom": [
        "+13128107854"
      ],
      "enabled": true
    }
  },
  "plugins": {
    "entries": {
      "brave": {
        "enabled": true,
        "config": {
          "webSearch": {
            "apiKey": "${BRAVE_API_KEY}"
          }
        }
      },
      "anthropic": {
        "enabled": true
      },
      "bonjour": {
        "enabled": false
      }
    }
  },
  "skills": {
    "install": {
      "nodeManager": "npm"
    },
    "entries": {
      "goplaces": {
        "apiKey": "${GOOGLE_PLACES_API_KEY}"
      },
      "openai-whisper-api": {
        "apiKey": "${OPENAI_API_KEY}"
      },
      "sag": {
        "apiKey": "${ELEVENLABS_API_KEY}"
      }
    }
  },
  "hooks": {
    "internal": {
      "enabled": true,
      "entries": {
        "session-memory": {
          "enabled": true
        },
        "command-logger": {
          "enabled": true
        },
        "bootstrap-extra-files": {
          "enabled": true
        },
        "boot-md": {
          "enabled": true
        }
      }
    }
  }
}
JSONEOF

echo "✓ OpenClaw config written (gateway bound to tailscale, tailscale mode on)."

# ── Phase 7: OpenClaw LaunchAgent ─────────────────────────────────────────────
step "OpenClaw LaunchAgent"
TMPDIR_VAL=$(getconf DARWIN_USER_TEMP_DIR 2>/dev/null || echo "/tmp/")

mkdir -p "$HOME/Library/LaunchAgents"
cat > "$OPENCLAW_PLIST" <<PLISTEOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>ai.openclaw.gateway</string>
    <key>Comment</key>
    <string>OpenClaw Gateway</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>ThrottleInterval</key>
    <integer>1</integer>
    <key>Umask</key>
    <integer>63</integer>
    <key>ProgramArguments</key>
    <array>
      <string>/opt/homebrew/opt/node@22/bin/node</string>
      <string>/opt/homebrew/lib/node_modules/openclaw/dist/index.js</string>
      <string>gateway</string>
      <string>--port</string>
      <string>18789</string>
    </array>
    <key>StandardOutPath</key>
    <string>${OPENCLAW_DIR}/logs/gateway.log</string>
    <key>StandardErrorPath</key>
    <string>${OPENCLAW_DIR}/logs/gateway.err.log</string>
    <key>EnvironmentVariables</key>
    <dict>
      <key>HOME</key>
      <string>${HOME}</string>
      <key>TMPDIR</key>
      <string>${TMPDIR_VAL}</string>
      <key>NODE_EXTRA_CA_CERTS</key>
      <string>/etc/ssl/cert.pem</string>
      <key>NODE_USE_SYSTEM_CA</key>
      <string>1</string>
      <key>PATH</key>
      <string>/opt/homebrew/opt/node@22/bin:${LOCAL_BIN}:${HOME}/.npm-global/bin:${HOME}/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
      <key>ANTHROPIC_API_KEY</key>
      <string>${ANTHROPIC_API_KEY}</string>
      <key>OPENCLAW_GATEWAY_PORT</key>
      <string>18789</string>
      <key>OPENCLAW_LAUNCHD_LABEL</key>
      <string>ai.openclaw.gateway</string>
      <key>OPENCLAW_SERVICE_MARKER</key>
      <string>openclaw</string>
      <key>OPENCLAW_SERVICE_KIND</key>
      <string>gateway</string>
    </dict>
  </dict>
</plist>
PLISTEOF

launchctl unload "$OPENCLAW_PLIST" 2>/dev/null || true
launchctl load "$OPENCLAW_PLIST"
echo "✓ OpenClaw gateway LaunchAgent loaded."

# ── Phase 7b: Remote access (SSH + Screen Sharing) ────────────────────────────
# Enables Remote Login (sshd) and Screen Sharing (VNC). Both will be reachable
# only over Tailscale because the firewall phase below blocks all non-CGNAT
# inbound. systemsetup -setremotelogin prompts for yes/no, so feed it via `yes`.
step "Remote access"

if sudo systemsetup -getremotelogin 2>/dev/null | grep -q "On"; then
  echo "✓ Remote Login (SSH) already enabled."
else
  echo "→ Enabling Remote Login (SSH)..."
  yes | sudo systemsetup -setremotelogin on >/dev/null
  echo "✓ Remote Login enabled."
fi

SCREEN_SHARING_PLIST="/System/Library/LaunchDaemons/com.apple.screensharing.plist"
if sudo launchctl print system/com.apple.screensharing &>/dev/null; then
  echo "✓ Screen Sharing already enabled."
else
  echo "→ Enabling Screen Sharing..."
  sudo launchctl load -w "$SCREEN_SHARING_PLIST"
  echo "✓ Screen Sharing enabled."
fi

# ── Phase 8: Firewall ──────────────────────────────────────────────────────────
step "Firewall"

# 8a. macOS Application Firewall
echo "→ Enabling Application Firewall..."
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setstealthmode on
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setallowsigned on
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setallowsignedapp on
echo "✓ Application Firewall enabled (stealth mode on)."

# 8b. pf — block all incoming except loopback and Tailscale CGNAT (100.64.0.0/10)
echo "→ Configuring pf rules..."

sudo tee /etc/pf.anchors/tailscale-only > /dev/null <<'PFEOF'
# Allow loopback
pass quick on lo0 all

# Allow all traffic to/from Tailscale's CGNAT subnet (100.64.0.0/10)
pass quick inet from 100.64.0.0/10 to any
pass quick inet from any to 100.64.0.0/10

# Allow outbound connections and their established return packets
pass out quick keep state

# Block all other unsolicited incoming
block in quick all
PFEOF

if ! grep -q "tailscale-only" /etc/pf.conf; then
  sudo tee -a /etc/pf.conf > /dev/null <<'PFCONFEOF'

# Tailscale-only incoming firewall (added by setup-machine.sh)
anchor "tailscale-only"
load anchor "tailscale-only" from "/etc/pf.anchors/tailscale-only"
PFCONFEOF
  echo "  Added tailscale-only anchor to /etc/pf.conf"
fi

# LaunchDaemon to reload pf rules at every boot
sudo tee /Library/LaunchDaemons/com.tailscale.pf.plist > /dev/null <<'DAEMONEOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.tailscale.pf</string>
  <key>RunAtLoad</key>
  <true/>
  <key>ProgramArguments</key>
  <array>
    <string>/sbin/pfctl</string>
    <string>-ef</string>
    <string>/etc/pf.conf</string>
  </array>
</dict>
</plist>
DAEMONEOF

sudo launchctl unload /Library/LaunchDaemons/com.tailscale.pf.plist 2>/dev/null || true
sudo launchctl load /Library/LaunchDaemons/com.tailscale.pf.plist
sudo pfctl -ef /etc/pf.conf
echo "✓ pf firewall active — all non-Tailscale incoming connections blocked."

# ── Done ───────────────────────────────────────────────────────────────────────
echo
echo "╔══════════════════════════════════════════════════════════╗"
echo "║                   Setup complete                         ║"
echo "╠══════════════════════════════════════════════════════════╣"
printf  "║  Tailscale IP  : %-39s ║\n" "$TS_IP"
printf  "║  OpenClaw port : %-39s ║\n" "18789  (bound to Tailscale)"
printf  "║  Firewall      : %-39s ║\n" "pf + Application Firewall (stealth)"
printf  "║  Anthropic key : %-39s ║\n" "loaded from encrypted bundle"
printf  "║  Claude Code   : %-39s ║\n" "installed (uses ANTHROPIC_API_KEY)"
echo "╚══════════════════════════════════════════════════════════╝"
echo
echo "→ Handing off to a fresh zsh login shell so ANTHROPIC_API_KEY and Oh My Zsh"
echo "  are loaded. (Other terminals you already have open won't see the new env"
echo "  var until you source ~/.zshrc in them.)"
exec zsh -l

# ── Re-encrypting credentials ─────────────────────────────────────────────────
# To rotate a key or change the password, re-run something like:
#
#   read -rsp "New password: " NEW_PASS && export NEW_PASS && echo
#   printf '%s' 'sk-ant-...' | openssl enc -aes-256-cbc -pbkdf2 -a -salt -pass env:NEW_PASS
#
# Paste the resulting U2FsdGVk... blob into the corresponding CRED_* line at
# the top of this file. All credentials must use the same password.
