#!/usr/bin/env bash
#
# Manual production deploy for the Aradhya ERP. Run it ON THE SERVER, after uploading the changed files:
#
#   cd /home/aradhyaenterpris/public_html/erp.aradhyaenterprise.com
#   bash deploy/deploy.sh                # full deploy (does a git pull first when this folder is a git checkout)
#   bash deploy/deploy.sh --backup-only  # just take (and prune) the database backup
#
# What it does, in order:
#   1. Backs up the database to ~/erp-backups (gzip) and deletes backups older than 31 days. If the backup fails the
#      deploy stops, nothing has been changed yet.
#   2. Puts the site in maintenance mode (and always brings it back up, even if a later step fails).
#   3. Runs the central and per-workspace migrations (this is what runs the one-time data fixes), then the same seeders
#      the old pipeline ran.
#   4. Clears and rebuilds the config, route and view caches, and restarts the queue worker.
#
# Settings can be overridden from the environment: BACKUP_DIR, KEEP_DAYS, PHP_BIN.

set -Eeuo pipefail

APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BACKUP_DIR="${BACKUP_DIR:-$HOME/erp-backups}"
KEEP_DAYS="${KEEP_DAYS:-31}"
PHP_BIN="${PHP_BIN:-php}"
STAMP="$(date +%Y%m%d-%H%M%S)"
BACKUP_FILE="$BACKUP_DIR/erp-$STAMP.sql.gz"
SITE_DOWN=0

log() { printf '\n==> %s\n' "$*"; }
die() { printf '\nERROR: %s\n' "$*" >&2; exit 1; }

cd "$APP_DIR"
[ -f artisan ] || die "artisan not found in $APP_DIR, run this from the project folder."
[ -f .env ] || die ".env not found in $APP_DIR."

# Reads one KEY=value from .env, without sourcing the file (values may hold spaces or $).
env_get() {
    local line
    line="$(grep -E "^$1=" .env | tail -n 1 || true)"
    line="${line#*=}"
    line="${line%$'\r'}"
    line="${line#\"}"; line="${line%\"}"
    line="${line#\'}"; line="${line%\'}"
    printf '%s' "$line"
}

backup_database() {
    local host port name user pass
    host="$(env_get DB_HOST)"; port="$(env_get DB_PORT)"; name="$(env_get DB_DATABASE)"
    user="$(env_get DB_USERNAME)"; pass="$(env_get DB_PASSWORD)"
    : "${host:=127.0.0.1}"; : "${port:=3306}"
    [ -n "$name" ] && [ -n "$user" ] || die "DB_DATABASE / DB_USERNAME missing in .env."

    log "Backing up database '$name' to $BACKUP_FILE"
    mkdir -p "$BACKUP_DIR"
    chmod 700 "$BACKUP_DIR"

    # --single-transaction: a consistent copy without locking the live tables. The password goes in the environment,
    # not on the command line, so it does not show in the process list.
    if ! MYSQL_PWD="$pass" mysqldump --single-transaction --no-tablespaces \
        -h "$host" -P "$port" -u "$user" "$name" | gzip > "$BACKUP_FILE"; then
        rm -f "$BACKUP_FILE"
        die "Database backup failed, nothing was deployed."
    fi

    # An empty or truncated dump compresses to a few dozen bytes.
    [ "$(stat -c %s "$BACKUP_FILE")" -gt 1024 ] || { rm -f "$BACKUP_FILE"; die "Backup file is empty, nothing was deployed."; }
    gzip -t "$BACKUP_FILE" || { rm -f "$BACKUP_FILE"; die "Backup file is corrupt, nothing was deployed."; }
    chmod 600 "$BACKUP_FILE"
    printf 'Backup saved (%s).\n' "$(du -h "$BACKUP_FILE" | cut -f1)"

    log "Deleting backups older than $KEEP_DAYS days from $BACKUP_DIR"
    find "$BACKUP_DIR" -maxdepth 1 -type f -name 'erp-*.sql.gz' -mtime "+$KEEP_DAYS" -print -delete
    printf 'Backups kept: %s\n' "$(find "$BACKUP_DIR" -maxdepth 1 -type f -name 'erp-*.sql.gz' | wc -l)"
}

bring_site_up() {
    if [ "$SITE_DOWN" = 1 ]; then
        "$PHP_BIN" artisan up || true
        SITE_DOWN=0
    fi
}
trap 'printf "\nDeploy FAILED at line %s. Bringing the site back up. The backup is %s\n" "$LINENO" "$BACKUP_FILE" >&2; bring_site_up' ERR
trap bring_site_up EXIT

backup_database

if [ "${1:-}" = "--backup-only" ]; then
    log "Backup only, nothing deployed."
    exit 0
fi

update_code() {
    if [ ! -d .git ]; then
        log "No git checkout here, deploying the files as they are (upload them first)."
        return
    fi

    local old new changed
    old="$(git rev-parse HEAD)"
    log "Pulling the latest code (git pull --ff-only)"
    git pull --ff-only
    new="$(git rev-parse HEAD)"

    [ "$old" != "$new" ] || { printf 'Already up to date.\n'; return; }
    printf 'Updated %s -> %s\n' "${old:0:8}" "${new:0:8}"

    # vendor/ and public/build/ are not in git, so changes to them have to be done by hand.
    changed="$(git diff --name-only "$old" "$new")"
    if printf '%s\n' "$changed" | grep -qE '^composer\.(json|lock)$'; then
        printf '\nWARNING: composer.json/lock changed. vendor/ is not in git: run  composer install --no-dev --optimize-autoloader\n'
    fi
    if printf '%s\n' "$changed" | grep -qE '^(package(-lock)?\.json|tailwind\.config\.js|vite\.config\.js|resources/(css|js)/)'; then
        printf '\nWARNING: front-end sources changed. public/build is not in git: run  npm run build  on your PC and upload public/build\n'
    fi
}

update_code

log "Maintenance mode on"
"$PHP_BIN" artisan down --retry=60
SITE_DOWN=1

log "Clearing the config cache so migrations read the real .env"
"$PHP_BIN" artisan config:clear

log "Central migrations"
"$PHP_BIN" artisan migrate --force

log "Workspace migrations (includes the one-time data fixes)"
"$PHP_BIN" artisan tenants:migrate

log "Seeders (roles and permissions, plans)"
"$PHP_BIN" artisan tenants:seed --class=RolePermissionSeeder --force
"$PHP_BIN" artisan db:seed --class=PlanSeeder --force

log "Rebuilding caches"
"$PHP_BIN" artisan route:clear
"$PHP_BIN" artisan view:clear
"$PHP_BIN" artisan config:cache
"$PHP_BIN" artisan route:cache
"$PHP_BIN" artisan view:cache
"$PHP_BIN" artisan storage:link 2>/dev/null || true
"$PHP_BIN" artisan queue:restart || true

log "Maintenance mode off"
bring_site_up

log "Deploy finished. Backup: $BACKUP_FILE"
