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

# Build a universal (arm64 + x86_64) Node.js runtime staged inside dist/runtime/node-universal.
# The script downloads the official macOS tarballs for each architecture, combines the Mach-O
# binaries with lipo, and leaves the result ready to be copied into the packaged app.

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

NODE_VERSION="${NODE_VERSION:-20.11.0}"
CACHE_ROOT="${NODE_CACHE_DIR:-$HOME/.cache/ra-h/node}"
OUTPUT_ROOT="${NODE_OUTPUT_DIR:-$REPO_ROOT/dist/runtime/node-universal}"
STAGING_ROOT="${NODE_STAGING_DIR:-$REPO_ROOT/dist/.staging/node-universal}"

ARM_ARCH="arm64"
X64_ARCH="x64"

ARM_TARBALL="node-v${NODE_VERSION}-darwin-${ARM_ARCH}"
X64_TARBALL="node-v${NODE_VERSION}-darwin-${X64_ARCH}"

ensure_prerequisites() {
  if ! command -v curl >/dev/null 2>&1; then
    echo "❌ curl not found. Install curl before running this script." >&2
    exit 1
  fi
  if ! command -v tar >/dev/null 2>&1; then
    echo "❌ tar not found. Install GNU tar / BSD tar before running this script." >&2
    exit 1
  fi
  if ! command -v rsync >/dev/null 2>&1; then
    echo "❌ rsync not found. Install rsync before running this script." >&2
    exit 1
  fi
  if ! command -v lipo >/dev/null 2>&1; then
    echo "❌ lipo not found. Install Xcode command line tools (xcode-select --install)." >&2
    exit 1
  fi
}

download_node() {
  local tarball_name="$1"
  local archive_dir="$CACHE_ROOT/${tarball_name}"
  if [ -x "${archive_dir}/bin/node" ]; then
    echo "✓ Node ${tarball_name} cached at ${archive_dir}"
    return
  fi

  mkdir -p "$CACHE_ROOT"
  local url="https://nodejs.org/dist/v${NODE_VERSION}/${tarball_name}.tar.gz"
  echo "⬇️  Downloading ${url}"
  tmp_archive="$(mktemp -d -t node-archive-XXXX)"
  trap 'rm -rf "$tmp_archive"' EXIT
  curl -fsSL "$url" | tar xz -C "$tmp_archive"
  mv "${tmp_archive}/${tarball_name}" "$archive_dir"
  rm -rf "$tmp_archive"
  trap - EXIT
  echo "✓ Node ${tarball_name} cached"
}

prepare_staging() {
  rm -rf "$OUTPUT_ROOT"
  rm -rf "$STAGING_ROOT"
  mkdir -p "$STAGING_ROOT"

  mkdir -p "$OUTPUT_ROOT"
}

copy_arch_tree() {
  local src="$1"
  local dest="$2"
  mkdir -p "$(dirname "$dest")"
  rsync -a --delete "$src/" "$dest/"
}

create_universal_binary() {
  local relative_path="$1"
  local arm_file="${STAGING_ROOT}/${ARM_TARBALL}/${relative_path}"
  local x64_file="${STAGING_ROOT}/${X64_TARBALL}/${relative_path}"
  local output_file="${OUTPUT_ROOT}/${relative_path}"

  if [ ! -f "$arm_file" ] || [ ! -f "$x64_file" ]; then
    return
  fi

  mkdir -p "$(dirname "$output_file")"
  lipo -create "$arm_file" "$x64_file" -output "$output_file"
}

walk_and_merge_binaries() {
  local pattern='*'
  (
    cd "${STAGING_ROOT}/${ARM_TARBALL}"
    find . -type f | while read -r relative; do
      if file "${relative}" 2>/dev/null | grep -q "Mach-O"; then
        create_universal_binary "${relative#./}"
      fi
    done
  )
}

finalize_assets() {
  # Copy everything from arm64 tree first (text files, scripts, npm, etc.)
  copy_arch_tree "${STAGING_ROOT}/${ARM_TARBALL}" "$OUTPUT_ROOT"
  # Merge Mach-O binaries/dylibs with lipo (overwrites the copies above).
  walk_and_merge_binaries

  chmod +x "$OUTPUT_ROOT/bin/node"
  echo "node" >"$OUTPUT_ROOT/.arch"
}

print_summary() {
  echo ""
  echo "✅ Universal Node runtime prepared:"
  echo "   - Version: v${NODE_VERSION}"
  echo "   - Output : ${OUTPUT_ROOT}"
  echo "   - Includes npm CLI at ${OUTPUT_ROOT}/lib/node_modules/npm/"
  echo ""
  lipo -info "$OUTPUT_ROOT/bin/node"
}

main() {
  ensure_prerequisites

  download_node "$ARM_TARBALL"
  download_node "$X64_TARBALL"

  prepare_staging

  copy_arch_tree "$CACHE_ROOT/$ARM_TARBALL" "$STAGING_ROOT/$ARM_TARBALL"
  copy_arch_tree "$CACHE_ROOT/$X64_TARBALL" "$STAGING_ROOT/$X64_TARBALL"

  finalize_assets
  print_summary
}

main "$@"
