#!/usr/bin/env bash

set -euo pipefail

export MISE_RUSTUP_HOME="$PWD/rustup"
export MISE_CARGO_HOME="$PWD/cargo"

mkdir -p "$MISE_RUSTUP_HOME" "$MISE_CARGO_HOME/bin"
touch "$MISE_RUSTUP_HOME/settings.toml"

cat >"$MISE_CARGO_HOME/bin/cargo" <<'EOF'
#!/usr/bin/env bash
echo "cargo"
EOF

cat >"$MISE_CARGO_HOME/bin/rustc" <<'EOF'
#!/usr/bin/env bash
echo "rustc ${RUSTUP_TOOLCHAIN:-unknown}"
EOF

cat >"$MISE_CARGO_HOME/bin/rustup" <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

echo "$*" >>"$MISE_CARGO_HOME/rustup.log"

case "$1 $2" in
	"show profile")
		echo default
		;;
	"component list")
		toolchain="${*: -1}"
		[[ -d "$MISE_RUSTUP_HOME/toolchains/$toolchain" ]] || exit 1
		cat "$MISE_CARGO_HOME/components" 2>/dev/null || true
		;;
	"target list")
		toolchain="${*: -1}"
		[[ -d "$MISE_RUSTUP_HOME/toolchains/$toolchain" ]] || exit 1
		cat "$MISE_CARGO_HOME/targets" 2>/dev/null || true
		;;
	"toolchain install")
		shift 2
		toolchain="$1"
		shift
		toolchain_exists=false
		[[ -d "$MISE_RUSTUP_HOME/toolchains/$toolchain" ]] && toolchain_exists=true
		mkdir -p "$MISE_RUSTUP_HOME/toolchains/$toolchain"
		host=x86_64-unknown-linux-gnu
		profile=""
		while [[ $# -gt 0 ]]; do
			case "$1" in
				--component)
					# rustup lists host components with the host's triple
					case "$2" in
						cargo | rust-std | rustc) echo "$2-$host" >>"$MISE_CARGO_HOME/components" ;;
						*) echo "$2" >>"$MISE_CARGO_HOME/components" ;;
					esac
					shift 2
					;;
				--target)
					echo "$2" >>"$MISE_CARGO_HOME/targets"
					shift 2
					;;
				--profile)
					profile="$2"
					shift 2
					;;
				*)
					shift
					;;
			esac
			done
		# a toolchain this fake creates gets its profile's components
		if ! $toolchain_exists; then
			printf '%s\n' "cargo-$host" "rust-std-$host" "rustc-$host" >>"$MISE_CARGO_HOME/components"
			if [[ -z "$profile" || "$profile" == "default" || "$profile" == "d" ]]; then
				printf '%s\n' clippy rust-docs rustfmt >>"$MISE_CARGO_HOME/components"
			fi
		fi
		sort -u "$MISE_CARGO_HOME/components" -o "$MISE_CARGO_HOME/components" 2>/dev/null || true
		sort -u "$MISE_CARGO_HOME/targets" -o "$MISE_CARGO_HOME/targets" 2>/dev/null || true
		;;
	"toolchain uninstall")
		;;
	*)
		echo "unexpected rustup args: $*" >&2
		exit 1
		;;
esac
EOF

chmod +x "$MISE_CARGO_HOME/bin/cargo" "$MISE_CARGO_HOME/bin/rustc" "$MISE_CARGO_HOME/bin/rustup"

assert_does_not_call_rustup() {
  cp "$MISE_CARGO_HOME/rustup.log" "$MISE_CARGO_HOME/rustup.log.before"
  "$@" >/dev/null
  cmp -s "$MISE_CARGO_HOME/rustup.log.before" "$MISE_CARGO_HOME/rustup.log" ||
    fail "$* unexpectedly invoked rustup"
}

assert_hook_env_does_not_call_rustup() {
  assert_does_not_call_rustup mise hook-env -s bash --force
}

cat >mise.toml <<EOF
[tools]
rust = "1.81.0"
EOF

mise install
assert_contains "grep -c '^toolchain install' '$MISE_CARGO_HOME/rustup.log'" "1"

sed -i.bak '/^rust-docs$/d' "$MISE_CARGO_HOME/components"
assert_fail_contains "mise install rust --dry-run-code 2>&1" "would install"
mise install
assert_contains "grep -c '^toolchain install' '$MISE_CARGO_HOME/rustup.log'" "2"
assert_contains "cat '$MISE_CARGO_HOME/components'" "rust-docs"

rm -rf "$MISE_RUSTUP_HOME/toolchains/1.81.0"
assert_hook_env_does_not_call_rustup
assert_fail_contains "mise install rust --dry-run-code 2>&1" "would install"
mise install
assert_contains "grep -c '^toolchain install' '$MISE_CARGO_HOME/rustup.log'" "3"

cat >mise.toml <<EOF
[tools]
rust = { version = "1.81.0", profile = "default" }
EOF

sed -i.bak '/^rustfmt$/d' "$MISE_CARGO_HOME/components"
assert_hook_env_does_not_call_rustup
assert_fail_contains "mise install rust --dry-run-code 2>&1" "would install"
mise install
assert_contains "grep -c '^toolchain install' '$MISE_CARGO_HOME/rustup.log'" "4"
assert_contains "cat '$MISE_CARGO_HOME/components'" "clippy"
assert_contains "cat '$MISE_CARGO_HOME/components'" "rust-docs"
assert_contains "cat '$MISE_CARGO_HOME/components'" "rustfmt"

cat >mise.toml <<EOF
[tools]
rust = { version = "1.81.0", profile = "d" }
EOF

sed -i '/^rustfmt$/d' "$MISE_CARGO_HOME/components"
assert_fail_contains "mise install rust --dry-run-code 2>&1" "would install"
mise install
assert_contains "grep -c '^toolchain install' '$MISE_CARGO_HOME/rustup.log'" "5"
assert_contains "cat '$MISE_CARGO_HOME/components'" "rustfmt"

cat >mise.toml <<EOF
[tools]
rust = { version = "1.81.0", components = ["rustfmt", "rust-src"], targets = ["wasm32-unknown-unknown"] }
EOF

assert_fail_contains "mise install rust --dry-run-code 2>&1" "would install"

mise exec -- rustc -V
assert_contains "grep -c '^toolchain install' '$MISE_CARGO_HOME/rustup.log'" "6"
assert_contains "cat '$MISE_CARGO_HOME/components'" "rustfmt"
assert_contains "cat '$MISE_CARGO_HOME/components'" "rust-src"
assert_contains "cat '$MISE_CARGO_HOME/targets'" "wasm32-unknown-unknown"

mise install
assert_contains "grep -c '^toolchain install' '$MISE_CARGO_HOME/rustup.log'" "6"
assert_contains "mise install rust --dry-run-code 2>&1" "already installed"

# Shims and exec without auto-install only need install markers; the rustup
# satisfaction check is reserved for tools mise is about to install.
mise reshim
assert_does_not_call_rustup "$MISE_DATA_DIR/shims/rustc" -V
MISE_EXEC_AUTO_INSTALL=0 assert_does_not_call_rustup mise exec -- rustc -V

# Every profile needs cargo, rustc and the host's standard library, and an
# existing toolchain only gets them back when mise names them. A cross
# target's standard library does not stand in for the host's.
cat >mise.toml <<EOF2
[tools]
rust = { version = "1.81.0", profile = "minimal" }
EOF2

sed -i.bak -e '/^cargo/d' -e '/^rust-std$/d' -e '/^rust-std-x86_64-unknown-linux-gnu$/d' "$MISE_CARGO_HOME/components"
echo rust-std-wasm32-unknown-unknown >>"$MISE_CARGO_HOME/components"
assert_fail_contains "mise install rust --dry-run-code 2>&1" "would install"
mise install
assert_contains "grep -c '^toolchain install' '$MISE_CARGO_HOME/rustup.log'" "7"
last_install="grep '^toolchain install' '$MISE_CARGO_HOME/rustup.log' | tail -1"
assert_contains "$last_install" "--component cargo"
assert_contains "$last_install" "--component rust-std"
assert_contains "cat '$MISE_CARGO_HOME/components'" "rust-std-x86_64-unknown-linux-gnu"
assert_contains "mise install rust --dry-run-code 2>&1" "already installed"
