#!/usr/bin/env bash
# Fast on-host release deploy (same model as climpme/web/deploy/deploy.sh).
set -Eeuo pipefail

APP_ROOT="${LADILL_APP_ROOT:-/var/www/ladill-care}"
RELEASES_DIR="$APP_ROOT/releases"
SHARED_DIR="$APP_ROOT/shared"
CURRENT_LINK="$APP_ROOT/current"
RELEASE_ARCHIVE="${LADILL_RELEASE_ARCHIVE:-/tmp/ladill-release.tgz}"
KEEP_RELEASES="${LADILL_KEEP_RELEASES:-5}"

STAMP="$(date +%Y%m%d%H%M%S)"
NEW_RELEASE="$RELEASES_DIR/$STAMP"

log() {
  printf '[%s] %s\n' "$(date '+%F %T')" "$*"
}

bootstrap_shared_env() {
  local candidate=""

  if [ -f "$SHARED_DIR/.env" ]; then
    return 0
  fi

  for candidate in "$CURRENT_LINK/.env" "$APP_ROOT/.env"; do
    if [ -f "$candidate" ]; then
      cp -fL "$candidate" "$SHARED_DIR/.env"
      log "Bootstrapped shared .env from $candidate"
      return 0
    fi
  done

  return 1
}

ensure_writable_paths() {
  local paths=("$@")

  [ "${#paths[@]}" -gt 0 ] || return 0

  chmod -R ug+rwX "${paths[@]}" 2>/dev/null || true

  if command -v find >/dev/null 2>&1; then
    find "${paths[@]}" -type d -exec chmod ug+rwx {} + 2>/dev/null || true
    find "${paths[@]}" -type d -exec chmod g+s {} + 2>/dev/null || true
  fi
}

normalize_shared_permissions() {
  local owner="${LADILL_DEPLOY_USER:-deploy}"
  local group="${LADILL_DEPLOY_GROUP:-www-data}"
  local shared_paths=("$SHARED_DIR/storage" "$SHARED_DIR/bootstrap-cache")

  id -u "$owner" >/dev/null 2>&1 || return 0
  getent group "$group" >/dev/null 2>&1 || return 0

  if chown -R "$owner:$group" "${shared_paths[@]}" 2>/dev/null; then
    :
  elif command -v sudo >/dev/null 2>&1; then
    sudo -n chown -R "$owner:$group" "${shared_paths[@]}" 2>/dev/null || true
  fi

  ensure_writable_paths "${shared_paths[@]}"
}

run_artisan() {
  local command="$1"

  if [ -f "$NEW_RELEASE/artisan" ]; then
    (cd "$NEW_RELEASE" && php artisan ${command})
  fi
}

uses_sqlite() {
  [ -f "$SHARED_DIR/.env" ] && grep -Eq '^DB_CONNECTION=sqlite([[:space:]]*)$' "$SHARED_DIR/.env"
}

sqlite_database_setting() {
  [ -f "$SHARED_DIR/.env" ] || return 1

  grep -E '^DB_DATABASE=' "$SHARED_DIR/.env" | tail -n 1 | cut -d= -f2- | sed -e 's/^"//' -e 's/"$//' -e "s/^'//" -e "s/'$//"
}

prepare_sqlite_database() {
  local configured_path=""
  local db_name=""
  local shared_sqlite_path=""
  local current_sqlite_path=""
  local release_sqlite_path=""

  configured_path="$(sqlite_database_setting || true)"
  [ -n "$configured_path" ] || configured_path="database/database.sqlite"

  if [[ "$configured_path" = /* ]]; then
    shared_sqlite_path="$configured_path"
    mkdir -p "$(dirname "$shared_sqlite_path")"
  else
    db_name="$(basename "$configured_path")"
    shared_sqlite_path="$SHARED_DIR/database/$db_name"
    current_sqlite_path="$CURRENT_LINK/$configured_path"
    release_sqlite_path="$NEW_RELEASE/$configured_path"

    mkdir -p "$SHARED_DIR/database" "$(dirname "$release_sqlite_path")"

    if [ ! -f "$shared_sqlite_path" ]; then
      if [ -f "$current_sqlite_path" ]; then
        cp -fL "$current_sqlite_path" "$shared_sqlite_path"
        log "Bootstrapped shared sqlite database from $current_sqlite_path"
      elif [ -f "$APP_ROOT/$configured_path" ]; then
        cp -fL "$APP_ROOT/$configured_path" "$shared_sqlite_path"
        log "Bootstrapped shared sqlite database from $APP_ROOT/$configured_path"
      else
        touch "$shared_sqlite_path"
        log "Created shared sqlite database at $shared_sqlite_path"
      fi
    fi

    rm -f "$release_sqlite_path"
    ln -sfn "$shared_sqlite_path" "$release_sqlite_path"
    ensure_writable_paths "$SHARED_DIR/database"
    return 0
  fi

  if [ ! -f "$shared_sqlite_path" ]; then
    touch "$shared_sqlite_path"
    log "Created shared sqlite database at $shared_sqlite_path"
  fi

  ensure_writable_paths "$(dirname "$shared_sqlite_path")"
}

resolve_npm() {
  if command -v npm >/dev/null 2>&1; then command -v npm; return 0; fi
  local candidate
  for candidate in "${NODE_ROOT:-/tmp/ladill-node-22-r1}/bin/npm" /tmp/ladill-node-*/bin/npm; do
    [ -x "$candidate" ] && { echo "$candidate"; return 0; }
  done
  return 1
}

# Ensure compiled Vite assets exist in the release. CI builds them into the
# archive; this only (re)builds when the manifest is missing — e.g. a manual
# on-host deploy — so a release without assets is never promoted (which would
# make every @vite page 500). Runs before the current symlink is switched, so a
# failure here leaves the previous good release live.
build_frontend_assets() {
  if [ -f "$NEW_RELEASE/public/build/manifest.json" ]; then
    log "Frontend assets present — skipping vite build"
    return 0
  fi

  [ -f "$NEW_RELEASE/package.json" ] || { log "No package.json — skipping vite build"; return 0; }

  local npm_bin
  if ! npm_bin="$(resolve_npm)"; then
    echo "ERROR: vite manifest missing and npm not found; cannot build frontend assets (pages would 500)." >&2
    return 1
  fi

  log "Building frontend assets with $npm_bin"
  (
    cd "$NEW_RELEASE"
    export PATH="$(dirname "$npm_bin"):$PATH"
    if [ -f package-lock.json ]; then
      npm ci --no-audit --no-fund
    else
      npm install --no-audit --no-fund
    fi
    npm run build
  )
}

log "Preparing release $STAMP"
[ -f "$RELEASE_ARCHIVE" ] || { echo "Release archive not found: $RELEASE_ARCHIVE" >&2; exit 1; }

mkdir -p "$RELEASES_DIR" "$SHARED_DIR" "$NEW_RELEASE"
tar -xzf "$RELEASE_ARCHIVE" -C "$NEW_RELEASE"
chmod -R u+rwX "$NEW_RELEASE" 2>/dev/null || true

log "Linking shared paths"
mkdir -p "$SHARED_DIR/storage/"{app/public,app/private,framework/{cache/data,sessions,testing,views},logs}
mkdir -p "$SHARED_DIR/bootstrap-cache"
normalize_shared_permissions

if bootstrap_shared_env; then
  ln -sfn "$SHARED_DIR/.env" "$NEW_RELEASE/.env"
else
  echo "Missing deployment environment file. Expected $SHARED_DIR/.env or an existing $CURRENT_LINK/.env / $APP_ROOT/.env to bootstrap from." >&2
  exit 1
fi

rm -rf "$NEW_RELEASE/storage"
ln -sfn "$SHARED_DIR/storage" "$NEW_RELEASE/storage"
mkdir -p "$NEW_RELEASE/bootstrap"
rm -rf "$NEW_RELEASE/bootstrap/cache"
ln -sfn "$SHARED_DIR/bootstrap-cache" "$NEW_RELEASE/bootstrap/cache"
normalize_shared_permissions

if uses_sqlite; then
  log "Preparing shared sqlite database"
  prepare_sqlite_database
  if id -u www-data >/dev/null 2>&1; then
    chgrp www-data "$SHARED_DIR/database" 2>/dev/null || true
    find "$SHARED_DIR/database" -type f -exec chgrp www-data {} + 2>/dev/null || true
    find "$SHARED_DIR/database" -type d -exec chgrp www-data {} + 2>/dev/null || true
  fi
fi

log "Installing PHP dependencies"
if [ -f "$NEW_RELEASE/composer.json" ]; then
  if [ "$(id -u)" -eq 0 ]; then
    export COMPOSER_ALLOW_SUPERUSER=1
  fi
  export COMPOSER_HOME="${COMPOSER_HOME:-/home/deploy/.composer}"
  (
    cd "$NEW_RELEASE"
    composer install \
      --no-dev \
      --prefer-dist \
      --no-interaction \
      --no-progress \
      --optimize-autoloader \
      --classmap-authoritative
  )
fi

log "Ensuring frontend assets"
if ! build_frontend_assets; then
  echo "Frontend asset build failed — keeping previous release live" >&2
  exit 1
fi

log "Running migrations"
if ! run_artisan "migrate --force"; then
  log "Migration failed — clearing shared bootstrap cache"
  run_artisan "optimize:clear" || true
  exit 1
fi

switch_current_release() {
  # Remove a root-owned symlink left from manual bootstrap so deploy can replace it.
  if [ -e "$CURRENT_LINK" ] || [ -L "$CURRENT_LINK" ]; then
    rm -f "$CURRENT_LINK" 2>/dev/null || true
  fi

  if ln -sfnT "$NEW_RELEASE" "$CURRENT_LINK" 2>/dev/null; then
    return 0
  fi

  if command -v sudo >/dev/null 2>&1 && sudo -n rm -f "$CURRENT_LINK" && sudo -n ln -sfnT "$NEW_RELEASE" "$CURRENT_LINK"; then
    return 0
  fi

  echo "Unable to update current release symlink at $CURRENT_LINK (ensure $APP_ROOT is owned by the deploy user)" >&2
  return 1
}

log "Switching current release"
if ! switch_current_release; then
  echo "Failed to point $CURRENT_LINK at $NEW_RELEASE" >&2
  exit 1
fi

LIVE_REV="$(cat "$CURRENT_LINK/REVISION" 2>/dev/null || echo unknown)"
log "Live release: $STAMP (revision $LIVE_REV)"

log "Optimizing Laravel"
if [ -L "$CURRENT_LINK" ] && [ -f "$CURRENT_LINK/artisan" ]; then
  (
    cd "$CURRENT_LINK"
    php artisan storage:link || true
    php artisan optimize:clear || true
    php artisan config:cache || true
    php artisan route:cache || true
    php artisan view:cache || true
  )
fi

log "Reloading services"
if command -v systemctl >/dev/null 2>&1; then
  systemctl reload php8.4-fpm 2>/dev/null || sudo -n systemctl reload php8.4-fpm
  systemctl reload nginx 2>/dev/null || sudo -n systemctl reload nginx
fi

log "Restarting queues"
if [ -L "$CURRENT_LINK" ] && [ -f "$CURRENT_LINK/artisan" ]; then
  (cd "$CURRENT_LINK" && php artisan queue:restart) || true
fi
if command -v supervisorctl >/dev/null 2>&1; then
  supervisorctl restart ladill-care-worker:* 2>/dev/null || true
fi

log "Cleaning old releases"
mapfile -t OLD_RELEASES < <(ls -1dt "$RELEASES_DIR"/* 2>/dev/null | tail -n "+$((KEEP_RELEASES + 1))" || true)
if [ "${#OLD_RELEASES[@]}" -gt 0 ]; then
  chmod -R u+rwX "${OLD_RELEASES[@]}" 2>/dev/null || true
  rm -rf "${OLD_RELEASES[@]}" 2>/dev/null || true
fi

log "Deploy completed: $STAMP"
