#!/usr/bin/env bash
#
# Deploy a local WordPress site (e.g. a WordPress Studio site) to the live
# Awesomate site. SNAPSHOTS LIVE FIRST via the hub (so a bad deploy is one
# rollback away), then pushes files, then optionally the database.
#
#   deploy.sh --from ~/Studio/mysite --domain mysite.awesomate.site [options]
#
# Options:
#   --docroot PATH     Live docroot (default: /home/<cpanel-user>/public_html)
#   --local-url URL    Local site URL (default: `wp option get home` in --from)
#   --live-url URL     Live site URL (default: https://<domain>)
#   --with-db          Also migrate the database (export local → import live →
#                      search-replace URL). OFF by default — files-only is the
#                      safe common case. See the DB note below.
#   --yes              Skip the confirmation prompt.
#
# DB note: --with-db runs `wp db export` on the local site. That requires the
# local WP to produce a MySQL-compatible dump. A WordPress Studio site backed
# by SQLite will NOT export a MySQL dump cleanly — for those, migrate content
# with a migration plugin or keep DB changes on live. If the import fails, the
# live site is untouched-except-files and the pre-deploy snapshot is intact.
#
# Client-side deps: bash, ssh, rsync, curl, and (for --with-db) wp-cli locally.
set -euo pipefail

CRED="$HOME/.awesomate/credentials.json"
[ -f "$CRED" ] || { echo "Not connected — run the Connect Claude Code setup from hub.awesomate.ai/claude first."; exit 1; }

FROM="" DOMAIN="" DOCROOT="" LOCAL_URL="" LIVE_URL="" WITH_DB=0 ASSUME_YES=0
while [ "$#" -gt 0 ]; do
  case "$1" in
    --from) FROM="$2"; shift 2;;
    --domain) DOMAIN="$2"; shift 2;;
    --docroot) DOCROOT="$2"; shift 2;;
    --local-url) LOCAL_URL="$2"; shift 2;;
    --live-url) LIVE_URL="$2"; shift 2;;
    --with-db) WITH_DB=1; shift;;
    --yes) ASSUME_YES=1; shift;;
    *) echo "Unknown option: $1"; exit 1;;
  esac
done

[ -n "$FROM" ] && [ -n "$DOMAIN" ] || { echo "Usage: deploy.sh --from <local-wp-dir> --domain <live-domain> [--with-db] [--docroot PATH]"; exit 1; }
[ -d "$FROM/wp-content" ] || { echo "--from ($FROM) doesn't look like a WordPress site (no wp-content/)."; exit 1; }
# Guard the rsync --delete below: require a COMPLETE WordPress root AND a
# non-empty wp-content, so an empty/partial --from (interrupted pull-live, a
# bare theme repo, a wrong path) can't wipe the entire live wp-content.
{ [ -f "$FROM/wp-load.php" ] || [ -f "$FROM/wp-includes/version.php" ]; } || {
  echo "--from ($FROM) isn't a complete WordPress root (missing wp-load.php / wp-includes). Refusing to deploy."; exit 1; }
{ [ -d "$FROM/wp-content/themes" ] || [ -d "$FROM/wp-content/plugins" ]; } || {
  echo "--from wp-content looks empty (no themes/ or plugins/). Refusing to --delete against live."; exit 1; }
case "$DOMAIN" in *[!a-z0-9.-]*|"") echo "Invalid --domain."; exit 1;; esac

# Resolve the account (folder pin / AWESOMATE_ACCOUNT / sole profile) and its
# connection details + PAT via the shared resolver (node is present wherever
# the MCP runs). Capture first, THEN eval — so a resolver failure (no-shell
# plan exits 2, unpinned multi-account exits 1) aborts here with its own
# message instead of leaving $U/$P/… unset for `set -u` to trip on later.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if ! CREDS_EVAL="$(node "$SCRIPT_DIR/resolve-account.mjs" --ssh --api)"; then
  exit 1
fi
eval "$CREDS_EVAL"
echo "→ account: $ACCT"

# Harden every credentials-derived field BEFORE it reaches ssh/rsync/scp. rsync
# re-tokenizes its -e remote-shell string on whitespace (ignoring shell quotes),
# so a port/host/user/keypath carrying whitespace could inject extra ssh options
# (e.g. -oProxyCommand=…) and run arbitrary code on THIS machine. Charclass-gate.
case "$P" in ''|*[!0-9]*) echo "Invalid ssh port in credentials."; exit 1;; esac
case "$H" in ''|*[!a-zA-Z0-9.-]*) echo "Invalid ssh host in credentials."; exit 1;; esac
case "$U" in ''|*[!a-z0-9]*) echo "Invalid ssh user in credentials."; exit 1;; esac
case "$K" in ''|*[[:space:]]*) echo "Invalid key path in credentials (whitespace not allowed)."; exit 1;; esac

DOCROOT="${DOCROOT:-/home/$U/public_html}"
LIVE_URL="${LIVE_URL:-https://$DOMAIN}"
SSH=(ssh -i "$K" -p "$P" -o StrictHostKeyChecking=accept-new -o BatchMode=yes)

if [ "$ASSUME_YES" != "1" ]; then
  echo "About to deploy '$FROM' → $U@$H:$DOCROOT (live: $LIVE_URL). DB migration: $([ "$WITH_DB" = 1 ] && echo yes || echo no)."
  printf "A snapshot is taken first. Continue? [y/N] "; read -r ans; case "$ans" in y|Y) ;; *) echo "Aborted."; exit 1;; esac
fi

# 1) Snapshot live FIRST via the hub (the safety net; abort if it fails).
echo "→ Snapshotting live site…"
SNAP_JSON="$(curl -fsS -X POST -H "Authorization: Bearer $PAT" -H 'Content-Type: application/json' \
  -d '{"reason":"pre-deploy"}' "$API/api/hosting-access/sites/$DOMAIN/snapshot")" || { echo "Snapshot failed — aborting deploy (nothing changed)."; exit 1; }
SNAP_ID="$(printf '%s' "$SNAP_JSON" | node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{try{console.log(JSON.parse(s).snapshotId||"")}catch{console.log("")}})')"
[ -n "$SNAP_ID" ] || { echo "Snapshot returned no id — aborting."; exit 1; }
echo "  snapshot: $SNAP_ID (roll back any time: awesomate_rollback_site $DOMAIN $SNAP_ID)"

rollback_hint() { echo; echo "✗ Deploy failed. Your live site's pre-deploy snapshot is $SNAP_ID — restore it with the awesomate_rollback_site tool (domain $DOMAIN)."; }
trap 'rollback_hint' ERR

# 2) Files: sync wp-content only (never the docroot root — leaves wp-config etc).
echo "→ Syncing files (wp-content)…"
rsync -az --delete --max-delete=200 -e "ssh -i $(printf %q "$K") -p $P -o StrictHostKeyChecking=accept-new -o BatchMode=yes" \
  "$FROM/wp-content/" "$U@$H:$DOCROOT/wp-content/"

# 3) DB (opt-in).
if [ "$WITH_DB" = "1" ]; then
  echo "→ Migrating database…"
  command -v wp >/dev/null || { echo "wp-cli not found locally — install it or deploy without --with-db."; exit 1; }
  LOCAL_URL="${LOCAL_URL:-$(wp --path="$FROM" option get home 2>/dev/null || true)}"
  [ -n "$LOCAL_URL" ] || { echo "Couldn't determine local URL — pass --local-url."; exit 1; }
  TMP="$(mktemp -d)"; DUMP="$TMP/db.sql"
  wp --path="$FROM" db export "$DUMP" --quiet || { echo "Local db export failed (SQLite Studio sites can't export MySQL — see the DB note in this script)."; rm -rf "$TMP"; exit 1; }
  scp -i "$K" -P "$P" -o StrictHostKeyChecking=accept-new -o BatchMode=yes "$DUMP" "$U@$H:$DOCROOT/.deploy-import.sql"
  "${SSH[@]}" "$U@$H" "cd $(printf %q "$DOCROOT") && wp db import .deploy-import.sql --quiet && wp search-replace $(printf %q "$LOCAL_URL") $(printf %q "$LIVE_URL") --all-tables --quiet && rm -f .deploy-import.sql"
  rm -rf "$TMP"
fi

# 4) Verify.
echo "→ Verifying $LIVE_URL …"
CODE="$(curl -s -o /dev/null -w '%{http_code}' -m 20 -L "$LIVE_URL" || echo 000)"
trap - ERR
case "$CODE" in 2*|3*) echo "✓ Deployed. $LIVE_URL responded $CODE. Pre-deploy snapshot: $SNAP_ID.";; *) echo "⚠ Deployed, but $LIVE_URL responded $CODE — check the site; roll back with snapshot $SNAP_ID if needed.";; esac
