#!/usr/bin/env bash
# Update the LIVE site with a new release zip — keeps every user, order, article, upload and setting.
#
# On cPanel → Terminal:
#   source /home/sazzadb1/virtualenv/thereversetimes.com/3.9/bin/activate && cd /home/sazzadb1/thereversetimes.com
#   bash deploy/update_live.sh thereversetimes-YYYYMMDD-HHMM.zip
#
# What it does:
#   1. backs up the database, uploads and current code into backups/
#   2. replaces ONLY the code (db.sqlite3, media/, private_media/, .env, logs/ are never touched)
#   3. installs packages, applies database migrations, collects static files
#   4. restarts the site
#   If any step fails it puts the old code and database back automatically.
set -euo pipefail

APP_DIR="$(cd "$(dirname "$0")/.." && pwd)"
cd "$APP_DIR"
ZIP="${1:-}"
STAMP=$(date +%Y%m%d-%H%M%S)
BK="backups/$STAMP"

say() { printf '\n\033[1m%s\033[0m\n' "$*"; }
die() { printf '\n\033[31m✗ %s\033[0m\n' "$*"; exit 1; }

[ -n "$ZIP" ] && [ -f "$ZIP" ] || die "Give the release zip, e.g.: bash deploy/update_live.sh thereversetimes-20261030-1200.zip"
[ -f manage.py ] || die "Run this from the app folder (the one with manage.py)."
[ -f .env ] || die ".env is missing. Copy .env.example to .env and fill it in first (see DEPLOY.md)."
grep -q '^SECRET_KEY=..' .env || die "SECRET_KEY is empty in .env (see DEPLOY.md)."
grep -q '^DEBUG=False' .env || die "Set DEBUG=False in .env on the live site."
command -v python >/dev/null || die "Activate the cPanel virtualenv first (see the top of this script)."
if unzip -l "$ZIP" | grep -E '(db\.sqlite3|(^|/)\.env$|(^| )media/|private_media/)' >/dev/null; then
  die "This zip contains data or secrets. Build it with deploy/make_release.sh."
fi

say "1/6 Backing up live data → $BK"
mkdir -p "$BK"
if [ -f db.sqlite3 ]; then
  python - "$BK/db.sqlite3" <<'PY'
import sqlite3, sys
src = sqlite3.connect('db.sqlite3'); dst = sqlite3.connect(sys.argv[1])
src.backup(dst); dst.close(); src.close()   # safe copy even while the site is running
PY
fi
gzip -1 "$BK/db.sqlite3"   # compressed: a database full of logs shrinks a lot, saving disk quota
tar -czf "$BK/media.tar.gz" media private_media 2>/dev/null || true
tar -czf "$BK/code.tar.gz" apps reverse_times templates static locale deploy manage.py passenger_wsgi.py requirements.txt 2>/dev/null || true
echo "   database, uploads and current code saved"

rollback() {
  printf '\n\033[31m✗ Update failed — restoring the previous version…\033[0m\n'
  rm -rf apps reverse_times templates static locale
  tar -xzf "$BK/code.tar.gz"
  [ -f "$BK/db.sqlite3.gz" ] && gunzip -c "$BK/db.sqlite3.gz" > db.sqlite3
  mkdir -p tmp && touch tmp/restart.txt
  printf '\033[31m  Old version restored. Nothing was lost. Backup kept in %s\033[0m\n' "$BK"
  exit 1
}
trap rollback ERR

# One-time move: older versions kept the Gmail password and Telegram token inside settings.py.
# Copy them into .env (on the server only, values are never printed) before that file is replaced.
python - <<'PY'
import re
try:
    old = open('reverse_times/settings.py', encoding='utf-8').read()
except OSError:
    old = ''
env = open('.env', encoding='utf-8').read()
have = {line.split('=', 1)[0].strip() for line in env.splitlines() if '=' in line}
added = []
for key in ('EMAIL_HOST_USER', 'EMAIL_HOST_PASSWORD', 'TELEGRAM_BOT_TOKEN', 'TELEGRAM_CHAT_ID'):
    m = re.search(r"^%s\s*=\s*['\"]([^'\"]+)['\"]" % key, old, re.M)
    if key not in have and m:
        env += ('' if env.endswith('\n') else '\n') + f'{key}={m.group(1)}\n'
        added.append(key)
# A short secret key is weak: replace it with a new random 64-character one (readers sign in again once).
import secrets
m = re.search(r'^SECRET_KEY=(.*)$', env, re.M)
if m and len(m.group(1).strip()) < 50:
    env = env.replace(m.group(0), 'SECRET_KEY=' + secrets.token_urlsafe(48))
    added.append('new SECRET_KEY')
if added:
    open('.env', 'w', encoding='utf-8').write(env)
print('   .env updated:', ', '.join(added) if added else 'nothing needed')
PY
chmod 600 .env

# Old leftovers from the first upload (e.g. a static llms.txt that hides the live one) → backup folder.
for f in llms.txt magazine.html populate.py firebase.json .firebaserc the_reverse_times_master_prompt.md; do
  [ -e "$f" ] && mv "$f" "$BK/" && echo "   moved old file $f to $BK/"
done

say "2/6 Replacing the code"
TMP=$(mktemp -d)
unzip -q "$ZIP" -d "$TMP"
for d in apps reverse_times templates static locale deploy; do
  [ -d "$TMP/$d" ] && { rm -rf "$d"; cp -R "$TMP/$d" "$d"; }
done
for f in manage.py passenger_wsgi.py requirements.txt .env.example DEPLOY.md; do
  [ -f "$TMP/$f" ] && cp "$TMP/$f" "$f"
done
# .htaccess: only installed the first time, so your cPanel lines are never overwritten.
[ -f .htaccess ] || { [ -f "$TMP/.htaccess" ] && cp "$TMP/.htaccess" .htaccess; }
rm -rf "$TMP"
# Keep private folders private even if the app folder is inside public_html.
for d in private_media backups logs; do mkdir -p "$d"; printf 'Require all denied\n' > "$d/.htaccess"; done

say "3/6 Installing packages"
pip install -q -r requirements.txt

# Rebuilding a big table needs about twice the database size in free disk space.
python - <<'PY'
import json, os, subprocess, sys
need = os.path.getsize('db.sqlite3') * 2.2 / 1024 / 1024 if os.path.exists('db.sqlite3') else 0
try:
    info = json.loads(subprocess.run(['uapi', '--output=json', 'Quota', 'get_quota_info'], capture_output=True, text=True).stdout)['result']['data']
    free = float(info['megabyte_limit']) - float(info['megabytes_used']) if float(info.get('megabyte_limit') or 0) else 1e9
except Exception:
    free = 1e9  # quota unknown: carry on
print(f'   free disk: {free:.0f} MB, needed for the database update: {need:.0f} MB')
if free < need:
    print('   Not enough disk space — free some space (or ask the host for more) and run the update again.')
    sys.exit(1)
PY

say "4/6 Updating the database (adds new tables/columns; existing data is kept)"
python manage.py migrate --noinput

say "5/6 Collecting static files"
python manage.py collectstatic --noinput -v 0
rm -rf .cache            # drop cached pages from the old version

python manage.py check --deploy --fail-level ERROR >/dev/null

trap - ERR
say "6/6 Restarting the site"
mkdir -p tmp && touch tmp/restart.txt

# Keep the 10 most recent backups.
ls -1dt backups/2*/ 2>/dev/null | tail -n +11 | xargs -r rm -rf

printf '\n\033[32m✓ Live site updated. Backup of the previous version: %s\033[0m\n' "$BK"
echo "  Open https://thereversetimes.com and check the homepage, a magazine page and the admin."
