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

usage() {
  cat <<'EOF'
Usage:
  deploy-node-release --site-root /var/www/node22/example.com --repo /srv/git/example.com [--ref main] --node-version 18|20|22 [--service node-site@example.com.service] [--install-cmd 'npm ci'] [--build-cmd 'npm run build'] [--shared-link REL ...] [--healthcheck-url URL] [--healthcheck-expect TEXT] [--healthcheck-insecure]
  deploy-node-release --help
EOF
}

site_root=""
repo=""
ref=""
node_version=""
service_name=""
install_cmd="npm ci"
build_cmd="npm run build"
healthcheck_url=""
healthcheck_expect=""
healthcheck_insecure=0
declare -a shared_links=()

while [[ $# -gt 0 ]]; do
  case "$1" in
    --site-root) site_root="${2:-}"; shift 2 ;;
    --repo) repo="${2:-}"; shift 2 ;;
    --ref) ref="${2:-}"; shift 2 ;;
    --node-version) node_version="${2:-}"; shift 2 ;;
    --service) service_name="${2:-}"; shift 2 ;;
    --install-cmd) install_cmd="${2:-}"; shift 2 ;;
    --build-cmd) build_cmd="${2:-}"; shift 2 ;;
    --shared-link) shared_links+=("${2:-}"); shift 2 ;;
    --healthcheck-url) healthcheck_url="${2:-}"; shift 2 ;;
    --healthcheck-expect) healthcheck_expect="${2:-}"; shift 2 ;;
    --healthcheck-insecure) healthcheck_insecure=1; shift ;;
    --help|-h) usage; exit 0 ;;
    *) echo "Unknown argument: $1" >&2; usage; exit 2 ;;
  esac
done

[[ -n "$site_root" && -n "$repo" && -n "$node_version" ]] || { usage; exit 2; }
[[ $(id -u) -eq 0 ]] || { echo 'Run as root' >&2; exit 1; }
[[ -d "$site_root/releases" && -d "$site_root/shared" ]] || { echo "Run init-release-layout first for $site_root" >&2; exit 1; }
[[ -d "$repo" ]] || { echo "Repo/source path not found: $repo" >&2; exit 1; }

copy_tree() {
  local src="$1" dst="$2"
  if command -v rsync >/dev/null 2>&1; then
    rsync -a --delete \
      --exclude '.git' \
      --exclude 'node_modules' \
      --exclude '.next/cache' \
      "$src/" "$dst/"
  else
    mkdir -p "$dst"
    tar -C "$src" \
      --exclude='.git' \
      --exclude='node_modules' \
      --exclude='.next/cache' \
      -cf - . | tar -C "$dst" -xf -
  fi
}

run_healthcheck() {
  [[ -n "$healthcheck_url" ]] || return 0
  local -a curl_args=(curl -fsS --max-time 10)
  if [[ "$healthcheck_insecure" -eq 1 ]]; then
    curl_args+=(-k)
  fi
  local attempt response tmp
  tmp=$(mktemp)
  for attempt in $(seq 1 20); do
    if response=$("${curl_args[@]}" "$healthcheck_url" 2>"$tmp"); then
      if [[ -z "$healthcheck_expect" ]] || grep -Fq "$healthcheck_expect" <<<"$response"; then
        printf '%s\n' "$response"
        rm -f "$tmp"
        return 0
      fi
      printf 'Healthcheck response missing expected text on attempt %s\n' "$attempt" >&2
    else
      cat "$tmp" >&2 || true
    fi
    sleep 1
  done
  rm -f "$tmp"
  return 1
}

build_shell_cmd() {
  local dir="$1" cmd="$2"
  printf 'cd %q; set -a; [ -f ./.env ] && . ./.env || true; [ -f ./.env.local ] && . ./.env.local || true; set +a; %s' "$dir" "$cmd"
}

if [[ -n "$ref" && -d "$repo/.git" ]]; then
  sudo -u git git -C "$repo" fetch --all --tags --prune || true
  sudo -u git git -C "$repo" checkout "$ref"
  if sudo -u git git -C "$repo" remote get-url origin >/dev/null 2>&1; then
    sudo -u git git -C "$repo" pull --ff-only origin "$ref"
  fi
fi

ts=$(date +%Y%m%d%H%M%S)
release_dir="$site_root/releases/$ts"
mkdir -p "$release_dir"
copy_tree "$repo" "$release_dir"
chown -R git:webread "$release_dir"
find "$release_dir" -type d -exec chmod 2750 {} +
find "$release_dir" -type f -exec chmod 0640 {} +
if command -v setfacl >/dev/null 2>&1; then
  setfacl -R -m u:nginx:rx,u:cicd:rwx,u:git:rwx,g:webread:rx "$release_dir"
  setfacl -R -d -m u:nginx:rx,u:cicd:rwx,u:git:rwx,g:webread:rx "$release_dir"
fi

printf '%s\n' "$node_version" > "$release_dir/.node-version"
chown git:webread "$release_dir/.node-version"
chmod 0640 "$release_dir/.node-version"

for rel in "${shared_links[@]}"; do
  shared_target="$site_root/shared/$rel"
  mkdir -p "$(dirname "$shared_target")"
  if [[ -d "$repo/$rel" || -d "$release_dir/$rel" ]]; then
    mkdir -p "$shared_target"
  elif [[ ! -e "$shared_target" ]]; then
    : > "$shared_target"
  fi
  rm -rf "$release_dir/$rel"
  mkdir -p "$(dirname "$release_dir/$rel")"
  ln -sfn "$shared_target" "$release_dir/$rel"
done

if [[ -n "$install_cmd" ]]; then
  cmd="$(build_shell_cmd "$release_dir" "$install_cmd")"
  /usr/local/bin/node-with-fnm --user git --version "$node_version" --shell-command "$cmd"
fi

if [[ -n "$build_cmd" ]]; then
  cmd="$(build_shell_cmd "$release_dir" "$build_cmd")"
  /usr/local/bin/node-with-fnm --user git --version "$node_version" --shell-command "$cmd"
fi

ln -sfn "$release_dir" "$site_root/current"
restorecon -RF "$site_root" >/dev/null 2>&1 || true

if [[ -n "$service_name" ]]; then
  systemctl restart "$service_name"
  systemctl is-active "$service_name" >/dev/null
fi

healthcheck_output=""
if [[ -n "$healthcheck_url" ]]; then
  healthcheck_output="$(run_healthcheck)" || {
    echo "Healthcheck failed: $healthcheck_url" >&2
    exit 1
  }
fi

echo "Deployed release: $release_dir"
echo "Current -> $(readlink -f "$site_root/current")"
if [[ -n "$service_name" ]]; then
  echo "Service restarted: $service_name"
fi
if [[ -n "$healthcheck_output" ]]; then
  echo "Healthcheck OK: $healthcheck_url"
  printf '%s\n' "$healthcheck_output"
fi
