#!/usr/bin/env bash
#
# Deploy a release bundle on a shared cPanel host. Run this ON THE HOST, from
# the application directory, for every deploy AFTER the first.
#
#   bash deploy-shared.sh
#
# This is deploy.sh with the three things a shared host does not have taken
# out, and nothing else changed:
#
#   - no `npm ci && npm run build`  - public/build arrives in the bundle,
#                                     built by package-shared.sh on a machine
#                                     with Node >= 22.12
#   - no `systemctl`                - there is no supervised worker to restart;
#                                     the queue runs from cron, and
#                                     `queue:restart` is what signals it
#   - no `git pull`                 - releases arrive as an uploaded archive
#
# It never writes .env, never generates an APP_KEY and never seeds. Those are
# first-install steps: docs/deployment-shared-hosting.md.

set -euo pipefail

APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$APP_DIR"

say()  { printf '\n\033[1m==> %s\033[0m\n' "$1"; }
fail() { printf '\n\033[31mdeploy-shared.sh: %s\033[0m\n' "$1" >&2; exit 1; }

# --- 0. refuse to run against a half-configured host ------------------------

[ -f .env ] || fail ".env is missing. This is a first install: follow docs/deployment-shared-hosting.md before running this."

# An empty APP_KEY here would tempt somebody to run key:generate, which
# permanently destroys every stored employee IBAN (employees.iban uses
# Laravel's `encrypted` cast, and there is no key history).
grep -q '^APP_KEY=base64:' .env \
    || fail "APP_KEY is not set in .env. If this is a first install, generate it ONCE as part of the install - never as part of a deploy. If it was set and is now gone, restore it from your password store before doing anything else: every employee IBAN depends on it."

grep -q '^APP_DEBUG=false' .env \
    || fail "APP_DEBUG is not false in .env. Fix it before deploying: true prints stack traces, file paths and .env contents to whoever triggers an error."

# cron has no login PATH, and neither does every shared-host shell. Resolve the
# interpreter the same way the cron lines must, and prefer an explicit one.
PHP_BIN="${PHP_BIN:-}"
if [ -z "$PHP_BIN" ]; then
    for candidate in \
        /opt/cpanel/ea-php84/root/usr/bin/php \
        /opt/cpanel/ea-php83/root/usr/bin/php \
        /usr/local/bin/php \
        "$(command -v php 2>/dev/null || true)"
    do
        [ -n "$candidate" ] && [ -x "$candidate" ] || continue
        if "$candidate" -r 'exit(PHP_VERSION_ID >= 80300 ? 0 : 1);' 2>/dev/null; then
            PHP_BIN="$candidate"
            break
        fi
    done
fi
[ -n "$PHP_BIN" ] || fail "No PHP 8.3+ binary found. Set it explicitly, e.g. PHP_BIN=/opt/cpanel/ea-php83/root/usr/bin/php bash deploy-shared.sh"
say "Using PHP $("$PHP_BIN" -r 'echo PHP_VERSION;') at $PHP_BIN"

# --- 1. the bundle must be complete -----------------------------------------

# vendor/ and public/build/ are built by package-shared.sh, not here. If either
# is missing, the upload was incomplete - and the public/build case is the one
# that costs an afternoon, because it 500s every admin page including login
# rather than degrading to an unstyled one.
[ -f vendor/autoload.php ] \
    || fail "vendor/autoload.php is missing. The bundle is incomplete - re-upload it. vendor/ is built by package-shared.sh on your own machine."

[ -f public/build/manifest.json ] \
    || fail "public/build/manifest.json is missing. Every admin page will return 500, including the login page, because Filament resolves its theme through the Vite helper on every request. Re-upload the bundle."

# --- 2. migrate -------------------------------------------------------------

say "Applying migrations"
"$PHP_BIN" artisan migrate --force

# --- 3. caches --------------------------------------------------------------

# Clear before caching: a stale config cache from the previous release must
# never be readable while the new one is being written.
say "Rebuilding caches"
"$PHP_BIN" artisan optimize:clear
"$PHP_BIN" artisan optimize

# Idempotent, and irrelevant to document scans - those are served by
# DocumentFileController from a private disk that storage:link cannot reach.
# It is here only so a release that starts using the public disk works.
"$PHP_BIN" artisan storage:link 2>/dev/null || true

# --- 4. the queue -----------------------------------------------------------

# There is no service to restart. queue:restart writes a timestamp into the
# cache store, and each cron-launched `queue:work --stop-when-empty` reads it
# and exits if it started before the signal - which is exactly what makes a
# worker pick up new code without supervision.
#
# This is why CACHE_STORE must be a real store and never `array`: with `array`
# the signal is written to memory that dies with the process, and a worker
# would keep running last release's code until it happened to exit.
say "Signalling the queue workers"
"$PHP_BIN" artisan queue:restart

# --- 5. report --------------------------------------------------------------

say "Deployed"
"$PHP_BIN" artisan about --only=environment

printf '\n  Check these before you walk away:\n'
printf '    1. Debug Mode reads OFF and Environment reads production, above.\n'
printf '    2. curl -s -o /dev/null -w "%%{http_code}\\n" "$(grep -m1 ^APP_URL= .env | cut -d= -f2-)/admin/login"  -> 200\n'
printf '    3. %s artisan schedule:list   -> eight commands, in UAE time\n' "$PHP_BIN"
printf '    4. %s artisan queue:monitor default   -> [0] OK\n' "$PHP_BIN"
printf '\n  Seeders are not part of a deploy. If the release notes mention a new\n'
printf '  alert horizon or a new ledger account, run these two as well - both are\n'
printf '  safe to re-run and change nothing when there is nothing new:\n\n'
printf '    %s artisan db:seed --class=AlertRuleSeeder --force\n' "$PHP_BIN"
printf '    %s artisan db:seed --class=ChartOfAccountsSeeder --force\n\n' "$PHP_BIN"
