#!/usr/bin/env bash
# Run on the DigitalOcean host; never from GitHub Actions.
set -Eeuo pipefail
umask 022

PROJECT=/mnt/gxo_volume_01/moro
STATE=/mnt/gxo_volume_01/moro-deploy
PHP=/usr/bin/php8.4
export GIT_TERMINAL_PROMPT=0
mkdir -p "$STATE/backups" "$STATE/builds"
exec 9>"$STATE/deploy.lock"
flock -n 9 || exit 0
cd "$PROJECT"
git diff --quiet
git diff --cached --quiet
git fetch --quiet origin develop
TARGET=$(git rev-parse refs/remotes/origin/develop)
PREVIOUS=$(git rev-parse HEAD)
LIVE=$(cat "$STATE/deployed-sha" 2>/dev/null || true)
if [[ "$TARGET" = "$PREVIOUS" && "$TARGET" = "$LIVE" ]]; then
    exit 0
fi
git merge-base --is-ancestor "$PREVIOUS" "$TARGET"
printf '%s deploy %s -> %s\n' "$(date -Is)" "$PREVIOUS" "$TARGET"

STAGE=$(mktemp -d "$STATE/builds/$TARGET.XXXXXX")
BACKUP="$STATE/backups/$(date +%Y%m%dT%H%M%S)-$PREVIOUS"
mkdir -m 700 "$BACKUP"
git archive "$TARGET" | tar -x -C "$STAGE"
DEPENDENCIES_CHANGED=0
if git diff --quiet "$PREVIOUS" "$TARGET" -- composer.json composer.lock; then
    ln -s "$PROJECT/vendor" "$STAGE/vendor"
else
    DEPENDENCIES_CHANGED=1
    (cd "$STAGE" && composer install --no-dev --no-scripts --no-interaction --prefer-dist --optimize-autoloader)
fi
while IFS= read -r -d '' file; do
    "$PHP" -l "$file" >/dev/null
done < <(find "$STAGE/app" "$STAGE/config" "$STAGE/database/migrations" -name '*.php' -print0)

ASSETS_CHANGED=0
if ! git diff --quiet "$PREVIOUS" "$TARGET" -- resources package.json package-lock.json 'webpack*' scripts/ops/write-build-env.cjs; then
    ASSETS_CHANGED=1
    export NVM_DIR=/home/gxo/.nvm
    set +u
    . "$NVM_DIR/nvm.sh"
    nvm use 14.21.3
    set -u
    if git diff --quiet "$PREVIOUS" "$TARGET" -- package.json package-lock.json; then
        ln -s "$PROJECT/node_modules" "$STAGE/node_modules"
    else
        (cd "$STAGE" && npm ci --no-audit --no-fund)
    fi
    (cd "$STAGE" && node scripts/ops/write-build-env.cjs "$PROJECT/.env" "$STAGE/.env")
    (cd "$STAGE" && npm run production-all >"$BACKUP/build.log" 2>&1) || {
        tail -60 "$BACKUP/build.log"
        exit 1
    }
    # Check every asset referenced by both Mix manifests before touching live files.
    "$PHP" "$STAGE/scripts/ops/check-mix-assets.php" "$STAGE/public"
    tar -czf "$BACKUP/assets.tar.gz" -C "$PROJECT/public" \
        js css backend/js backend/css mix-manifest.json backend/mix-manifest.json
fi

# Credentials stay in a temporary mode-0600 defaults file on this DO host.
"$PHP" "$STAGE/scripts/ops/backup-database.php" "$PROJECT" "$BACKUP/database.sql"
printf '%s\n' "$PREVIOUS" >"$BACKUP/previous-sha"
MAINTENANCE=0
rollback() {
    local status=$?
    trap - ERR
    if [[ "$MAINTENANCE" = 1 ]]; then
        printf '%s deploy failed; restoring code %s (database backup: %s)\n' "$(date -Is)" "$PREVIOUS" "$BACKUP"
        git reset --hard "$PREVIOUS"
        if [[ -d "$BACKUP/vendor" ]]; then
            mv "$PROJECT/vendor" "$BACKUP/failed-vendor"
            mv "$BACKUP/vendor" "$PROJECT/vendor"
        fi
        if [[ -f "$BACKUP/assets.tar.gz" ]]; then
            tar -xzf "$BACKUP/assets.tar.gz" -C "$PROJECT/public"
        fi
        "$PHP" artisan config:clear
        "$PHP" artisan route:clear
        "$PHP" artisan view:clear
        "$PHP" artisan up
        sudo -n supervisorctl restart 'moro-dev-queue:*'
        sudo -n systemctl reload php8.4-fpm
    fi
    exit "$status"
}
trap rollback ERR
MAINTENANCE=1
"$PHP" artisan down --retry=30
git merge --ff-only "$TARGET"
if [[ "$DEPENDENCIES_CHANGED" = 1 ]]; then
    mv "$PROJECT/vendor" "$BACKUP/vendor"
    mv "$STAGE/vendor" "$PROJECT/vendor"
fi
if [[ "$ASSETS_CHANGED" = 1 ]]; then
    rsync -a --exclude=uploads --exclude=storage "$STAGE/public/" "$PROJECT/public/"
fi
"$PHP" artisan config:clear
"$PHP" artisan migrate --force --no-interaction
"$PHP" scripts/ops/refresh-settings-cache.php
"$PHP" artisan view:cache
"$PHP" artisan route:cache
"$PHP" artisan up
sudo -n supervisorctl restart 'moro-dev-queue:*'
sudo -n systemctl reload php8.4-fpm

for path in / /rental /faq /admin/login /js/app.js /backend/js/admin.js; do
    code=$(curl --silent --show-error --location --max-time 30 --output /dev/null \
        --write-out '%{http_code}' "https://moro.gxo.co.jp$path")
    [[ "$code" = 200 ]] || { printf 'health failed %s HTTP %s\n' "$path" "$code"; false; }
done
[[ "$(git rev-parse HEAD)" = "$TARGET" ]]
printf '%s\n' "$TARGET" >"$STATE/deployed-sha.new"
mv "$STATE/deployed-sha.new" "$STATE/deployed-sha"
printf '%s deployed %s; backup %s\n' "$(date -Is)" "$TARGET" "$BACKUP"
MAINTENANCE=0
trap - ERR
# Keep the failure workspace for diagnosis; successful build files are disposable.
rm -rf -- "$STAGE"
