#!/usr/bin/env bash
set -Eeuo pipefail

DOMAIN="safelife.website"
PORT="2084"
APP_NAME="safelife-api"
ARCHIVE_NAME="SafeLife-cPanel-OneClick-2084.zip"
INSTALL_ROOT="$(pwd -P)"
PAYLOAD="$INSTALL_ROOT/safelife-install-payload"

if [[ "$(id -u)" == "0" ]]; then
  if [[ -x /scripts/whoowns ]]; then
    CPANEL_USER="$(/scripts/whoowns "$DOMAIN" | tr -d '[:space:]')"
  else
    CPANEL_USER="$(stat -c '%U' "$INSTALL_ROOT")"
  fi
  if [[ -z "$CPANEL_USER" || "$CPANEL_USER" == "root" || "$CPANEL_USER" == "nobody" ]]; then
    printf 'Could not detect the cPanel account that owns %s.\n' "$DOMAIN" >&2
    exit 1
  fi
  CPANEL_HOME="$(getent passwd "$CPANEL_USER" | cut -d: -f6)"
  [[ -n "$CPANEL_HOME" ]] || CPANEL_HOME="/home/$CPANEL_USER"
  DOCROOT="$CPANEL_HOME/public_html"
  UAPI_USER_ARGUMENT=("--user=$CPANEL_USER")
else
  CPANEL_USER="$(id -un)"
  CPANEL_HOME="$HOME"
  DOCROOT="$INSTALL_ROOT"
  UAPI_USER_ARGUMENT=()
fi

APP_ROOT="$CPANEL_HOME/safelife"
BACKEND_DIR="$APP_ROOT/safelife-backend"

trap 'printf "\nInstallation failed on line %s. The installer files were kept for diagnosis.\n" "$LINENO" >&2' ERR

if [[ "$(basename "$INSTALL_ROOT")" != "public_html" ]]; then
  printf 'Run this command from the cPanel public_html directory.\n' >&2
  exit 1
fi

if [[ ! -d "$PAYLOAD/frontend" || ! -d "$PAYLOAD/backend" ]]; then
  printf 'Installation payload is missing. Extract %s first.\n' "$ARCHIVE_NAME" >&2
  exit 1
fi

command -v node >/dev/null 2>&1 || { printf 'Node.js is not available. Enable Node.js 20.19+ in cPanel.\n' >&2; exit 1; }
command -v npm >/dev/null 2>&1 || { printf 'npm is not available.\n' >&2; exit 1; }

NODE_VERSION="$(node -p 'process.versions.node')"
if ! node -e "const [a,b]=process.versions.node.split('.').map(Number);process.exit(a===22||(a===20&&b>=19)?0:1)"; then
  NVM_DIRECTORY="${NVM_DIR:-$HOME/.nvm}"
  if [[ -s "$NVM_DIRECTORY/nvm.sh" ]]; then
    printf 'Switching from Node.js %s to the supported Node.js 22 LTS...\n' "$NODE_VERSION"
    # shellcheck source=/dev/null
    source "$NVM_DIRECTORY/nvm.sh"
    nvm install 22
    nvm use 22
    hash -r
    NODE_VERSION="$(node -p 'process.versions.node')"
  else
    printf 'Node.js %s is unsupported here. Install/use Node.js 22 LTS and rerun.\n' "$NODE_VERSION" >&2
    exit 1
  fi
fi

if ! command -v pm2 >/dev/null 2>&1; then
  printf 'Installing PM2 for Node.js %s...\n' "$NODE_VERSION"
  npm install --global pm2
fi

printf '\nSafe Life one-command installer\n'
printf 'Domain: https://%s/\nAPI port: %s (internal)\n\n' "$DOMAIN" "$PORT"
printf 'cPanel account: %s\nDocument root: %s\n\n' "$CPANEL_USER" "$DOCROOT"

mkdir -p "$DOCROOT"
mkdir -p "$BACKEND_DIR"
FRESH_INSTALL=0

# Preserve an existing production .env during upgrades.
if [[ ! -f "$BACKEND_DIR/.env" ]]; then
  FRESH_INSTALL=1
  DEFAULT_DB_NAME="${CPANEL_USER}_safelife"
  DEFAULT_DB_USER="${CPANEL_USER}_safelife"

  read -r -p "MySQL database name [$DEFAULT_DB_NAME]: " DB_NAME
  DB_NAME="${DB_NAME:-$DEFAULT_DB_NAME}"
  read -r -p "MySQL username [$DEFAULT_DB_USER]: " DB_USER
  DB_USER="${DB_USER:-$DEFAULT_DB_USER}"
  read -r -s -p 'MySQL password: ' DB_PASSWORD
  printf '\n'
  [[ -n "$DB_PASSWORD" ]] || { printf 'MySQL password cannot be empty.\n' >&2; exit 1; }

  read -r -p 'Initial admin email [admin@safelife.website]: ' ADMIN_EMAIL
  ADMIN_EMAIL="${ADMIN_EMAIL:-admin@safelife.website}"
  read -r -s -p 'Initial admin password (minimum 12 characters): ' ADMIN_PASSWORD
  printf '\n'
  if (( ${#ADMIN_PASSWORD} < 12 )); then
    printf 'Admin password must contain at least 12 characters.\n' >&2
    exit 1
  fi

  if command -v uapi >/dev/null 2>&1; then
    printf 'Creating the MySQL database and user through cPanel UAPI when needed...\n'
    uapi --output=json "${UAPI_USER_ARGUMENT[@]}" Mysql create_database "name=$DB_NAME" >/dev/null 2>&1 || true
    uapi --output=json "${UAPI_USER_ARGUMENT[@]}" Mysql create_user "name=$DB_USER" "password=$DB_PASSWORD" >/dev/null 2>&1 || true
    uapi --output=json "${UAPI_USER_ARGUMENT[@]}" Mysql set_privileges_on_database \
      "user=$DB_USER" \
      "database=$DB_NAME" \
      'privileges=ALL%20PRIVILEGES' >/dev/null
  else
    printf 'cPanel UAPI is unavailable; using the existing MySQL database credentials.\n'
  fi

  DB_PASSWORD_ENCODED="$(printf '%s' "$DB_PASSWORD" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>process.stdout.write(encodeURIComponent(s)))")"
  JWT_SECRET="$(node -e "process.stdout.write(require('node:crypto').randomBytes(48).toString('hex'))")"

  umask 077
  cat > "$BACKEND_DIR/.env" <<ENVIRONMENT
DATABASE_URL=mysql://${DB_USER}:${DB_PASSWORD_ENCODED}@127.0.0.1:3306/${DB_NAME}
JWT_ACCESS_SECRET=${JWT_SECRET}
BODY_LIMIT=100kb
THROTTLE_TTL_MS=60000
THROTTLE_LIMIT=120
JWT_ACCESS_TTL=15m
REFRESH_TOKEN_DAYS=30
ADMIN_EMAIL=${ADMIN_EMAIL}
ADMIN_PASSWORD=${ADMIN_PASSWORD}
ADMIN_NAME=Safe Life Admin
ENVIRONMENT
  unset DB_PASSWORD DB_PASSWORD_ENCODED ADMIN_PASSWORD JWT_SECRET
  printf 'Created a protected production .env file.\n'
else
  printf 'Existing production .env found; keeping it unchanged.\n'
fi

printf 'Installing backend outside public_html...\n'
cp -a "$PAYLOAD/backend/." "$BACKEND_DIR/"
cp -f "$PAYLOAD/ecosystem.config.cjs" "$APP_ROOT/ecosystem.config.cjs"

cd "$BACKEND_DIR"
if ! npm ci; then
  printf 'npm ci rejected the lockfile on this npm version; repairing it with npm install...\n'
  npm install --no-audit --no-fund
fi
npm run db:deploy
npm run build
if [[ ! -f "$BACKEND_DIR/.seed-completed" ]]; then
  npm run db:seed
  touch "$BACKEND_DIR/.seed-completed"
fi
npm prune --omit=dev

printf 'Publishing frontend to public_html...\n'
cp -a "$PAYLOAD/frontend/." "$DOCROOT/"

cd "$APP_ROOT"
pm2 startOrReload ecosystem.config.cjs --env production --update-env
pm2 save

printf 'Checking the API on port %s...\n' "$PORT"
for attempt in {1..15}; do
  if node -e "fetch('http://127.0.0.1:${PORT}/api/v1/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"; then
    break
  fi
  if (( attempt == 15 )); then
    printf 'PM2 started, but the API health check failed. Run: pm2 logs %s\n' "$APP_NAME" >&2
    exit 1
  fi
  sleep 1
done

# Remove backend source payload and the uploaded archive from the public site
# only after installation and the health check have succeeded.
rm -rf -- "$PAYLOAD"
rm -f -- "$INSTALL_ROOT/$ARCHIVE_NAME"
rm -f -- "$INSTALL_ROOT/install.sh"

trap - ERR
printf '\nInstallation completed successfully.\n'
printf 'Website: https://%s/\n' "$DOMAIN"
printf 'API: https://%s/api/v1/health\n' "$DOMAIN"
printf 'PM2 process: %s on 127.0.0.1:%s\n' "$APP_NAME" "$PORT"
