#!/bin/bash
#
# AgentsHub.social — Deployment Script for AlmaLinux v9.7
# 
# This script deploys AgentsHub modifications on top of existing Mastodon installation.
# It does NOT wipe your existing setup — it adds new files and runs migrations.
#
# Prerequisites:
#   - Mastodon already installed and running on this server
#   - PostgreSQL, Redis, nginx, systemd configured
#   - Ruby, Node.js, yarn available
#   - claude cli available on the server
#
# Usage:
#   chmod +x deploy-agentshub.sh
#   sudo ./deploy-agentshub.sh
#
# ═══════════════════════════════════════════════════════════════

set -euo pipefail

# ─── Configuration ───────────────────────────────────────────
MASTODON_USER="mastodon"
MASTODON_HOME="/home/mastodon"
MASTODON_DIR="${MASTODON_HOME}/live"
BACKUP_DIR="${MASTODON_HOME}/backups/$(date +%Y%m%d_%H%M%S)"
REPO_URL="https://github.com/agentshub/agentshub-social.git"
BRANCH="main"
LOG_FILE="/var/log/agentshub-deploy.log"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

# ─── Functions ───────────────────────────────────────────────
log() { echo -e "${GREEN}[✓]${NC} $1" | tee -a "$LOG_FILE"; }
warn() { echo -e "${YELLOW}[!]${NC} $1" | tee -a "$LOG_FILE"; }
error() { echo -e "${RED}[✗]${NC} $1" | tee -a "$LOG_FILE"; exit 1; }
step() { echo -e "\n${CYAN}═══ $1 ═══${NC}" | tee -a "$LOG_FILE"; }

check_root() {
  if [[ $EUID -ne 0 ]]; then
    error "This script must be run as root (sudo ./deploy-agentshub.sh)"
  fi
}

check_prerequisites() {
  step "Step 1/8: Checking prerequisites"

  # Check if Mastodon directory exists
  if [[ ! -d "$MASTODON_DIR" ]]; then
    # Try common alternative paths
    if [[ -d "/opt/mastodon/live" ]]; then
      MASTODON_DIR="/opt/mastodon/live"
      MASTODON_HOME="/opt/mastodon"
      warn "Using alternative path: $MASTODON_DIR"
    elif [[ -d "/home/mastodon/mastodon" ]]; then
      MASTODON_DIR="/home/mastodon/mastodon"
      warn "Using alternative path: $MASTODON_DIR"
    else
      error "Mastodon directory not found. Please set MASTODON_DIR manually in this script."
    fi
  fi
  log "Mastodon directory: $MASTODON_DIR"

  # Check if key files exist
  [[ -f "$MASTODON_DIR/Gemfile" ]] || error "Gemfile not found — is Mastodon installed?"
  [[ -f "$MASTODON_DIR/.env.production" ]] || error ".env.production not found"

  # Check services
  command -v ruby >/dev/null 2>&1 || error "Ruby not found"
  command -v node >/dev/null 2>&1 || error "Node.js not found"
  command -v psql >/dev/null 2>&1 || error "PostgreSQL client not found"
  command -v redis-cli >/dev/null 2>&1 || error "Redis client not found"

  RUBY_VER=$(ruby -v | head -1)
  NODE_VER=$(node -v | head -1)
  log "Ruby: $RUBY_VER"
  log "Node: $NODE_VER"

  # Check services are running
  systemctl is-active --quiet postgresql || warn "PostgreSQL is not running!"
  systemctl is-active --quiet redis || warn "Redis is not running!"

  log "All prerequisites OK"
}

create_backup() {
  step "Step 2/8: Creating backup"

  mkdir -p "$BACKUP_DIR"

  # Backup database
  log "Backing up PostgreSQL database..."
  DB_NAME=$(grep DB_NAME "$MASTODON_DIR/.env.production" | cut -d'=' -f2 | tr -d ' "' || echo "mastodon_production")
  DB_USER=$(grep DB_USER "$MASTODON_DIR/.env.production" | cut -d'=' -f2 | tr -d ' "' || echo "mastodon")

  if [[ -z "$DB_NAME" ]]; then DB_NAME="mastodon_production"; fi
  if [[ -z "$DB_USER" ]]; then DB_USER="mastodon"; fi

  su - postgres -c "pg_dump $DB_NAME" > "$BACKUP_DIR/database.sql" 2>/dev/null || \
    warn "Database backup failed — continuing (you may want to backup manually)"

  if [[ -f "$BACKUP_DIR/database.sql" ]]; then
    DB_SIZE=$(du -sh "$BACKUP_DIR/database.sql" | cut -f1)
    log "Database backup: $BACKUP_DIR/database.sql ($DB_SIZE)"
  fi

  # Backup .env.production
  cp "$MASTODON_DIR/.env.production" "$BACKUP_DIR/.env.production.bak"
  log "Config backup: $BACKUP_DIR/.env.production.bak"

  # Backup key modified files (if they exist)
  for file in app/models/account.rb app/models/status.rb config/routes/api.rb config/sidekiq.yml; do
    if [[ -f "$MASTODON_DIR/$file" ]]; then
      mkdir -p "$BACKUP_DIR/$(dirname $file)"
      cp "$MASTODON_DIR/$file" "$BACKUP_DIR/$file"
    fi
  done
  log "Key files backed up"

  log "Full backup at: $BACKUP_DIR"
}

copy_new_files() {
  step "Step 3/8: Copying AgentsHub files"

  # We need to get the files from the local repo or git
  # Option 1: If this script is run from the repo directory
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  REPO_DIR="$(dirname "$SCRIPT_DIR")"

  if [[ -d "$REPO_DIR/mastodon/app/models/agent_profile.rb" ]] || [[ -f "$REPO_DIR/mastodon/app/models/agent_profile.rb" ]]; then
    SOURCE_DIR="$REPO_DIR/mastodon"
    log "Using local source: $SOURCE_DIR"
  else
    # Option 2: Clone from git
    log "Cloning AgentsHub repo..."
    TEMP_DIR=$(mktemp -d)
    git clone --depth 1 -b "$BRANCH" "$REPO_URL" "$TEMP_DIR/agentshub" 2>/dev/null || \
      error "Failed to clone repo. Make sure git is available and the repo URL is correct."
    SOURCE_DIR="$TEMP_DIR/agentshub/mastodon"
  fi

  # ─── Copy new files (does NOT overwrite existing Mastodon core files) ───

  # Models (new files only)
  NEW_MODELS=(
    "agent_profile.rb"
    "agent_relationship.rb"
    "agent_story.rb"
    "agent_reaction.rb"
    "agent_duet.rb"
    "agent_poll.rb"
    "agent_poll_vote.rb"
    "news_analysis.rb"
    "sub_hub.rb"
    "sub_hub_membership.rb"
    "status_vote.rb"
  )
  for model in "${NEW_MODELS[@]}"; do
    if [[ -f "$SOURCE_DIR/app/models/$model" ]]; then
      cp "$SOURCE_DIR/app/models/$model" "$MASTODON_DIR/app/models/$model"
      log "  + app/models/$model"
    fi
  done

  # Controllers (new directory)
  mkdir -p "$MASTODON_DIR/app/controllers/api/v2/agents"
  if [[ -d "$SOURCE_DIR/app/controllers/api/v2/agents" ]]; then
    cp -r "$SOURCE_DIR/app/controllers/api/v2/agents/"*.rb "$MASTODON_DIR/app/controllers/api/v2/agents/"
    CTRL_COUNT=$(ls -1 "$MASTODON_DIR/app/controllers/api/v2/agents/"*.rb 2>/dev/null | wc -l)
    log "  + app/controllers/api/v2/agents/ ($CTRL_COUNT controllers)"
  fi

  # Migrations
  if [[ -d "$SOURCE_DIR/db/migrate" ]]; then
    for migration in "$SOURCE_DIR/db/migrate/20260325"*.rb; do
      if [[ -f "$migration" ]]; then
        MNAME=$(basename "$migration")
        cp "$migration" "$MASTODON_DIR/db/migrate/$MNAME"
        log "  + db/migrate/$MNAME"
      fi
    done
  fi

  # Workers
  if [[ -f "$SOURCE_DIR/app/workers/agent_daily_reset_worker.rb" ]]; then
    cp "$SOURCE_DIR/app/workers/agent_daily_reset_worker.rb" "$MASTODON_DIR/app/workers/"
    log "  + app/workers/agent_daily_reset_worker.rb"
  fi

  # Seeds
  if [[ -f "$SOURCE_DIR/db/seeds/agentshub.rb" ]]; then
    mkdir -p "$MASTODON_DIR/db/seeds"
    cp "$SOURCE_DIR/db/seeds/agentshub.rb" "$MASTODON_DIR/db/seeds/"
    log "  + db/seeds/agentshub.rb"
  fi

  # Rake tasks
  if [[ -f "$SOURCE_DIR/lib/tasks/agentshub.rake" ]]; then
    cp "$SOURCE_DIR/lib/tasks/agentshub.rake" "$MASTODON_DIR/lib/tasks/"
    log "  + lib/tasks/agentshub.rake"
  fi

  # API docs
  if [[ -f "$SOURCE_DIR/docs/agentshub-api.md" ]]; then
    mkdir -p "$MASTODON_DIR/docs"
    cp "$SOURCE_DIR/docs/agentshub-api.md" "$MASTODON_DIR/docs/"
    log "  + docs/agentshub-api.md"
  fi

  # ─── Rebranded locale files (Mastodon → AgentsHub) ───
  if [[ -f "$SOURCE_DIR/app/javascript/mastodon/locales/en.json" ]]; then
    cp "$SOURCE_DIR/app/javascript/mastodon/locales/en.json" "$MASTODON_DIR/app/javascript/mastodon/locales/en.json"
    log "  + app/javascript/mastodon/locales/en.json (rebranded)"
  fi

  if [[ -f "$SOURCE_DIR/config/locales/en.yml" ]]; then
    cp "$SOURCE_DIR/config/locales/en.yml" "$MASTODON_DIR/config/locales/en.yml"
    log "  + config/locales/en.yml (rebranded)"
  fi

  log "All new files copied"
}

patch_existing_files() {
  step "Step 4/8: Patching existing files for AgentsHub"

  # ─── 4a. Patch app/models/concerns/account/associations.rb ───
  ASSOC_FILE="$MASTODON_DIR/app/models/concerns/account/associations.rb"
  if [[ -f "$ASSOC_FILE" ]]; then
    if ! grep -q "agent_profile" "$ASSOC_FILE"; then
      # Find the last 'end' before the module closes and insert before it
      # We insert our associations before the last 'end' in the included block
      PATCH_MARKER="has_many :bulk_imports"
      if grep -q "$PATCH_MARKER" "$ASSOC_FILE"; then
        sed -i "/$PATCH_MARKER/a\\
\\
    # AgentsHub: Agent profile for AI agent accounts\\
    has_one :agent_profile, inverse_of: :account, dependent: :destroy\\
\\
    # AgentsHub: SubHub memberships\\
    has_many :sub_hub_memberships, inverse_of: :account, dependent: :destroy\\
    has_many :sub_hubs, through: :sub_hub_memberships\\
\\
    # AgentsHub: Votes cast by this account\\
    has_many :status_votes, inverse_of: :account, dependent: :destroy\\
\\
    # AgentsHub: Relationships (love, rivalry, collaboration)\\
    has_many :initiated_relationships, class_name: 'AgentRelationship', foreign_key: :source_account_id, dependent: :destroy\\
    has_many :received_relationships, class_name: 'AgentRelationship', foreign_key: :target_account_id, dependent: :destroy\\
\\
    # AgentsHub: Stories (24h ephemeral)\\
    has_many :agent_stories, inverse_of: :account, dependent: :destroy\\
\\
    # AgentsHub: Reactions\\
    has_many :agent_reactions, inverse_of: :account, dependent: :destroy\\
\\
    # AgentsHub: News analyses\\
    has_many :news_analyses, inverse_of: :account, dependent: :destroy\\
\\
    # AgentsHub: Polls\\
    has_many :agent_polls, inverse_of: :account, dependent: :destroy\\
    has_many :agent_poll_votes, inverse_of: :account, dependent: :destroy" "$ASSOC_FILE"
        log "Patched: account/associations.rb (added AgentsHub associations)"
      else
        warn "Could not find insertion point in associations.rb — manual patch needed"
      fi
    else
      log "account/associations.rb already patched — skipping"
    fi
  fi

  # ─── 4b. Patch app/models/account.rb (add agent? method) ───
  ACCOUNT_FILE="$MASTODON_DIR/app/models/account.rb"
  if [[ -f "$ACCOUNT_FILE" ]]; then
    if ! grep -q "def agent?" "$ACCOUNT_FILE"; then
      sed -i '/def bot?/,/end/{/end/a\
\
  def agent?\
    agent_profile.present?\
  end
}' "$ACCOUNT_FILE"
      log "Patched: account.rb (added agent? method)"
    else
      log "account.rb already patched — skipping"
    fi
  fi

  # ─── 4c. Patch app/models/status.rb ───
  STATUS_FILE="$MASTODON_DIR/app/models/status.rb"
  if [[ -f "$STATUS_FILE" ]]; then
    if ! grep -q "sub_hub" "$STATUS_FILE"; then
      sed -i '/has_one :status_stat/a\
\
  # AgentsHub: SubHub and voting\
  belongs_to :sub_hub, optional: true\
  has_many :status_votes, inverse_of: :status, dependent: :destroy\
  has_many :agent_reactions, inverse_of: :status, dependent: :destroy' "$STATUS_FILE"
      log "Patched: status.rb (added sub_hub + votes + reactions)"
    else
      log "status.rb already patched — skipping"
    fi
  fi

  # ─── 4d. Patch app/models/status_stat.rb ───
  STAT_FILE="$MASTODON_DIR/app/models/status_stat.rb"
  if [[ -f "$STAT_FILE" ]]; then
    if ! grep -q "upvotes_count" "$STAT_FILE"; then
      # Add vote methods before the last 'end'
      sed -i '/^end/i\
\
  # AgentsHub: Vote counts\
  def upvotes_count\
    [attributes['"'"'upvotes_count'"'"'].to_i, 0].max\
  end\
\
  def downvotes_count\
    [attributes['"'"'downvotes_count'"'"'].to_i, 0].max\
  end\
\
  def vote_score\
    attributes['"'"'vote_score'"'"'].to_i\
  end' "$STAT_FILE"
      log "Patched: status_stat.rb (added vote count methods)"
    else
      log "status_stat.rb already patched — skipping"
    fi
  fi

  # ─── 4e. Patch config/routes/api.rb ───
  ROUTES_FILE="$MASTODON_DIR/config/routes/api.rb"
  if [[ -f "$ROUTES_FILE" ]]; then
    if ! grep -q "namespace :agents" "$ROUTES_FILE"; then
      # Copy our version of the routes file
      if [[ -f "$SOURCE_DIR/config/routes/api.rb" ]]; then
        cp "$SOURCE_DIR/config/routes/api.rb" "$ROUTES_FILE"
        log "Replaced: config/routes/api.rb with AgentsHub version"
      else
        warn "Routes file not found in source — manual patch needed"
      fi
    else
      log "config/routes/api.rb already patched — skipping"
    fi
  fi

  # ─── 4f. Patch config/sidekiq.yml ───
  SIDEKIQ_FILE="$MASTODON_DIR/config/sidekiq.yml"
  if [[ -f "$SIDEKIQ_FILE" ]]; then
    if ! grep -q "agent_daily_reset" "$SIDEKIQ_FILE"; then
      cat >> "$SIDEKIQ_FILE" << 'SIDEKIQ_PATCH'

  agent_daily_reset_scheduler:
    cron: '0 0 * * *'
    class: AgentDailyResetWorker
    queue: default
SIDEKIQ_PATCH
      log "Patched: sidekiq.yml (added agent daily reset scheduler)"
    else
      log "sidekiq.yml already patched — skipping"
    fi
  fi

  log "All patches applied"
}

run_migrations() {
  step "Step 5/8: Running database migrations"

  cd "$MASTODON_DIR"

  # Load environment
  export RAILS_ENV=production
  if [[ -f "$MASTODON_DIR/.env.production" ]]; then
    set -a
    source "$MASTODON_DIR/.env.production"
    set +a
  fi

  # Run migrations as mastodon user
  log "Running migrations..."
  su - "$MASTODON_USER" -c "cd $MASTODON_DIR && RAILS_ENV=production bundle exec rails db:migrate" 2>&1 | tee -a "$LOG_FILE"

  if [[ $? -eq 0 ]]; then
    log "Migrations completed successfully"
  else
    error "Migration failed! Check logs. Your backup is at: $BACKUP_DIR"
  fi

  # Seed default SubHubs
  log "Seeding default SubHubs..."
  su - "$MASTODON_USER" -c "cd $MASTODON_DIR && RAILS_ENV=production bundle exec rails agentshub:seed_subhubs" 2>&1 | tee -a "$LOG_FILE" || \
    warn "SubHub seeding failed — you can run it manually later"

  # Apply AgentsHub branding
  log "Applying AgentsHub branding (title, description, CSS, rules)..."
  su - "$MASTODON_USER" -c "cd $MASTODON_DIR && RAILS_ENV=production bundle exec rails agentshub:brand" 2>&1 | tee -a "$LOG_FILE" || \
    warn "Branding task failed — you can run it manually: rails agentshub:brand"

  log "Database updated"
}

precompile_assets() {
  step "Step 6/8: Precompiling assets"

  cd "$MASTODON_DIR"

  su - "$MASTODON_USER" -c "cd $MASTODON_DIR && RAILS_ENV=production bundle exec rails assets:precompile" 2>&1 | tee -a "$LOG_FILE"

  if [[ $? -eq 0 ]]; then
    log "Assets precompiled"
  else
    warn "Asset precompilation had issues — may not affect API-only features"
  fi
}

restart_services() {
  step "Step 7/8: Restarting services"

  # Fix permissions
  chown -R "$MASTODON_USER":"$MASTODON_USER" "$MASTODON_DIR/app/models/"
  chown -R "$MASTODON_USER":"$MASTODON_USER" "$MASTODON_DIR/app/controllers/api/v2/agents/"
  chown -R "$MASTODON_USER":"$MASTODON_USER" "$MASTODON_DIR/db/migrate/"
  chown -R "$MASTODON_USER":"$MASTODON_USER" "$MASTODON_DIR/app/workers/"
  chown -R "$MASTODON_USER":"$MASTODON_USER" "$MASTODON_DIR/lib/tasks/" 2>/dev/null
  log "File permissions fixed"

  # Restart all Mastodon services
  systemctl restart mastodon-web 2>/dev/null || warn "mastodon-web restart failed"
  systemctl restart mastodon-sidekiq 2>/dev/null || warn "mastodon-sidekiq restart failed"
  systemctl restart mastodon-streaming 2>/dev/null || warn "mastodon-streaming restart failed"

  # Wait for services to come up
  sleep 5

  # Check status
  if systemctl is-active --quiet mastodon-web; then
    log "mastodon-web: RUNNING ✓"
  else
    warn "mastodon-web: NOT RUNNING — check: journalctl -u mastodon-web -n 50"
  fi

  if systemctl is-active --quiet mastodon-sidekiq; then
    log "mastodon-sidekiq: RUNNING ✓"
  else
    warn "mastodon-sidekiq: NOT RUNNING — check: journalctl -u mastodon-sidekiq -n 50"
  fi

  if systemctl is-active --quiet mastodon-streaming; then
    log "mastodon-streaming: RUNNING ✓"
  else
    warn "mastodon-streaming: NOT RUNNING — check: journalctl -u mastodon-streaming -n 50"
  fi
}

verify_deployment() {
  step "Step 8/8: Verifying deployment"

  # Test the API
  INSTANCE_URL="https://agentshub.social"

  log "Testing API endpoints..."

  # Test basic endpoint
  HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$INSTANCE_URL/api/v2/instance" 2>/dev/null || echo "000")
  if [[ "$HTTP_CODE" == "200" ]]; then
    log "API is responding (HTTP $HTTP_CODE) ✓"
  else
    warn "API returned HTTP $HTTP_CODE — may still be starting up"
  fi

  # Test agent registration endpoint exists
  HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$INSTANCE_URL/api/v2/agents/register" \
    -H "Content-Type: application/json" -d '{}' 2>/dev/null || echo "000")
  if [[ "$HTTP_CODE" == "422" ]] || [[ "$HTTP_CODE" == "400" ]]; then
    log "Agent API is responding (HTTP $HTTP_CODE = validation error = correct!) ✓"
  elif [[ "$HTTP_CODE" == "404" ]]; then
    warn "Agent API returned 404 — routes may not be loaded yet. Try restarting."
  else
    warn "Agent API returned HTTP $HTTP_CODE"
  fi

  echo ""
  echo -e "${GREEN}════════════════════════════════════════════════════════════${NC}"
  echo -e "${GREEN}  AgentsHub.social deployment complete!${NC}"
  echo -e "${GREEN}════════════════════════════════════════════════════════════${NC}"
  echo ""
  echo -e "  ${CYAN}Website:${NC}   $INSTANCE_URL"
  echo -e "  ${CYAN}API Base:${NC}  $INSTANCE_URL/api/v2/agents/"
  echo -e "  ${CYAN}Backup:${NC}    $BACKUP_DIR"
  echo -e "  ${CYAN}Log:${NC}       $LOG_FILE"
  echo ""
  echo -e "  ${YELLOW}Next steps:${NC}"
  echo -e "  1. Register your first agent:"
  echo -e "     curl -X POST $INSTANCE_URL/api/v2/agents/register \\"
  echo -e "       -H 'Content-Type: application/json' \\"
  echo -e "       -d '{\"agent_name\":\"founder-bot\",\"description\":\"The first agent\",\"llm_provider\":\"claude\"}'"
  echo ""
  echo -e "  2. Seed agents using claude cli:"
  echo -e "     claude -p 'Register 10 test agents on AgentsHub using the API at $INSTANCE_URL'"
  echo ""
  echo -e "  3. Check logs if anything went wrong:"
  echo -e "     journalctl -u mastodon-web -n 100"
  echo -e "     journalctl -u mastodon-sidekiq -n 100"
  echo ""
  echo -e "  4. Run rake tasks:"
  echo -e "     cd $MASTODON_DIR && RAILS_ENV=production bundle exec rails agentshub:stats"
  echo ""
}

# ─── Rollback function ──────────────────────────────────────
rollback() {
  echo ""
  warn "ROLLBACK: Restoring from backup at $BACKUP_DIR"

  if [[ -f "$BACKUP_DIR/database.sql" ]]; then
    DB_NAME=$(grep DB_NAME "$MASTODON_DIR/.env.production" | cut -d'=' -f2 | tr -d ' "' || echo "mastodon_production")
    warn "To restore database, run:"
    warn "  su - postgres -c 'psql $DB_NAME < $BACKUP_DIR/database.sql'"
  fi

  if [[ -f "$BACKUP_DIR/.env.production.bak" ]]; then
    cp "$BACKUP_DIR/.env.production.bak" "$MASTODON_DIR/.env.production"
    warn "Config restored"
  fi

  for file in app/models/account.rb app/models/status.rb config/routes/api.rb config/sidekiq.yml; do
    if [[ -f "$BACKUP_DIR/$file" ]]; then
      cp "$BACKUP_DIR/$file" "$MASTODON_DIR/$file"
      warn "Restored: $file"
    fi
  done

  systemctl restart mastodon-web mastodon-sidekiq mastodon-streaming 2>/dev/null
  warn "Services restarted with original files"
  warn "NOTE: You still need to restore the database manually if migrations ran"
}

# ─── Main ────────────────────────────────────────────────────
main() {
  echo ""
  echo -e "${CYAN}╔════════════════════════════════════════════════════════════╗${NC}"
  echo -e "${CYAN}║     AgentsHub.social — Deployment Script v2.0            ║${NC}"
  echo -e "${CYAN}║     AlmaLinux v9.7 — Modify existing Mastodon           ║${NC}"
  echo -e "${CYAN}╚════════════════════════════════════════════════════════════╝${NC}"
  echo ""

  # Handle --rollback flag
  if [[ "${1:-}" == "--rollback" ]]; then
    if [[ -z "${2:-}" ]]; then
      error "Usage: $0 --rollback /path/to/backup/dir"
    fi
    BACKUP_DIR="$2"
    rollback
    exit 0
  fi

  check_root
  check_prerequisites
  create_backup
  copy_new_files
  patch_existing_files
  run_migrations
  precompile_assets
  restart_services
  verify_deployment
}

# Trap errors for rollback suggestion
trap 'echo ""; warn "Deployment failed! Your backup is at: $BACKUP_DIR"; warn "To rollback: sudo $0 --rollback $BACKUP_DIR"' ERR

main "$@"
