_ns_ohk2aaDu() {
  local is_dev=$1; shift # "true" or "false"
  if $is_dev; then
    local arg_suffix="^*"
  else
    local arg_suffix=""
  fi
  local args=()
  local pkg
  for pkg in "$@"; do
    if [[ "$pkg" = -- ]]; then
      break
    fi
    if [[ "$pkg" =~ ^.*[#:/].*$ ]]; then
      args+=("${pkg}${arg_suffix}")
    else
      args+=(nixpkgs#"${pkg}${arg_suffix}")
    fi
  done

  # Run anything after -- as a command
  local cmd=("$@")
  while [[ "${#cmd[@]}" -gt 1 && "${cmd[0]}" != "--" ]]; do
    cmd=("${cmd[@]:1}")
  done
  cmd=("${cmd[@]:1}")
  if [[ ${#cmd[@]} -gt 0 ]]; then
    (
      _ns_ohk2aaDu false "${args[@]}"
      set -x
      "${cmd[@]}"
    )
    return $?
  fi

  # echo current shell
  # TODO: deduplicate repeats, 'x^*' should replace 'x'
  # echo >&2 -n + exec nix shell
  echo >&2 -n + ns
  if [[ -v _PREVIOUS_NS_ARGS  ]]; then
    echo >&2 -n " ${_PREVIOUS_NS_ARGS:-}"
  fi
  >&2 printf " %q"  "${args[@]}"
  echo >&2

  if [[ "${#args[@]}" -eq 0 ]]; then
    return
  fi

  local outpaths="$(
    export NIXPKGS_ALLOW_BROKEN
    export NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM
    export NIXPKGS_ALLOW_UNFREE
    export NIXPKGS_ALLOW_INSECURE
    if [[ -t 2 ]] && command -v nom >/dev/null ]]; then
      nom build --extra-experimental-features "nix-command flakes" --impure "${args[@]}" --print-out-paths --no-link
    else
      nix build --extra-experimental-features "nix-command flakes" --impure "${args[@]}" --print-out-paths --no-link
    fi
  )"
  if [[ $? -ne 0 || -z "$outpaths" ]]; then
    return
  fi

  local -a storepaths_native=()
  local -a storepaths_build=()
  readarray -d $'\n' -t storepaths_native <<<"$outpaths"
  readarray -d $'\n' -t storepaths_build  <<<"$outpaths"

  # traverse propagated inputs
  # this is safe since the store paths form a DAG
  # TODO: rethink native logic
  local -i idx=-1
  while [[ $((++idx)) -lt "${#storepaths_native[@]}" ]]; do
    if [[ -f "${storepaths_native[$idx]}"/nix-support/propagated-native-build-inputs ]]; then
      storepaths_native+=($(cat "${storepaths_native[$idx]}"/nix-support/propagated-native-build-inputs))
    fi
    # TODO: needed?
    if [[ -f "${storepaths_native[$idx]}"/nix-support/propagated-build-inputs ]]; then
      storepaths_build+=($(cat "${storepaths_native[$idx]}"/nix-support/propagated-build-inputs))
    fi
  done
  local -i idx=-1
  while [[ $((++idx)) -lt "${#storepaths_build[@]}" ]]; do
    if [[ -f "${storepaths_build[$idx]}"/nix-support/propagated-build-inputs ]]; then
      storepaths_build+=($(cat "${storepaths_build[$idx]}"/nix-support/propagated-build-inputs))
    fi
  done

  # find things to add to env

  local -A seen=()
  # for storepath in "${storepaths_native[@]}"; do
  for storepath in "${storepaths_build[@]}"; do
    if [[ -v seen["$storepath"] ]]; then
      continue
    fi
    seen["$storepath"]=1

    # TODO: $dev/lib/cmake
    # TODO: source completions from /share/bash-completion/completions/*
    if [[ -d "$storepath/bin" ]]; then
      export PATH="$storepath/bin${PATH:+":$PATH"}"
    fi
    if >/dev/null compgen -G "$storepath/lib/*.so*"; then
      export LDPATH="-L$storepath/lib${LDPATH:+" $LDPATH"}"
      export LD_LIBRARY_PATH="$storepath/lib${LD_LIBRARY_PATH:+":$LD_LIBRARY_PATH"}"
      export CMAKE_LIBRARY_PATH="$storepath/lib${CMAKE_LIBRARY_PATH:+":$CMAKE_LIBRARY_PATH"}"
    fi
    if [[ -d "$storepath/lib/pkgconfig" ]]; then
      export PKG_CONFIG_PATH="$storepath/lib/pkgconfig${PKG_CONFIG_PATH:+":$PKG_CONFIG_PATH"}"
    fi
    if [[ -d "$storepath/share" ]]; then
      export XDG_DATA_DIRS="$storepath/share${XDG_DATA_DIRS:+":$XDG_DATA_DIRS"}"
    fi
    if [[ -d "$storepath/share/man" ]]; then
      if [[ ! -v MANPATH ]] && command -v manpath >/dev/null; then
        export MANPATH="$(manpath)"
      fi
      export MANPATH="$storepath/share/man:$MANPATH"
    fi
    # TODO: see /nix/store/...-bash-completion-2.14.0/etc/profile.d/bash_completion.sh for details on how to source these
    # if [[ -d "$storepath/share/bash-completion/completions" ]] && shopt -q progcomp; then
    #   "$storepath/share/bash-completion/completions/*.bash"
    #   "$storepath/share/bash-completion/completions/*"
    #   "$storepath/share/bash-completion/completions/_*"
    # fi
    if [[ -d "$storepath/share/pkgconfig" ]]; then
      export PKG_CONFIG_PATH="$storepath/share/pkgconfig${PKG_CONFIG_PATH:+":$PKG_CONFIG_PATH"}"
    fi
    if [[ -d "$storepath/lib/pkgconfig" ]]; then
      export PKG_CONFIG_PATH="$storepath/lib/pkgconfig${PKG_CONFIG_PATH:+":$PKG_CONFIG_PATH"}"
    fi
    if [[ -d "$storepath/include" ]]; then
      export CPATH="$storepath/include${CPATH:+":$CPATH"}"
      export CMAKE_INCLUDE_PATH="$storepath/include${CMAKE_INCLUDE_PATH:+":$CMAKE_INCLUDE_PATH"}"
    fi
    if >/dev/null compgen -G "$storepath/lib/python*/site-packages"; then
      export PYTHONPATH="$(IFS=: ; printf "%s" "$storepath"/lib/python*/site-packages)${PYTHONPATH:+":$PYTHONPATH"}"
      # export PYTHONNOUSERSITE=1
    fi
  done

  if [[ -v _PREVIOUS_NS_ARGS ]]; then
    export _PREVIOUS_NS_ARGS="$_PREVIOUS_NS_ARGS$(printf " %q" "${args[@]}")"
  else
    export _PREVIOUS_NS_ARGS="$(printf " %q" "${args[@]}" | cut -c2-)"
  fi
}

ns() { _ns_ohk2aaDu false "$@"; }
nd() { _ns_ohk2aaDu true "$@"; }

# TODO: some way to "pop"?
# TODO: support empty args with nix-search

# enter
ne() {
  local pkg="$1"
  if [[ -z "$pkg" ]]; then
    return
  fi
  if [[ ! "$pkg" =~ ^.*[#:/].*$ ]]; then
    pkg=nixpkgs#"${pkg}"
  fi

  ns "$@"
  # storepath="$(nix --extra-experimental-features "nix-command flakes" build --impure "$pkg" --print-out-paths --no-link | head -n1)"
  storepath="$(nix --extra-experimental-features "nix-command flakes" eval --impure "$pkg" --raw)"
  printf >&2 "+ cd %q\n" "$storepath"
  cd "$storepath"
}

# TODO: add a 'enable-unfree' command which switches the NIX_CONFIG with a file
# where https://nix.dev/manual/nix/2.26/command-ref/conf-file#conf-impure-env is
# set with NIXPKGS_ALLOW_UNFREE.
# blocked on lix support for the option
