#!/bin/sh

set -e

# /cli_versions names the current MotherDuck CLI release and the base URL its
# binaries are served from, so pointing MD_API_HOST at another environment is
# enough to install that environment's build. Our own tests use that.
MD_API_HOST="${MD_API_HOST:-api.motherduck.com}"
MD_DEFAULT_DOWNLOAD_BASE_URL="https://cli.motherduck.com"

# The CLI is installed per-user, so nothing here needs root.
MD_CLI_BIN_DIR="${HOME}/.motherduck/bin"

is_interactive() {
  # Explicit overrides — common CI convention + a MotherDuck-specific one
  if [ -n "${CI:-}" ] || [ -n "${MOTHERDUCK_NONINTERACTIVE:-}" ]; then
    return 1
  fi
  # Can we actually open /dev/tty for reading? (this is what `read ... </dev/tty` needs)
  if ! (exec </dev/tty) 2>/dev/null; then
    return 1
  fi
  return 0
}

find_rc_file() {
  shell_name=$(basename "$SHELL")
  rc_files=""
  case "$shell_name" in
  bash) rc_files="$HOME/.bashrc $HOME/.bash_profile $HOME/.profile" ;;
  zsh) rc_files="$HOME/.zshrc $HOME/.zprofile" ;;
  fish) rc_files="$HOME/.config/fish/config.fish" ;;
  ksh | mksh) rc_files="$HOME/.kshrc $HOME/.profile" ;;
  tcsh | csh) rc_files="$HOME/.tcshrc $HOME/.cshrc" ;;
  *) rc_files="$HOME/.profile" ;;
  esac

  # Return first existing file
  for rc in $rc_files; do
    if [ -f "$rc" ]; then
      echo "$rc"
      return 0
    fi
  done
}

validate_md_connection() {
  CLI_PATH="${1}"

  if ! DATABASES=$(${CLI_PATH} -list -c 'LOAD motherduck' -c 'PRAGMA md_connect;SHOW DATABASES' 2>&1); then
    echo "❌ Failed to validate MotherDuck connection, please contact support@motherduck.com"
    echo "Output from DuckDB:"
    echo "$DATABASES"
    exit 1
  fi

  NB_DBS=$(echo "$DATABASES" | wc -l | xargs)
  PLURAL=""
  if [ "$NB_DBS" -gt 1 ]; then
    PLURAL="s"
  fi

  echo "🔗 Validated MotherDuck connection successfully; found ${NB_DBS} database${PLURAL}."
}

# The published binaries are named with Node's platform/arch spelling rather
# than uname's.
md_cli_target_key() {
  os=$(uname -s)
  arch=$(uname -m)

  case "$os" in
  Darwin) os_key="darwin" ;;
  Linux)
    os_key="linux"
    # Only glibc builds are published. A glibc binary on musl fails at exec time
    # with a missing-loader error that says nothing about why.
    for musl_loader in /lib/ld-musl-*; do
      if [ -e "$musl_loader" ]; then
        echo >&2 "❌ The MotherDuck CLI does not support musl-based Linux (such as Alpine) yet."
        return 1
      fi
    done
    ;;
  *)
    echo >&2 "❌ The MotherDuck CLI does not support '${os}'. Please contact support@motherduck.com"
    return 1
    ;;
  esac

  case "$arch" in
  arm64 | aarch64) arch_key="arm64" ;;
  x86_64 | amd64) arch_key="x64" ;;
  *)
    echo >&2 "❌ The MotherDuck CLI does not support the '${arch}' architecture. Please contact support@motherduck.com"
    return 1
    ;;
  esac

  echo "${os_key}-${arch_key}"
}

# Reads one string field out of the cli_versions document. jq is not present on a
# bare machine, and that document is generated from a fixed set of string
# fields, so this does not need to parse arbitrary JSON.
json_field() {
  printf '%s\n' "$1" |
    sed -n "s/.*\"${2}\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" |
    head -n 1
}

# macOS ships shasum and no sha256sum; Linux ships sha256sum.
sha256_hex() {
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | cut -d' ' -f1
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$1" | cut -d' ' -f1
  else
    return 1
  fi
}

# Appends the install directory to the user's shell rc file, unless it is already
# on PATH or a previous run already added it.
add_cli_dir_to_path() {
  case ":${PATH}:" in
  *":${MD_CLI_BIN_DIR}:"*)
    return 0
    ;;
  esac

  RC_FILE=$(find_rc_file)
  if [ -z "$RC_FILE" ]; then
    echo "🐣 Could not find a shell rc file; add '${MD_CLI_BIN_DIR}' to your PATH manually."
    return 0
  fi

  if grep -q "# motherduck cli begin" "$RC_FILE"; then
    echo "✅ '${MD_CLI_BIN_DIR}' is already added to PATH in '${RC_FILE}'."
    return 0
  fi

  case "$(basename "${SHELL:-sh}")" in
  fish) path_line="fish_add_path ${MD_CLI_BIN_DIR}" ;;
  tcsh | csh) path_line="setenv PATH \"${MD_CLI_BIN_DIR}:\$PATH\"" ;;
  *) path_line="export PATH=\"${MD_CLI_BIN_DIR}:\$PATH\"" ;;
  esac

  {
    echo
    echo "# motherduck cli begin"
    echo "# Added by MotherDuck install script on $(date)"
    echo "$path_line"
    echo "# motherduck cli end"
  } >>"$RC_FILE"
  echo "✅ Added '${MD_CLI_BIN_DIR}' to PATH in '${RC_FILE}', please restart your shell or run 'source ${RC_FILE}' to load it."
}

install_motherduck_cli() {
  TARGET_KEY=$(md_cli_target_key) || exit 1

  VERSIONS_URL="https://${MD_API_HOST}/cli_versions"
  if ! CLI_VERSIONS=$(curl -sfL --max-time 10 "${VERSIONS_URL}"); then
    echo "❌ Could not fetch the MotherDuck CLI version list from '${VERSIONS_URL}'"
    exit 1
  fi

  # The pointer names where this environment's binaries live, so an environment
  # can be served from another bucket or host without changing this script. A
  # pointer that names none falls back to prod.
  DOWNLOAD_BASE_URL=$(json_field "$CLI_VERSIONS" download_base_url)
  DOWNLOAD_BASE_URL="${DOWNLOAD_BASE_URL:-${MD_DEFAULT_DOWNLOAD_BASE_URL}}"
  DOWNLOAD_BASE_URL="${DOWNLOAD_BASE_URL%/}"

  if [ -z "${MOTHERDUCK_VERSION}" ]; then
    MD_VERSION=$(json_field "$CLI_VERSIONS" latest)
  else
    MD_VERSION=${MOTHERDUCK_VERSION}
  fi

  if [ -z "$MD_VERSION" ]; then
    echo "❌ Could not determine which MotherDuck CLI version to install, please contact support@motherduck.com"
    exit 1
  fi

  CLI_BIN="${MD_CLI_BIN_DIR}/motherduck"
  if [ -x "$CLI_BIN" ] && [ "$("$CLI_BIN" --version 2>/dev/null)" = "$MD_VERSION" ]; then
    echo "🦆 MotherDuck CLI version '${MD_VERSION}' is already installed at '${CLI_BIN}'"
    add_cli_dir_to_path
    return 0
  fi

  BINARY_URL="${DOWNLOAD_BASE_URL}/${MD_VERSION}/${TARGET_KEY}/motherduck"

  mkdir -p "$MD_CLI_BIN_DIR"
  # Download beside the final path rather than into a temp directory: the move
  # into place has to be a rename, and a rename across filesystems is a copy,
  # which fails on Linux when the binary being replaced is running.
  TMP_BIN="${MD_CLI_BIN_DIR}/.motherduck-download.$$"
  trap 'rm -f "${TMP_BIN}" "${TMP_BIN}.sha256"' EXIT

  if ! curl -sfL --retry 3 -o "${TMP_BIN}" "${BINARY_URL}"; then
    echo "❌ Failed to download the MotherDuck CLI from '${BINARY_URL}'"
    exit 1
  fi
  if ! curl -sfL --retry 3 -o "${TMP_BIN}.sha256" "${BINARY_URL}.sha256"; then
    echo "❌ Failed to download the MotherDuck CLI checksum from '${BINARY_URL}.sha256'"
    exit 1
  fi

  EXPECTED_SHA=$(cut -d' ' -f1 <"${TMP_BIN}.sha256")
  if ! ACTUAL_SHA=$(sha256_hex "${TMP_BIN}"); then
    echo "❌ Neither sha256sum nor shasum could be found; cannot verify the download. Aborting."
    exit 1
  fi
  if [ -z "$EXPECTED_SHA" ] || [ "$EXPECTED_SHA" != "$ACTUAL_SHA" ]; then
    echo "❌ Checksum mismatch for the downloaded MotherDuck CLI, please contact support@motherduck.com"
    exit 1
  fi

  chmod 755 "${TMP_BIN}"
  mv -f "${TMP_BIN}" "${CLI_BIN}"
  trap - EXIT
  rm -f "${TMP_BIN}.sha256"

  if ! INSTALLED_VERSION=$("${CLI_BIN}" --version 2>&1); then
    echo "❌ The installed MotherDuck CLI failed to run, please contact support@motherduck.com"
    echo "Output from the MotherDuck CLI:"
    echo "$INSTALLED_VERSION"
    exit 1
  fi
  if [ "$INSTALLED_VERSION" != "$MD_VERSION" ]; then
    echo "❌ The installed MotherDuck CLI reports version '${INSTALLED_VERSION}', expected '${MD_VERSION}'. Please contact support@motherduck.com"
    exit 1
  fi

  echo "🦆 Installed MotherDuck CLI version '${MD_VERSION}' successfully at '${CLI_BIN}'"
  add_cli_dir_to_path
}

# Wrap in main to prevent partial execution if download is partial for some reason
main() {
  command -v curl >/dev/null 2>&1 || {
    echo >&2 "Required tool curl could not be found. Aborting."
    exit 1
  }

  if [ -n "${SKIP_DUCKDB_CLI}" ] && [ -n "${SKIP_MOTHERDUCK_CLI}" ]; then
    echo >&2 "Both SKIP_DUCKDB_CLI and SKIP_MOTHERDUCK_CLI are set, there is nothing to install. Aborting."
    exit 1
  fi

  if [ -z "${SKIP_MOTHERDUCK_CLI}" ]; then
    install_motherduck_cli
  fi

  if [ -n "${SKIP_DUCKDB_CLI}" ]; then
    echo
    echo "⏭️  Skipping the DuckDB CLI install because SKIP_DUCKDB_CLI is set."
    echo "   Run 'motherduck login' to authenticate the MotherDuck CLI."
    exit 0
  fi

  echo

  # Find out latest version if not set in the environment
  if [ -z "${DUCKDB_VERSION}" ]; then
    VER=$(curl -s https://api.motherduck.com/latest_supported_duckdb_version.txt)
  else
    VER=${DUCKDB_VERSION}
  fi

  if ! curl -s https://install.duckdb.org | DUCKDB_VERSION="${VER}" sh; then
    echo "DuckDB installation failed"
    exit 1
  fi

  echo
  echo "🦆 Installed DuckDB version '${VER}' successfully"

  # Try to find the installed DuckDB binary
  CLI_PATH="${HOME}/.duckdb/cli/${VER}/duckdb"
  if [ ! -x "${CLI_PATH}" ]; then
    echo "DuckDB CLI not found at expected path '${CLI_PATH}'"
    exit 1
  fi

  if ! ${CLI_PATH} -c "INSTALL motherduck"; then
    echo "Failed to install MotherDuck extension"
    exit 1
  fi

  echo "💾 Installed MotherDuck extension successfully"

  # If a MotherDuck token already exists in the environment, just exit. Both
  # spellings are accepted because the extension resolves the name with
  # md::getenv_anycase, so DuckDB sees an uppercase-only token too.
  if [ -n "${motherduck_token:-}" ] || [ -n "${MOTHERDUCK_TOKEN:-}" ]; then
    validate_md_connection "${CLI_PATH}"
    exit 0
  fi

  # Checking if `motherduck_token` exists in bashrc
  RC_FILE=$(find_rc_file)
  if [ -z "$RC_FILE" ]; then
    echo "🐣 Could not find a shell rc file to add the MotherDuck token to, exiting."
    exit 0
  fi

  # Check if token already exists in rc file. Anchored at the start of the line
  # so a commented-out export is not mistaken for a live one: it would be
  # reported as already set, then eval'd to nothing, leaving the connection
  # check to fail with no token. Only the two spellings the extension reads
  # count, so a mixed-case name is not taken for a token DuckDB would use.
  token_export_pattern='^[[:space:]]*export[[:space:]]+(motherduck_token|MOTHERDUCK_TOKEN)='
  export_token_line=$(grep -E "$token_export_pattern" "$RC_FILE" || true)
  if [ -n "$export_token_line" ]; then
    echo "✅ MotherDuck token already exists in '$RC_FILE', not adding again."

    NL=$(echo "$export_token_line" | wc -l)
    if [ "${NL}" -ne 1 ]; then
      echo "❌ Unexpectedly found $NL lines with motherduck_token in '$RC_FILE'. Will not check connection."
      exit 1
    fi

    eval "$export_token_line"
    validate_md_connection "${CLI_PATH}"
    exit 0
  fi

  echo "🔐 Your environment doesn't have a MotherDuck token yet. Adding one would remove the need to log in via a web browser every time."

  if ! is_interactive; then
    echo "   Non-interactive shell detected (no tty available), skipping token setup. Set 'motherduck_token' in your environment to enable this automatically."
    exit 0
  fi

  echo "   Do you want to fetch one and add it to '${RC_FILE}'?"
  while true; do
    printf "1) Yes\n2) No\n"
    printf "Please choose 1 or 2: "
    read -r yn </dev/tty

    case $yn in
    1 | Yes | yes | y | Y) break ;;
    2 | No | no | n | N)
      echo "Exiting without adding token."
      exit 0
      ;;
    *) echo "Invalid choice. Please enter 1 or 2." ;;
    esac
  done

  # Redirecting token to temp file.
  token_file=$(mktemp)

  ${CLI_PATH} -list -c 'LOAD motherduck' -c 'PRAGMA md_connect;PRAGMA print_md_token;' 2>&1 |
    tee "$token_file" |
    grep -Evi 'token|^[[:space:]]*level\|message[[:space:]]*$|^[[:space:]]*success[[:space:]]*$|^[[:space:]]*$'

  # Grep token
  export_token_line=$(grep 'export motherduck_token=' "$token_file")
  rm "$token_file"

  # Make sure it is one line
  NL=$(echo "$export_token_line" | wc -l)
  if [ "${NL}" -ne 1 ]; then
    echo "❌ Failed to get MotherDuck token, unexpected output, please contact support@motherduck.com"
    exit 1
  fi

  {
    echo
    echo "# motherduck begin"
    echo "# Added by MotherDuck install script on $(date)"
    echo "$export_token_line"
    echo "# motherduck end"
  } >>"$RC_FILE"
  echo "✅ Added MotherDuck token to '$RC_FILE'; please restart your shell or run 'source $RC_FILE' to load it."

  eval "$export_token_line"
  validate_md_connection "${CLI_PATH}"
}
main
