#!/usr/bin/env bash
#
# TemseeEdu deploy script. Two modes:
#
#   Redeploy (default) — for an already-installed site. Safe to re-run:
#   every step is idempotent.
#     ./deploy.sh                 # standard/VPS hosting
#     ./deploy.sh --cpanel        # shared cPanel hosting (file cache/session,
#                                   sync queue, Livewire/exception-renderer
#                                   asset publishing, no service reload)
#     ./deploy.sh --skip-build    # skip npm ci && npm run build even if npm
#                                   is present (public/build/ is committed to
#                                   git, so this is rarely needed — hosts with
#                                   no npm already skip the build automatically)
#     ./deploy.sh --skip-cron     # don't touch this account's crontab
#     ./deploy.sh --skip-migrate  # skip php artisan migrate + tenants:migrate
#
#   Fresh install — bootstraps a brand new box over SSH: clone, install
#   dependencies, run the interactive installer (edition choice, DB,
#   admin account — see `php artisan temsee:install --help` for every
#   value as a flag, for a fully scripted run), then falls through into
#   the same redeploy steps above.
#     ./deploy.sh --fresh --repo=https://github.com/Linxford/TemseeEdu.git --dir=/var/www/school
#     ./deploy.sh --fresh                          # already cloned, run from inside it
#     ./deploy.sh --fresh --cpanel                  # combine with any flag above
#
#   Fresh install does NOT create the database for you on cPanel — cPanel
#   hosting mostly doesn't allow CREATE DATABASE from the app. Create it in
#   cPanel's MySQL Databases first; temsee:install (via --hosting-mode
#   cpanel) or temsee:cpanel-install-school will use it once it exists.
#
# What this does NOT do, ever: touch a webserver's vhost config, or create
# a database for cPanel hosting (see above). It assumes the webserver
# document root (public/) is already correctly configured.

set -euo pipefail

CPANEL=0
SKIP_BUILD=0
SKIP_CRON=0
SKIP_MIGRATE=0
FRESH=0
REPO=""
TARGET_DIR=""
INSTALL_ARGS=()

for arg in "$@"; do
    case "$arg" in
        --cpanel) CPANEL=1 ;;
        --skip-build) SKIP_BUILD=1 ;;
        --skip-cron) SKIP_CRON=1 ;;
        --skip-migrate) SKIP_MIGRATE=1 ;;
        --fresh) FRESH=1 ;;
        --repo=*) REPO="${arg#--repo=}" ;;
        --dir=*) TARGET_DIR="${arg#--dir=}" ;;
        --edition=*|--hosting-mode=*|--app-url=*|--school-domain=*|--tenant-id=*|--school-name=*|--school-email=*|--school-phone=*|--admin-name=*|--admin-email=*|--admin-password=*|--db-connection=*|--db-host=*|--db-port=*|--db-database=*|--db-username=*|--db-password=*|--license-key=*|--no-interaction-defaults)
            # Passed straight through to `php artisan temsee:install` — see
            # its --help for the full list. Only meaningful with --fresh.
            INSTALL_ARGS+=("$arg") ;;
        *) echo "Unknown option: $arg" >&2; exit 1 ;;
    esac
done

# --cpanel only configured this script's own later steps (cron, cpanel-check,
# no service reload) — it never actually told `temsee:install` to use cPanel
# hosting mode, so `./deploy.sh --fresh --cpanel` alone left hosting mode
# unset and fell through to temsee:install's interactive prompt (or failed
# outright under --no-interaction-defaults). Inject it unless the caller
# already passed an explicit --hosting-mode=.
if [ "$CPANEL" -eq 1 ] && [ "$FRESH" -eq 1 ]; then
    HAS_HOSTING_MODE=0
    for arg in "${INSTALL_ARGS[@]:-}"; do
        case "$arg" in
            --hosting-mode=*) HAS_HOSTING_MODE=1 ;;
        esac
    done
    if [ "$HAS_HOSTING_MODE" -eq 0 ]; then
        INSTALL_ARGS+=("--hosting-mode=cpanel")
    fi
fi

log() { echo -e "\n\033[1;34m==>\033[0m $1"; }
warn() { echo -e "\033[1;33m!!\033[0m $1" >&2; }

if [ "$FRESH" -eq 1 ]; then
    if [ -n "$REPO" ]; then
        CLONE_DIR="${TARGET_DIR:-$(basename "$REPO" .git)}"
        if [ -d "$CLONE_DIR" ]; then
            warn "$CLONE_DIR already exists — using it as-is instead of re-cloning (run git pull yourself first if you wanted the latest)."
        else
            log "Cloning $REPO into $CLONE_DIR"
            git clone "$REPO" "$CLONE_DIR"
        fi
        cd "$CLONE_DIR"
    else
        cd "$(dirname "${BASH_SOURCE[0]}")"
        warn "No --repo given — assuming this is already a clone (running from $(pwd))."
    fi

    if [ ! -f .env ]; then
        log "Creating .env from .env.example"
        cp .env.example .env
    fi

    log "Installing PHP dependencies (needed before artisan can run at all)"
    composer install --no-dev --optimize-autoloader --no-interaction

    if ! grep -qE '^APP_KEY=.+' .env; then
        log "Generating APP_KEY"
        php artisan key:generate --force
    fi

    log "Running the interactive installer (php artisan temsee:install)"
    php artisan temsee:install "${INSTALL_ARGS[@]}"
else
    cd "$(dirname "${BASH_SOURCE[0]}")"
fi

if [ ! -f .env ]; then
    warn ".env not found. This script deploys an already-installed site — run with --fresh for first-time setup, or install manually first (visit /install, or php artisan temsee:cpanel-install-school on cPanel)."
    exit 1
fi

APP_DIR="$(pwd)"

log "Installing PHP dependencies (production, optimized autoloader)"
composer install --no-dev --optimize-autoloader --no-interaction

if [ "$SKIP_BUILD" -eq 0 ]; then
    if command -v npm >/dev/null 2>&1; then
        log "Building frontend assets"
        npm ci
        npm run build
    else
        # Most hosts this deploys to (shared cPanel, plain PHP boxes) have no
        # Node at all — that's expected, not a problem. public/build/ is
        # committed to git specifically so the assets already arrived with
        # this pull; there's nothing to build here. Just re-run
        # `npm run build` locally and commit the result whenever
        # resources/css or resources/js change.
        log "No npm on this host — using the public/build/ assets already checked into git."
    fi
else
    log "Skipping asset build (--skip-build)"
fi

# Take the site down only if something is already installed — never block a
# fresh install flow that hasn't gone through /install yet.
INSTALLED=$(php artisan install:status 2>/dev/null | grep -i "installed" | grep -i "yes" || true)
if [ -n "$INSTALLED" ]; then
    log "Enabling maintenance mode"
    php artisan down --retry=15 || true
fi
trap 'php artisan up >/dev/null 2>&1 || true' EXIT

log "Clearing cached config (stale cache would otherwise mask .env changes during migration)"
php artisan config:clear

if [ "$SKIP_MIGRATE" -eq 0 ]; then
    log "Running central migrations"
    php artisan migrate --force

    log "Running tenant migrations (all tenants)"
    php artisan tenants:migrate --force || warn "tenants:migrate reported an issue — check output above."
else
    log "Skipping migrations (--skip-migrate)"
fi

if [ "$CPANEL" -eq 1 ]; then
    log "Applying cPanel-safe repairs (writable dirs, Livewire/exception-renderer assets)"
    php artisan temsee:cpanel-check --fix
fi

log "Rebuilding caches (config, routes, views, Filament components)"
php artisan config:cache
php artisan route:cache
php artisan view:cache
php artisan filament:cache-components

log "Ensuring public/storage symlink exists"
php artisan storage:link || true

log "Clearing OPcache (if this PHP has an opcache-clear route configured, hit it manually — this script can't reach FPM's opcache from CLI)"
if command -v systemctl >/dev/null 2>&1 && [ "$CPANEL" -eq 0 ]; then
    for svc in php8.3-fpm php8.4-fpm php-fpm; do
        if systemctl is-active --quiet "$svc" 2>/dev/null; then
            log "Reloading $svc (picks up opcache.validate_timestamps=0 changes)"
            sudo systemctl reload "$svc" 2>/dev/null || warn "Could not reload $svc — reload it manually if OPcache is caching stale code."
            break
        fi
    done
else
    warn "Shared/cPanel hosting usually can't reload PHP-FPM from a deploy script. If opcache.validate_timestamps=0 is set, ask your host to reload PHP, or leave validate_timestamps on (slower, but no manual step)."
fi

if [ "$SKIP_CRON" -eq 0 ]; then
    log "Ensuring cron entries are installed"

    SCHEDULE_LINE="* * * * * cd ${APP_DIR} && php artisan schedule:run >> /dev/null 2>&1"

    QUEUE_CONNECTION=$(grep -E '^QUEUE_CONNECTION=' .env | tail -1 | cut -d= -f2- || echo "")
    QUEUE_LINE=""
    if [ "$QUEUE_CONNECTION" = "database" ]; then
        # sync needs no worker at all; redis/other connections are expected to
        # run a persistent `queue:work` under Supervisor, not cron — this
        # cron-triggered --stop-when-empty pattern is specifically the safe
        # shared-hosting answer for the database driver.
        QUEUE_LINE="* * * * * cd ${APP_DIR} && php artisan queue:work --stop-when-empty --tries=3 --max-time=55 >> /dev/null 2>&1"
    fi

    if command -v crontab >/dev/null 2>&1; then
        EXISTING_CRON="$(crontab -l 2>/dev/null || true)"
        NEW_CRON="$EXISTING_CRON"

        if ! grep -Fq "artisan schedule:run" <<< "$EXISTING_CRON"; then
            NEW_CRON="${NEW_CRON}
${SCHEDULE_LINE}"
            log "Adding scheduler cron entry"
        else
            log "Scheduler cron entry already present"
        fi

        if [ -n "$QUEUE_LINE" ]; then
            if ! grep -Fq "artisan queue:work" <<< "$EXISTING_CRON"; then
                NEW_CRON="${NEW_CRON}
${QUEUE_LINE}"
                log "Adding queue:work cron entry (QUEUE_CONNECTION=database)"
            else
                log "queue:work cron entry already present"
            fi
        fi

        printf '%s\n' "$NEW_CRON" | sed '/^$/d' | crontab -
    else
        warn "No crontab command available. Add this manually (e.g. cPanel's Cron Jobs page):"
        echo "  $SCHEDULE_LINE"
        if [ -n "$QUEUE_LINE" ]; then
            echo "  $QUEUE_LINE"
        fi
    fi
else
    log "Skipping cron setup (--skip-cron)"
fi

log "Deploy complete."
