#!/bin/sh
# Cybee installer — one command, no account, no card.
#
#   curl -sSf https://get.cybee.dev | sh                       # new org (auto-named; rename later)
#   curl -sSf https://get.cybee.dev | sh -s -- --name "Acme"   # new org with a name
#   curl -sSf https://get.cybee.dev | sh -s -- --join <TOKEN>   # join an existing org (teammate)
#   curl -sSf https://get.cybee.dev | sh -s -- --admin <TOKEN>  # join AND manage the fleet (admin)
#   ... --join <TOKEN> --owner "Hekuran Doli"                   # label whose Mac this is
#
# With no --join, this creates a fresh organization for you and prints your admin token — save
# it; it is how you manage the fleet from your AI assistant. Read this script: https://get.cybee.dev
set -eu

BACKEND="https://api.cybee.dev"
CONTENT="https://content.cybee.dev"
MCP="https://mcp.cybee.dev/mcp"
DOCS_CONNECT="https://docs.cybee.dev/connect"  # TODO: real docs page
FORCE=0
JOIN=""
NAME=""
ORG_ID=""
OWNER=""
OWNER_EXPLICIT=0
ADMIN_TOKEN=""
JOIN_IS_ADMIN=0

while [ $# -gt 0 ]; do
  case "$1" in
    --join) JOIN="$2"; shift 2 ;;
    # An admin invitation: one token that both enrolls this Mac in the org and makes it an
    # admin console (assistants wired, token saved). The backend accepts an admin token for
    # enrollment because admin strictly outranks join.
    --admin) ADMIN_TOKEN="$2"; JOIN="$2"; JOIN_IS_ADMIN=1; shift 2 ;;
    --name) NAME="$2"; shift 2 ;;
    --owner) OWNER="$2"; OWNER_EXPLICIT=1; shift 2 ;;
    --backend) BACKEND="$2"; shift 2 ;;
    --force) FORCE=1; shift ;;
    *) echo "unknown option: $1" >&2; exit 2 ;;
  esac
done

if [ "$(uname -s)" != "Darwin" ]; then
  echo "cybee: macOS only for now (Windows/Linux coming)." >&2; exit 1
fi
# Check the Mac before anything touches the backend, so an unsupported Mac never creates an org.
ARCH="$(uname -m)"
if [ "$ARCH" != "arm64" ]; then
  echo "Cybee supports Apple Silicon Macs only (this Mac is $ARCH)." >&2
  echo "Intel Macs are not supported." >&2
  exit 1
fi
OS_VER="$(sw_vers -productVersion 2>/dev/null)"
if [ "${OS_VER%%.*}" -lt 15 ] 2>/dev/null; then
  echo "Cybee needs macOS 15 or newer (this Mac runs $OS_VER)." >&2
  echo "Update macOS in System Settings > General > Software Update, then run this again." >&2
  exit 1
fi

# If --owner wasn't given, default to the real name of the Mac's primary (console) user — what
# they typed at setup. --owner always overrides. Handles both the "RealName: X" and two-line
# forms of dscl output, and skips root (e.g. when run via sudo).
if [ -z "$OWNER" ]; then
  CONSOLE_USER="$(stat -f%Su /dev/console 2>/dev/null || true)"
  if [ -n "$CONSOLE_USER" ] && [ "$CONSOLE_USER" != "root" ]; then
    OWNER="$(dscl . -read /Users/"$CONSOLE_USER" RealName 2>/dev/null \
      | awk 'NR==1{sub(/^RealName: */,"");if($0!=""){print;exit}} NR==2{sub(/^[[:space:]]*/,"");print;exit}')"
  fi
fi

# Extract a prefixed token (cyk_/cyj_/cya_) from a JSON blob without needing jq.
extract() { printf '%s' "$1" | grep -oE "\"$2\":\"[^\"]+\"" | head -1 | sed -E "s/.*:\"([^\"]+)\"/\1/"; }

UPGRADE=0
INSTALL_OK=0
ORG_CREATED=0

# If this run CREATED an org but the install never completed, delete the org again — otherwise
# every failed install leaves an orphan organization whose tokens nobody ever saw. Guarded by
# ORG_CREATED: with --admin we hold an admin token for an EXISTING org, and a failed install
# must never delete someone else's organization.
cleanup_failed_org() {
  if [ "$INSTALL_OK" != 1 ] && [ "$ORG_CREATED" = "1" ] && [ -n "$ADMIN_TOKEN" ]; then
    curl -fsS -X DELETE "$BACKEND/v1/org" -H "authorization: Bearer $ADMIN_TOKEN" >/dev/null 2>&1 || true
  fi
}
trap cleanup_failed_org EXIT

# The human whose config and AI assistants we set up. Under sudo, $HOME often stays the
# invoking user's — writing there as root leaves the person unable to read their own admin
# token, and `cybee connect` would wire root's (nonexistent) assistants with root's PATH.
CFG_USER="$(id -un)"
if [ "$(id -u)" = "0" ]; then
  CFG_USER="${SUDO_USER:-}"
  [ -n "$CFG_USER" ] && [ "$CFG_USER" != "root" ] || CFG_USER="$(stat -f%Su /dev/console 2>/dev/null || echo root)"
fi
CFG_HOME="$(dscl . -read "/Users/$CFG_USER" NFSHomeDirectory 2>/dev/null | awk '{print $2; exit}')"
[ -n "$CFG_HOME" ] || CFG_HOME="$HOME"
# Run a user-level command (cybee connect) as that human, not as root.
as_cfg_user() {
  if [ "$(id -u)" = "0" ] && [ "$CFG_USER" != "root" ]; then
    sudo -u "$CFG_USER" -H "$@"
  else
    "$@"
  fi
}
# Already enrolled? A plain re-run just reinstalls/upgrades the agent (organization unchanged).
# With --name or --join it would silently move this Mac into another org: refuse unless --force.
if [ -f /var/db/cybee/state.db ] && [ "$FORCE" != "1" ]; then
  if [ -n "$NAME" ] || [ -n "$JOIN" ]; then
    echo "cybee: this Mac is already enrolled in a Cybee organization." >&2
    echo "  'cybee status' shows it; 'sudo cybee uninstall' removes it." >&2
    echo "  To move this Mac to a different/new organization anyway: add --force" >&2
    echo "CYBEE_STATUS=already-enrolled"
    exit 5
  fi
  echo "==> Cybee is already enrolled on this Mac: reinstalling the agent (organization unchanged)"
  if [ "$OWNER_EXPLICIT" = "1" ]; then
    echo "    Note: --owner only applies when a Mac first enrols. To change who this Mac"
    echo "    belongs to, ask your AI assistant: \"set the owner of this Mac to <name>\""
    echo "    (no reinstall needed — --force would enrol it again as a second device)."
  fi
  UPGRADE=1
fi
if [ "$UPGRADE" = "0" ] && [ -z "$JOIN" ]; then
  # New organization. Auto-named unless --name given; you can rename later.
  [ -n "$NAME" ] || NAME="New org $(date +%Y-%m-%d)"
  echo "==> creating your Cybee organization"
  RESP="$(curl -fsS -X POST "$BACKEND/v1/org" -H 'content-type: application/json' -d "{\"name\":\"$NAME\"}")"
  ADMIN_TOKEN="$(extract "$RESP" admin_token)"
  JOIN="$(extract "$RESP" join_token)"
  ORG_ID="$(extract "$RESP" org_id)"
  [ -n "$JOIN" ] && [ -n "$ADMIN_TOKEN" ] || { echo "cybee: could not create org (backend response: $RESP)" >&2; exit 3; }
  ORG_CREATED=1
fi

PKG_URL="$CONTENT/mac/cybee-$ARCH.pkg"
TMP="$(mktemp -d)"; PKG="$TMP/cybee.pkg"

echo "==> downloading Cybee ($ARCH)"
curl -fsSL "$PKG_URL" -o "$PKG"

echo "==> verifying Apple signature & notarization"
spctl -a -vv -t install "$PKG" 2>&1 | grep -qi accepted || { echo "cybee: package not notarized — aborting." >&2; exit 3; }

# One privileged step: stash enrolment inputs for the agent, then run the installer. Root path
# chosen by context — already root; sudo (terminal, may prompt); otherwise macOS's own
# administrator dialog, so an AI assistant's shell (no TTY for sudo) can still install while the
# person types the password into the system dialog.
# The root script is a QUOTED heredoc: nothing from this shell is interpolated into it. The
# values it needs (join token, backend, owner, flags) travel as environment variables, so an
# owner name like O'Brien — or anything else — can never break out of the root shell.
shq() { printf "'%s'" "$(printf '%s' "$1" | sed "s/'/'\\\\''/g")"; }
ENVF="$TMP/install-env.sh"
{
  printf 'CYBEE_UPGRADE=%s\n' "$(shq "$UPGRADE")"
  printf 'CYBEE_FORCE=%s\n'   "$(shq "$FORCE")"
  printf 'CYBEE_JOIN=%s\n'    "$(shq "$JOIN")"
  printf 'CYBEE_BACKEND=%s\n' "$(shq "$BACKEND")"
  printf 'CYBEE_OWNER=%s\n'   "$(shq "$OWNER")"
  printf 'CYBEE_PKG=%s\n'     "$(shq "$PKG")"
} > "$ENVF"
chmod 600 "$ENVF"
PRIV="$TMP/install-root.sh"
cat > "$PRIV" <<'EOS'
#!/bin/sh
set -e
. "$(dirname "$0")/install-env.sh"
mkdir -p /var/db/cybee
chmod 700 /var/db/cybee
if [ "$CYBEE_UPGRADE" = "0" ]; then
  printf '%s' "$CYBEE_JOIN" > /var/db/cybee/join_token
  printf '%s' "$CYBEE_BACKEND" > /var/db/cybee/backend_url
  chmod 600 /var/db/cybee/join_token
  if [ -n "$CYBEE_OWNER" ]; then printf '%s' "$CYBEE_OWNER" > /var/db/cybee/owner; fi
fi
if [ "$CYBEE_FORCE" = "1" ] && [ -f /var/db/cybee/state.db ]; then
  # Moving to another organization (or re-enrolling this one). RETIRE THE OLD REGISTRATION
  # FIRST: the agent tells the backend it is going away, so the previous device row is marked
  # removed instead of lingering as a ghost Mac in the fleet. Without this, every --force
  # re-run left a duplicate entry for the same physical machine that nothing would ever
  # clean up. Best-effort: an offline or already-broken enrollment must not block a reinstall.
  if [ -x /usr/local/bin/cybee ]; then
    /usr/local/bin/cybee unenroll >/dev/null 2>&1 || true
  fi
  # Then drop the local enrollment so the agent re-enrols with the new token (alerts,
  # quarantine index and CIS baseline are kept).
  /usr/bin/sqlite3 /var/db/cybee/state.db "DELETE FROM agent_config WHERE key IN ('agent_token','device_id','org_id','backend_url')" || true
fi
/usr/sbin/installer -pkg "$CYBEE_PKG" -target /
EOS
chmod 700 "$PRIV"
if [ "$(id -u)" = "0" ]; then
  echo "==> installing"
  /bin/sh "$PRIV"
elif [ -t 1 ] || sudo -n true 2>/dev/null; then   # a real terminal: sudo can prompt on /dev/tty
  echo "==> installing (you may be asked for your password)"
  sudo /bin/sh "$PRIV"
else
  echo "==> installing — macOS will ask for this Mac's password in a dialog"
  /usr/bin/osascript -e "do shell script \"/bin/sh '$PRIV'\" with administrator privileges with prompt \"Cybee needs your password to install its security extension.\"" >/dev/null \
    || {
      echo "cybee: installation was not authorized." >&2
      echo "  If no password dialog appeared (SSH or no GUI session), re-run with sudo:" >&2
      echo "    curl -sSf https://get.cybee.dev | sudo sh" >&2
      rm -rf "$TMP"; exit 4
    }
fi
rm -rf "$TMP"
INSTALL_OK=1

# Keep the tokens where the person (and their assistant) can find them again: the admin token is
# shown once by the backend, and terminal output scrolls away.
if [ "$UPGRADE" = "1" ]; then
  echo
  echo "==> Cybee reinstalled. Check:  cybee status"
  echo "    If status shows real-time protection waiting, grant Full Disk Access:  cybee grant-fda"
  # Refresh the MCP token in any already-connected assistant. `cybee connect` reads the saved
  # ~/.config/cybee/admin_token itself — we never put the token on the command line (argv is
  # world-readable via ps).
  if [ -f "$CFG_HOME/.config/cybee/admin_token" ]; then
    as_cfg_user /usr/local/bin/cybee connect --url "$MCP" 2>/dev/null || true
  fi
  echo "CYBEE_STATUS=upgraded"; exit 0
fi
CFG="$CFG_HOME/.config/cybee"; mkdir -p "$CFG"; chmod 700 "$CFG"
if [ -n "$ADMIN_TOKEN" ]; then
  # This machine created the org: it is the admin. Save the admin token and the org it belongs
  # to, so the console can confirm the token matches this device's org before showing fleet.
  printf '%s\n' "$ADMIN_TOKEN" > "$CFG/admin_token"; chmod 600 "$CFG/admin_token"
  [ -n "$ORG_ID" ] && { printf '%s\n' "$ORG_ID" > "$CFG/org_id"; chmod 600 "$CFG/org_id"; }
else
  # Joined an existing org as a member — not an admin. Remove any stale admin token/org left
  # from a previous org on this machine so the console never shows fleet views it can't use.
  rm -f "$CFG/admin_token" "$CFG/org_id"
fi
# With --admin, $JOIN holds the ADMIN token (it enrolled this Mac) — it must never be stored
# or shown as a join token, which is a lower-privilege credential meant for sharing.
if [ "$JOIN_IS_ADMIN" = "0" ]; then
  printf '%s\n' "$JOIN" > "$CFG/join_token"; chmod 600 "$CFG/join_token"
fi
# Under sudo these files were written by root into the person's home — hand them back,
# or the customer gets "Permission denied" reading their own admin token.
if [ "$(id -u)" = "0" ] && [ "$CFG_USER" != "root" ]; then
  chown -R "$CFG_USER" "$CFG" 2>/dev/null || true
  # mkdir -p may have created ~/.config itself as root.
  [ "$(stat -f%Su "$CFG_HOME/.config" 2>/dev/null)" = "root" ] && chown "$CFG_USER" "$CFG_HOME/.config" 2>/dev/null || true
fi

# Presentation. Colors and the interactive "wait for Full Disk Access" step apply only on a real
# terminal; a non-TTY caller (an AI assistant running the script) gets a plain machine-readable
# summary instead and never blocks.
TTY=0; [ -t 1 ] && TTY=1
if [ "$TTY" = 1 ]; then
  B=$(printf '\033[1m'); DIM=$(printf '\033[2m'); G=$(printf '\033[32m')
  Y=$(printf '\033[33m'); C=$(printf '\033[36m'); RS=$(printf '\033[0m')
else
  B=; DIM=; G=; Y=; C=; RS=
fi
BAR="${C}│${RS}"
rule() { printf '%s\n' "${DIM}────────────────────────────────────────────────────────────${RS}"; }

echo
rule
printf '  %s  %sCybee is installed%s\n' "🐝" "${G}${B}" "${RS}"
rule

# --- Admin token (save it) ---------------------------------------------------
if [ -n "$ADMIN_TOKEN" ] && [ "$JOIN_IS_ADMIN" = "1" ]; then
  # Invited admin: they already hold the token (it was in the invite), so don't re-present it
  # as a secret to write down — just say what they now have.
  echo
  echo "  ${B}You are an admin of this fleet${RS} ${DIM}(token saved to $CFG/admin_token)${RS}"
  echo "  ${DIM}This Mac is protected AND can manage the fleet from your AI chat.${RS}"
elif [ -n "$ADMIN_TOKEN" ]; then
  echo
  echo "  ${B}Your admin token${RS} ${DIM}(also saved to $CFG/admin_token)${RS}"
  echo "  ${BAR} ${Y}${ADMIN_TOKEN}${RS}"
  echo "  ${DIM}Manages your whole fleet from an AI chat. Save it — shown once.${RS}"
  echo "  ${DIM}Need a co-admin later? Ask your AI assistant: \"invite an admin\".${RS}"
fi

# --- The one permission: Full Disk Access ------------------------------------
echo
echo "  ${Y}${B}Last step — turn on Full Disk Access${RS}"
echo "  ${BAR} Cybee's threat engine (Endpoint Security) is gated by macOS behind"
echo "  ${BAR} Full Disk Access. Until you switch it on, real-time protection can't run."
echo "  ${BAR}"
echo "  ${BAR} A ${B}System Settings${RS} window is opening at Full Disk Access."
echo "  ${BAR} Find ${B}CybeeES${RS} in the list and switch it ${G}${B}on${RS} — that's the only step."
# Give the message a beat, then open the Full Disk Access pane.
( sleep 2; open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles" >/dev/null 2>&1 ) &

if [ "$TTY" = 1 ]; then
  echo
  printf '  %sWaiting for you to enable it%s ' "${DIM}" "${RS}"
  granted=0; i=0
  while [ "$i" -lt 150 ]; do   # ~5 minutes at 2s
    if /usr/local/bin/cybee --json status 2>/dev/null | grep -qE '"realtime_protection":[[:space:]]*true'; then
      granted=1; break
    fi
    printf '.'; sleep 2; i=$((i+1))
  done
  echo
  if [ "$granted" = 1 ]; then
    echo "  ${G}${B}✓ Protection is ON.${RS} Cybee is watching this Mac in real time."
  else
    echo "  ${Y}Not enabled yet.${RS} Finish anytime: enable Cybee in Full Disk Access,"
    echo "  then run  ${C}cybee grant-fda${RS}  or check  ${C}cybee status${RS}."
  fi
else
  echo "  (Grant it later with: cybee grant-fda)"
fi

# --- Wire up local AI assistants (admin only) --------------------------------
# Scan for Claude Code / Cursor / Codex and point them at Cybee's MCP with the admin token.
# Re-running the installer refreshes the token in whatever is already configured.
if [ -n "$ADMIN_TOKEN" ]; then
  echo
  echo "  ${B}Connecting your AI assistants${RS}"
  # Token via env, never argv (ps-readable). `cybee connect` also falls back to the saved file.
  as_cfg_user env CYBEE_ADMIN_TOKEN="$ADMIN_TOKEN" /usr/local/bin/cybee connect --url "$MCP" 2>/dev/null | while IFS= read -r ln; do
    echo "  ${BAR} $ln"
  done
fi

# --- Footer ------------------------------------------------------------------
echo
if [ "$JOIN_IS_ADMIN" = "0" ]; then
  echo "  ${DIM}Add a teammate's Mac:${RS}  ${C}curl -sSf https://get.cybee.dev | sh -s -- --join $JOIN${RS}"
else
  echo "  ${DIM}Add a teammate's Mac:${RS}  ask your AI assistant — \"invite a teammate\""
fi
echo "  ${DIM}Remove any time:${RS}      ${C}sudo cybee uninstall${RS}"
echo

# Machine-readable summary — only for a non-TTY caller (an assistant parsing output).
if [ "$TTY" = 0 ]; then
  echo "CYBEE_STATUS=installed"
  [ -n "$ADMIN_TOKEN" ] && echo "CYBEE_ADMIN_TOKEN=$ADMIN_TOKEN"
  [ "$JOIN_IS_ADMIN" = "0" ] && echo "CYBEE_JOIN_TOKEN=$JOIN"
  echo "CYBEE_MCP_URL=$MCP"
fi
