#!/bin/bash
# Connect the user's OWN Google account to their local agent, through Google's
# Workspace CLI (`gws`, npm @googleworkspace/cli) and a Cloud project THEY own.
#
# Why not Drafted's Google connection: full-inbox read needs a restricted Gmail
# scope, and a restricted scope on Drafted's consent screen means an annual,
# paid CASA assessment (see GOOGLE_SCOPES in server/lib/google-auth.mjs). On the
# user's own project the scope is theirs, the token stays on this machine
# (encrypted, OS keyring), and Drafted's servers never see a message. The agent
# reads source material with `gws`; Drafted holds the work it produces.
#
# Run by install-mcp.sh (interactive installs only) and by `drafted google-setup`.
#   --from-installer   remember a "no" so the auto-updater's re-runs never re-ask
set -e

FROM_INSTALLER=0
[ "${1:-}" = "--from-installer" ] && FROM_INSTALLER=1

BOLD="\033[1m"; DIM="\033[2m"; GREEN="\033[32m"; YELLOW="\033[33m"; RED="\033[31m"; RESET="\033[0m"
say() { echo -e "    $1"; }
ok() { echo -e "    ${GREEN}✓${RESET} $1"; }
warn() { echo -e "    ${YELLOW}!${RESET} $1"; }
# Prompts read the terminal, not stdin (stdin may be a pipe). DRAFTED_TTY lets
# the test feed answers from a file.
TTY="${DRAFTED_TTY:-/dev/tty}"
exec 3<"$TTY" 2>/dev/null || exec 3</dev/null
ask() { local reply; read -r -p "    $1 " reply <&3 || reply=""; echo "$reply"; }
pause() { read -r -p "    $1 — press Enter when done " _ <&3 || true; }
open_url() {
  say "${DIM}$1${RESET}"
  if command -v open >/dev/null 2>&1; then open "$1" >/dev/null 2>&1 || true
  elif command -v xdg-open >/dev/null 2>&1; then xdg-open "$1" >/dev/null 2>&1 || true
  fi
}

DRAFTED_HOME="$HOME/.drafted"
DECLINED_MARKER="$DRAFTED_HOME/google-setup.declined"
NPM_GLOBAL_PREFIX="${NPM_GLOBAL_PREFIX:-$DRAFTED_HOME/npm-global}"
GWS_CONFIG_DIR="${GOOGLE_WORKSPACE_CLI_CONFIG_DIR:-$HOME/.config/gws}"
# Read-only, deliberately: the agent reads with gws, and EVERY send goes through
# Drafted's send_email action, which the user approves in the Drafted UI. A
# send-capable gws token would be a way around that approval.
GWS_SERVICES="gmail,drive,calendar,docs,sheets"
APIS="gmail.googleapis.com,drive.googleapis.com,calendar-json.googleapis.com,docs.googleapis.com,sheets.googleapis.com"

# `gws auth status` prints JSON; read ONE field without echoing the rest (it
# carries the account's client id and scopes, which don't belong in a log).
gws_status_field() {
  "$GWS" auth status 2>/dev/null | node -e '
    let s = ""; process.stdin.on("data", (d) => (s += d)).on("end", () => {
      let j = {}; try { j = JSON.parse(s); } catch {}
      const f = process.argv[1];
      if (f === "gmail") return console.log((j.has_refresh_token && (j.scopes || []).some((x) => x.includes("/auth/gmail"))) ? "yes" : "no");
      console.log(j[f] === undefined || j[f] === null ? "" : String(j[f]));
    });' "$1"
}

# Replies to email threads you connect to a project are noticed on THIS computer:
# `drafted mail-watch` checks them every 5 minutes with your read-only gws sign-in
# and tells Drafted only that a new message arrived. Idempotent.
install_watcher() {
  # DRAFTED_NO_WATCHER: tests must never load a real LaunchAgent.
  [ -z "${DRAFTED_NO_WATCHER:-}" ] && [ "$(uname -s)" = "Darwin" ] || return 0
  local drafted_bin; drafted_bin="$(command -v drafted 2>/dev/null || echo "$NPM_GLOBAL_PREFIX/bin/drafted")"
  [ -x "$drafted_bin" ] || return 0
  "$drafted_bin" mail-watch --install >/dev/null 2>&1 && ok "Checking connected email threads for replies every 5 minutes" || true
}

# ── The gws CLI ─────────────────────────────────────────────────
# Resolve Google's gws specifically: Homebrew's `gws` formula is an unrelated
# git-workspace tool that installs a binary of the same name.
GWS=""
for candidate in "$NPM_GLOBAL_PREFIX/bin/gws" "$(command -v gws 2>/dev/null || true)"; do
  if [ -n "$candidate" ] && [ -x "$candidate" ] && "$candidate" --help 2>&1 | grep -q "Google Workspace"; then
    GWS="$candidate"; break
  fi
done
# Already connected: nothing to ask. Checked BEFORE the consent prompt so an
# interactive installer re-run doesn't re-pitch a finished setup.
if [ -n "$GWS" ] && [ "$(gws_status_field gmail)" = "yes" ]; then
  ok "Google is connected for your agent (gws)"
  install_watcher
  exit 0
fi

# ── Consent ───────────────────────────────────────────────────────
echo ""
echo -e "  ${BOLD}Let your agent read your Gmail, Drive and Calendar${RESET} ${DIM}(optional, ~5 minutes)${RESET}"
say "Your agent (Claude or Codex) can then find the work already sitting in your inbox"
say "and files, and build it out on your Drafted surface."
say "${DIM}• Uses a Google Cloud project that YOU own — not a Drafted app.${RESET}"
say "${DIM}• Read-only. The login token is stored encrypted on this machine only.${RESET}"
say "${DIM}• Drafted's servers never receive your mail. Only what you ask your agent to${RESET}"
say "${DIM}  put on a surface is uploaded.${RESET}"
answer="$(ask "Set this up now? [y/N]")"
case "$answer" in
  [yY]*) /bin/rm -f "$DECLINED_MARKER" ;;
  *)
    [ "$FROM_INSTALLER" = 1 ] && { mkdir -p "$DRAFTED_HOME"; : > "$DECLINED_MARKER"; }
    say "${DIM}Skipped. Run ${BOLD}drafted google-setup${RESET}${DIM} any time.${RESET}"
    exit 0
    ;;
esac

if [ -z "$GWS" ]; then
  say "Installing Google's Workspace CLI (gws)..."
  npm install -g @googleworkspace/cli --prefix "$NPM_GLOBAL_PREFIX" >/dev/null
  GWS="$NPM_GLOBAL_PREFIX/bin/gws"
fi
ok "gws $("$GWS" --version 2>/dev/null | head -1 | sed 's/^gws //')"

# ── Your Cloud project + OAuth client ───────────────────────────
account_type="$(ask "Is this a Google Workspace (work/school) account, or a personal @gmail.com? [w/p]")"

if [ "$(gws_status_field client_config_exists)" != "true" ]; then
  if command -v gcloud >/dev/null 2>&1; then
    say "gcloud found — ${BOLD}gws auth setup${RESET} will sign you in, create a project and enable the APIs."
    "$GWS" auth setup || warn "gws auth setup did not finish — continuing with the manual steps."
  else
    say "${DIM}gcloud not installed — doing the same steps in the Cloud Console instead.${RESET}"
  fi
fi

PROJECT_ID="$(gws_status_field project_id)"
[ -z "$PROJECT_ID" ] && command -v gcloud >/dev/null 2>&1 && PROJECT_ID="$(gcloud config get-value project 2>/dev/null || true)"
if [ -z "$PROJECT_ID" ]; then
  say "Create a Cloud project (any name, e.g. ${BOLD}my-agent${RESET}):"
  open_url "https://console.cloud.google.com/projectcreate"
  PROJECT_ID="$(ask "Paste the project ID:")"
  [ -z "$PROJECT_ID" ] && { warn "No project ID — stopping. Run ${BOLD}drafted google-setup${RESET} to resume."; exit 1; }
fi
ok "Project $PROJECT_ID"

if [ "$(gws_status_field client_config_exists)" != "true" ]; then
  say ""
  say "${BOLD}a.${RESET} Enable the Gmail, Drive, Calendar, Docs and Sheets APIs (click Enable):"
  open_url "https://console.cloud.google.com/flows/enableapi?apiid=$APIS&project=$PROJECT_ID"
  pause "APIs enabled"
  say "${BOLD}b.${RESET} Name the app (e.g. ${BOLD}My agent${RESET}) and pick your own email as support contact:"
  open_url "https://console.cloud.google.com/auth/branding?project=$PROJECT_ID"
  case "$account_type" in
    [wW]*) say "   Audience: choose ${BOLD}Internal${RESET} — only your organisation, no review needed." ;;
    *)     say "   Audience: choose ${BOLD}External${RESET}." ;;
  esac
  pause "Branding saved"
  say "${BOLD}c.${RESET} Create an OAuth client: type ${BOLD}Desktop app${RESET} → Create → ${BOLD}Download JSON${RESET}:"
  open_url "https://console.cloud.google.com/auth/clients/create?project=$PROJECT_ID"
  pause "JSON downloaded"
  secret="$(ls -t "$HOME"/Downloads/client_secret_*.json 2>/dev/null | head -1 || true)"
  if [ -z "$secret" ]; then
    secret="$(ask "Couldn't find client_secret_*.json in Downloads. Path to the file:")"
  fi
  [ -f "$secret" ] || { warn "No client file — stopping. Run ${BOLD}drafted google-setup${RESET} to resume."; exit 1; }
  mkdir -p "$GWS_CONFIG_DIR"
  # Moved, not copied: a client secret shouldn't be left lying in Downloads.
  /bin/mv -f "$secret" "$GWS_CONFIG_DIR/client_secret.json"
  chmod 600 "$GWS_CONFIG_DIR/client_secret.json"
  ok "OAuth client saved"
fi

# External apps left in "Testing" get refresh tokens Google expires after
# 7 days. Publishing the user's OWN unverified app is allowed for personal use
# (Google caps unverified apps at 100 users) and makes the token long-lived.
case "$account_type" in
  [wW]*) ;;
  *)
    say ""
    say "${BOLD}d.${RESET} Click ${BOLD}Publish app${RESET} → Confirm. Without this Google logs you out every 7 days."
    open_url "https://console.cloud.google.com/auth/audience?project=$PROJECT_ID"
    pause "App published"
    ;;
esac

# ── Sign in ─────────────────────────────────────────────────────
say ""
say "Signing in. Google will say ${BOLD}\"Google hasn't verified this app\"${RESET} — it's your own app:"
say "click ${BOLD}Advanced → Go to <your app> (unsafe)${RESET}, then allow read access."
"$GWS" auth login --readonly -s "$GWS_SERVICES" || true

if [ "$(gws_status_field gmail)" = "yes" ]; then
  ok "Google connected — your agent can now read Gmail, Drive and Calendar via ${BOLD}gws${RESET}"
  install_watcher
  exit 0
fi
echo -e "    ${RED}✗${RESET} Sign-in didn't complete. Run ${BOLD}drafted google-setup${RESET} to try again."
exit 1
