#!/usr/bin/env bash
# Deploys a release zip with an atomic switch and automatic rollback if the health check fails.
#   release.sh cioclub-x.zip [--force "reason"]
# Caresoft practice: releases run during working hours only (Mon–Sat 10:00–18:00 IST) so the team is on hand.
set -euo pipefail
# shellcheck source=/dev/null
[ -f /etc/cioclub/release.env ] && . /etc/cioclub/release.env
ZIP="${1:?usage: release.sh <release.zip> [--force reason]}"
APP_DIR="${APP_DIR:-/var/www/cioclub}"
HEALTH_URL="${HEALTH_URL:-https://localhost/health?probe=live}"
DBA_ENV="${DBA_ENV:-/etc/cioclub/dba.env}"
KEEP="${KEEP_RELEASES:-5}"
FPM_SERVICE="${FPM_SERVICE:-php8.3-fpm}"

if [ "${2:-}" = "--force" ]; then
  echo "Working-hours guard overridden: ${3:-no reason given}" | tee -a "$APP_DIR/shared/storage/logs/release.log"
else
  dow=$(TZ=Asia/Kolkata date +%u); hr=$(TZ=Asia/Kolkata date +%H)
  if [ "$dow" = "7" ] || [ "$((10#$hr))" -lt 10 ] || [ "$((10#$hr))" -ge 18 ]; then
    echo "Outside working hours (Mon–Sat 10:00–18:00 IST). Re-run during working hours, or use --force \"reason\"." >&2; exit 2
  fi
fi

STAMP="$(date -u +%Y%m%d%H%M%S)"
REL="$APP_DIR/releases/$STAMP"
mkdir -p "$APP_DIR/releases" "$APP_DIR/shared/storage/logs" "$APP_DIR/shared/storage/vault" "$APP_DIR/shared/storage/cache" "$APP_DIR/shared/public/uploads"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
unzip -q "$ZIP" -d "$TMP"
SRC="$TMP/cioclub"; [ -d "$SRC" ] || SRC="$TMP"
mkdir -p "$REL" && cp -a "$SRC/." "$REL/"

# Shared, persistent pieces live outside the release folder.
[ -f "$APP_DIR/shared/config.php" ] || { echo "Missing $APP_DIR/shared/config.php" >&2; rm -rf "$REL"; exit 1; }
rm -rf "$REL/storage" "$REL/public/uploads" "$REL/public/router.php" "$REL/config.php"
ln -s "$APP_DIR/shared/config.php" "$REL/config.php"
ln -s "$APP_DIR/shared/storage" "$REL/storage"
ln -s "$APP_DIR/shared/public/uploads" "$REL/public/uploads"
[ -f "$APP_DIR/shared/public/uploads/.htaccess" ] || cp "$SRC/public/uploads/.htaccess" "$APP_DIR/shared/public/uploads/.htaccess"

# Syntax check every PHP file before going live.
bad=0; while IFS= read -r -d '' f; do php -l "$f" >/dev/null 2>&1 || { echo "PHP syntax error: $f" >&2; bad=1; }; done < <(find "$REL" -name '*.php' -not -path '*/storage/*' -print0)
[ "$bad" = 0 ] || { rm -rf "$REL"; exit 1; }

# Database migrations (forward-only, additive) with the DBA account kept in a root-only file.
if [ -r "$DBA_ENV" ]; then
  # shellcheck source=/dev/null
  . "$DBA_ENV"
  MIG="$(php "$REL/bin/install.php" --dba-user="$DBA_USER" --dba-pass="$DBA_PASS" 2>&1)" || true
  echo "$MIG" | grep -E 'apply' || true
  if echo "$MIG" | grep -qE 'FAILED|Could not connect|Refusing'; then echo "$MIG" >&2; echo "Migration failed; release aborted, live site unchanged." >&2; rm -rf "$REL"; exit 1; fi
fi

PREV="$(readlink -f "$APP_DIR/current" 2>/dev/null || true)"
ln -sfn "$REL" "$APP_DIR/current.new" && mv -Tf "$APP_DIR/current.new" "$APP_DIR/current"
[ "${SKIP_RELOAD:-}" = "1" ] || systemctl reload "$FPM_SERVICE"

sleep "${HEALTH_WAIT:-2}"
if curl -fsS -k --max-time 10 "$HEALTH_URL" >/dev/null; then
  echo "$(date -u +%FT%TZ) released $STAMP" | tee -a "$APP_DIR/shared/storage/logs/release.log"
else
  echo "Health check failed; rolling back" >&2
  if [ -n "$PREV" ] && [ -d "$PREV" ] && [ "$PREV" != "$REL" ]; then
    ln -sfn "$PREV" "$APP_DIR/current.new" && mv -Tf "$APP_DIR/current.new" "$APP_DIR/current"
    [ "${SKIP_RELOAD:-}" = "1" ] || systemctl reload "$FPM_SERVICE"
  fi
  echo "$(date -u +%FT%TZ) FAILED $STAMP, rolled back to ${PREV##*/}" >> "$APP_DIR/shared/storage/logs/release.log"
  rm -rf "$REL"
  exit 1
fi
ls -1dt "$APP_DIR"/releases/*/ | tail -n +"$((KEEP + 1))" | xargs -r rm -rf
