#!/bin/bash
set -e

# ── TuneVault Self-Hosted Update ──────────────────────────────────────────────
# Updates an existing TuneVault install in place. Auto-detects Docker vs direct
# Node.js (same detection as setup.sh). Always backs up .env first and never
# touches the database volume/data. Run from your install directory:
#     bash update.sh
#
# Output helpers — same color-coded style as setup.sh.
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'; CYAN='\033[0;36m'; NC='\033[0m'
ok()   { echo -e "${GREEN}✓${NC} $1"; }
warn() { echo -e "${YELLOW}⚠${NC} $1"; }
fail() { echo -e "${RED}✗${NC} $1"; }
step() { CURRENT_PHASE="$1"; echo -e "\n${CYAN}▶ $1${NC}"; }

CURRENT_PHASE="startup"
trap 'fail "Update failed during: ${CURRENT_PHASE}"; echo "  Fix the error shown above and re-run: bash update.sh"' ERR

# Run from the script's own directory so relative paths are the install dir.
cd "$(dirname "$0")"

echo -e "${CYAN}╔═══════════════════════════════════════╗${NC}"
echo -e "${CYAN}║   TuneVault Self-Hosted Update         ║${NC}"
echo -e "${CYAN}╚═══════════════════════════════════════╝${NC}"

# ── Guard: must be an existing install ────────────────────────────────────────
if [ ! -f .env ]; then
  fail "No .env found in $(pwd) — run this from your TuneVault install directory."
  echo "  First-time install? Use setup.sh instead."
  exit 1
fi

# ── 1. Back up .env (always, before touching anything) ────────────────────────
step "Backing up configuration"
BACKUP=".env.bak.$(date +%Y%m%d-%H%M%S)"
cp .env "$BACKUP"
ok "Saved current .env → ${BACKUP}"

# ── 2. Update the source code ─────────────────────────────────────────────────
step "Updating source code"
if [ -d .git ]; then
  BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo main)
  git fetch --all --prune
  # --ff-only: never create a merge commit; if it can't fast-forward, stop and
  # let the operator resolve (protects local edits). .env is gitignored so pull
  # never clobbers it.
  if ! git pull --ff-only origin "$BRANCH"; then
    fail "git pull could not fast-forward (local commits or conflicts on branch ${BRANCH})."
    echo "  Resolve manually (git status), then re-run: bash update.sh"
    exit 1
  fi
  ok "Pulled latest code (branch: ${BRANCH})"
else
  warn "This install is not a git checkout (it was extracted from a release zip)."
  echo "  Download the newest release zip and extract it OVER this directory, keeping"
  echo "  your .env in place (the zip does not contain .env):"
  echo ""
  echo "    curl -LO https://releases.tunevault.app/tunevault-selfhosted-vX.Y.Z.zip"
  echo "    unzip -o tunevault-selfhosted-vX.Y.Z.zip -d /tmp/tv-new"
  echo "    cp -a /tmp/tv-new/tunevault/. \"$(pwd)/\""
  echo ""
  read -r -p "Have you already extracted the new code over this directory? [y/N] " ANS
  if [ "$ANS" != "y" ] && [ "$ANS" != "Y" ]; then
    warn "No code change applied. Extract the new zip as shown above, then re-run: bash update.sh"
    exit 0
  fi
  ok "Proceeding with the freshly-extracted code in $(pwd)"
fi

# Defensive: if the update path somehow removed .env, restore it from backup.
if [ ! -f .env ]; then
  cp "$BACKUP" .env
  warn ".env was missing after the update — restored it from ${BACKUP}"
fi

# Re-apply the SELinux label on .env (enforcing systems) so the service can read it.
if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce)" = "Enforcing" ]; then
  sudo restorecon -v "$(pwd)/.env" >/dev/null 2>&1 || true
fi

# ── 3. Rebuild / restart (auto-detect Docker vs direct Node.js) ───────────────
RUNMODE=""
if command -v docker >/dev/null 2>&1 && command -v docker-compose >/dev/null 2>&1 && [ -f docker-compose.yml ]; then
  step "Rebuilding containers (Docker Compose)"
  # NEVER pass -v here — that deletes the postgres_data volume (wipes the DB).
  # 'up -d --build' rebuilds only the app; the db service uses a prebuilt image
  # and is left running with its volume intact. Migrations run on container start
  # (Dockerfile CMD: node migrate.js && node server.js).
  docker-compose up -d --build
  ok "Containers rebuilt and restarted — database volume preserved"
  RUNMODE="docker"
else
  step "Updating direct Node.js install"
  npm install --production 2>/dev/null
  ok "Dependencies updated"

  # Load env + run migrations (idempotent — safe to run every update).
  set -a; source .env; set +a
  node migrate.js
  ok "Database migrations applied"

  # Restart whichever supervisor is running the app.
  if systemctl list-unit-files 2>/dev/null | grep -q '^tunevault\.service' \
     || systemctl status tunevault >/dev/null 2>&1; then
    sudo systemctl restart tunevault
    ok "Restarted systemd service: tunevault"
    RUNMODE="systemd"
  elif command -v pm2 >/dev/null 2>&1 && pm2 describe tunevault >/dev/null 2>&1; then
    pm2 restart tunevault
    ok "Restarted pm2 process: tunevault"
    RUNMODE="pm2"
  else
    warn "Couldn't detect a running service (systemd 'tunevault' or pm2 'tunevault')."
    echo "  Start it manually, e.g.:"
    echo "    sudo systemctl start tunevault"
    echo "    # or:  pm2 start server.js --name tunevault"
    RUNMODE="manual"
  fi
fi

# ── 4. Version confirmation ───────────────────────────────────────────────────
step "Update complete"
NEWVER="unknown"
if [ -f VERSION ]; then
  NEWVER=$(tr -d ' \t\r\n' < VERSION)
elif command -v node >/dev/null 2>&1 && [ -f package.json ]; then
  NEWVER=$(node -e "process.stdout.write(require('./package.json').version||'unknown')" 2>/dev/null || echo unknown)
fi

echo ""
echo -e "${GREEN}╔═══════════════════════════════════════╗${NC}"
echo -e "${GREEN}║   ✓ TuneVault updated                 ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════╝${NC}"
ok "Now running version: ${NEWVER}"
[ -n "$RUNMODE" ] && ok "Run mode: ${RUNMODE}"
echo ""
echo -e "${CYAN}Upgrade the proxy agent:${NC}"
echo "  Each Oracle server's proxy agent auto-upgrades on its next heartbeat now that"
echo "  the app is updated — no manual step. Just wait ~60 seconds, then open a connection"
echo "  card and confirm it reports the new agent/proxy version."
echo ""
echo "  Your previous config was backed up to: ${BACKUP}"
