#!/bin/sh
# ApiVault CLI installer
#
# Usage:
#   curl -fsSL https://apivault.tech/install.sh | sh
#   curl -fsSL https://apivault.tech/install.sh | sh -s -- --version 0.1.0
#
# Environment:
#   APIVAULT_VERSION   npm version tag (default: latest)
#   APIVAULT_PACKAGE   npm package name (default: apivault)
#   APIVAULT_INSTALL   install root (default: ~/.local/share/apivault)
#   APIVAULT_BIN       directory for the apivault symlink (default: ~/.local/bin)

set -eu

PACKAGE="${APIVAULT_PACKAGE:-apivault}"
VERSION="${APIVAULT_VERSION:-latest}"
INSTALL_DIR="${APIVAULT_INSTALL:-$HOME/.local/share/apivault}"
BIN_DIR="${APIVAULT_BIN:-$HOME/.local/bin}"

usage() {
  cat <<EOF
ApiVault CLI installer

Usage:
  curl -fsSL https://apivault.tech/install.sh | sh
  curl -fsSL https://apivault.tech/install.sh | sh -s -- [options]

Options:
  --version <tag>    Install a specific npm version (default: latest)
  -h, --help         Show this help

Environment:
  APIVAULT_VERSION, APIVAULT_PACKAGE, APIVAULT_INSTALL, APIVAULT_BIN
EOF
}

parse_args() {
  while [ $# -gt 0 ]; do
    case "$1" in
      --version)
        if [ $# -lt 2 ]; then
          echo "Error: --version requires a value" >&2
          exit 1
        fi
        VERSION="$2"
        shift 2
        ;;
      -h | --help)
        usage
        exit 0
        ;;
      *)
        echo "Error: unknown option: $1" >&2
        usage >&2
        exit 1
        ;;
    esac
  done
}

require_node() {
  if ! command -v node >/dev/null 2>&1; then
    echo "Error: Node.js 18.17 or newer is required." >&2
    echo "Install from https://nodejs.org/ and rerun the installer." >&2
    exit 1
  fi

  node - <<'NODE'
const [major, minor] = process.version.slice(1).split(".").map(Number);
if (major < 18 || (major === 18 && minor < 17)) {
  console.error(`Error: Node.js 18.17+ required (found ${process.version}).`);
  process.exit(1);
}
NODE
}

resolve_release() {
  export APIVAULT_PACKAGE="$PACKAGE"
  export APIVAULT_VERSION="$VERSION"

  eval "$(
    node - <<'NODE'
const pkg = process.env.APIVAULT_PACKAGE;
const requested = process.env.APIVAULT_VERSION;
const url =
  requested === "latest"
    ? `https://registry.npmjs.org/${pkg}/latest`
    : `https://registry.npmjs.org/${pkg}/${requested}`;

fetch(url)
  .then(async (response) => {
    if (!response.ok) {
      console.error(
        `Error: could not resolve ${pkg}@${requested} on npm (HTTP ${response.status}).`,
      );
      console.error("Publish the package first, or pick another --version.");
      process.exit(1);
    }

    const data = await response.json();
    const tarball = data.dist?.tarball;
    const version = data.version;

    if (!tarball || !version) {
      console.error("Error: npm registry response did not include a tarball.");
      process.exit(1);
    }

    const esc = (value) => `'${String(value).replace(/'/g, `'\\''`)}'`;
    process.stderr.write(`Installing ${pkg}@${version}\n`);
    console.log(`TARBALL_URL=${esc(tarball)}`);
    console.log(`RESOLVED_VERSION=${esc(version)}`);
  })
  .catch((error) => {
    console.error(`Error: ${error.message}`);
    process.exit(1);
  });
NODE
  )"
}

install_cli() {
  tmp_dir="$(mktemp -d 2>/dev/null || mktemp -d -t apivault-install)"
  trap 'rm -rf "$tmp_dir"' EXIT INT HUP TERM

  mkdir -p "$INSTALL_DIR" "$BIN_DIR"

  if ! curl -fsSL "$TARBALL_URL" -o "$tmp_dir/package.tgz"; then
    echo "Error: failed to download $PACKAGE@$RESOLVED_VERSION." >&2
    exit 1
  fi

  rm -rf "$INSTALL_DIR"/*
  mkdir -p "$INSTALL_DIR"
  tar -xzf "$tmp_dir/package.tgz" -C "$tmp_dir"
  cp -R "$tmp_dir/package/." "$INSTALL_DIR/"

  if [ ! -f "$INSTALL_DIR/dist/cli.js" ]; then
    echo "Error: installed package is missing dist/cli.js." >&2
    exit 1
  fi

  chmod +x "$INSTALL_DIR/dist/cli.js"
  ln -sf "$INSTALL_DIR/dist/cli.js" "$BIN_DIR/apivault"
}

verify_install() {
  if ! "$BIN_DIR/apivault" --version >/dev/null 2>&1; then
    echo "Error: apivault was installed but failed to run." >&2
    exit 1
  fi

  installed_version="$("$BIN_DIR/apivault" --version 2>/dev/null || true)"
  echo "apivault $installed_version installed to $BIN_DIR/apivault"
}

configure_path() {
  if command -v apivault >/dev/null 2>&1; then
    echo "Run 'apivault login' to connect your account."
    return
  fi

  echo ""
  echo "$BIN_DIR is not on your PATH."

  case "${SHELL:-}" in
    */zsh)
      rc_file="$HOME/.zshrc"
      ;;
    */bash)
      rc_file="$HOME/.bashrc"
      ;;
    *)
      rc_file=""
      ;;
  esac

  if [ -n "$rc_file" ] && ! grep -q "$BIN_DIR" "$rc_file" 2>/dev/null; then
    {
      echo ""
      echo "# Added by ApiVault CLI installer"
      echo "export PATH=\"$BIN_DIR:\$PATH\""
    } >>"$rc_file"
    echo "Added $BIN_DIR to PATH in $rc_file"
    echo "Restart your shell, then run: apivault login"
    return
  fi

  echo "Add this to your shell profile:"
  echo "  export PATH=\"$BIN_DIR:\$PATH\""
  echo "Then run: apivault login"
}

main() {
  parse_args "$@"
  require_node
  resolve_release
  install_cli
  verify_install
  configure_path
}

main "$@"
