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

# The entire script is wrapped in a brace group so that `curl ... | bash` only ever runs a
# fully-downloaded copy: bash parses the whole group before executing it, so a truncated
# download (dropped connection, proxy hiccup) fails to parse and runs nothing.
{

platform=$(uname -ms)

if [[ ${OS:-} = Windows_NT ]]; then
	if [[ $platform != MINGW64* ]]; then
		powershell -c "irm cli.alis.build/install.ps1|iex"
		exit $?
	fi
fi

# Reset
Color_Off=''

# Regular Colors
Red=''
Green=''
Dim='' # White
Accent=''
Muted=''

# Bold
Bold_White=''
Bold_Brand=''

if [[ -t 1 ]]; then
	# Reset
	Color_Off=$'\033[0m' # Text Reset

	# Regular Colors
	Red=$'\033[38;2;240;34;34m'    # Brand red
	Green=$'\033[38;2;51;194;125m' # GreenDark
	Dim=$'\033[2m'                  # Faint
	Accent=$'\033[38;2;0;99;131m'  # Cyan
	Muted=$'\033[38;2;138;143;148m'

	# Bold
	Bold_White=$'\033[1m' # Bold White
	Bold_Brand=$'\033[1;38;2;240;34;34m'
fi

error() {
	echo -e "${Red}error${Color_Off}:" "$@" >&2
	exit 1
}

warn() {
	echo -e "${Red}warning${Color_Off}:" "$@" >&2
}

info() {
	echo -e "${Dim}$@ ${Color_Off}"
}

info_bold() {
	echo -e "${Bold_White}$@ ${Color_Off}"
}

status_line() {
	printf '  %b%-10s%b %s\n' "$Accent" "$1" "$Color_Off" "$2"
}

status_start() {
	printf '  %b%-10s%b %s ...' "$Accent" "$1" "$Color_Off" "$2"
}

status_done() {
	printf ' %b%s%b\n' "$Green" "done" "$Color_Off"
}

print_banner() {
	echo
	echo -e "  ${Bold_Brand}alis${Color_Off} ${Bold_White}Build CLI${Color_Off}"
	echo -e "  ${Muted}Stay in the flow.${Color_Off}"
	echo
}

case $platform in
'Darwin x86_64')
	target=darwin-amd64
	;;
'Darwin arm64')
	target=darwin-arm64
	;;
'Linux aarch64' | 'Linux arm64')
	target=linux-arm64
	;;
'MINGW64'*)
	target=windows-amd64
	;;
'Linux riscv64')
	error 'Not supported on riscv64'
	;;
'Linux x86_64' | *)
	target=linux-amd64
	;;
esac

if [[ $target = darwin-x64 ]]; then
	if [[ $(sysctl -n sysctl.proc_translated 2>/dev/null) = 1 ]]; then
		target=darwin-arm64
		info "Your shell is running in Rosetta 2. Downloading alis for $target instead"
	fi
fi

exe_name=alis

alis_uri=https://cli.alis.build/alis-$target-latest

install_env=ALIS_INSTALL
bin_env=\$$install_env/bin
install_dir=${!install_env:-$HOME/.alis}
bin_dir=$install_dir/bin
exe=$bin_dir/alis
tmp_exe=

cleanup_tmp_exe() {
	if [[ -n ${tmp_exe:-} ]]; then
		rm -f "$tmp_exe"
	fi
}
trap cleanup_tmp_exe EXIT

print_banner

if [[ ! -d $bin_dir ]]; then
	mkdir -p "$bin_dir" ||
		error "Failed to create ~/.alis/bin directory \"$bin_dir\""
fi

tmp_exe=$(mktemp "$bin_dir/.alis.XXXXXX") ||
	error "Failed to create temporary download file in \"$bin_dir\""

status_start "Download" "$target"
if ! curl --proto '=https' --tlsv1.2 --fail --location --silent --show-error --output "$tmp_exe" "$alis_uri"; then
	echo
	error "Failed to download alis from \"$alis_uri\""
fi
status_done

# Verify the SHA-256 checksum before replacing the installed binary. The release pipeline
# publishes a `<binary>.sha256` sidecar next to every artifact.
status_start "Verify" "checksum"
checksum_uri=$alis_uri.sha256
expected_sha=$(curl --proto '=https' --tlsv1.2 --fail --location --silent --show-error "$checksum_uri" | cut -d' ' -f1 || true)
if [[ -z $expected_sha ]]; then
	echo
	error "Failed to download checksum from \"$checksum_uri\""
fi

if command -v sha256sum >/dev/null 2>&1; then
	actual_sha=$(sha256sum "$tmp_exe" | cut -d' ' -f1)
elif command -v shasum >/dev/null 2>&1; then
	actual_sha=$(shasum -a 256 "$tmp_exe" | cut -d' ' -f1)
else
	echo
	error 'Neither sha256sum nor shasum is available; cannot verify the download'
fi

if [[ $actual_sha != "$expected_sha" ]]; then
	echo
	error "Checksum mismatch for alis (expected $expected_sha, got $actual_sha). Aborting install."
fi
status_done

chmod +x "$tmp_exe" ||
	error 'Failed to set permissions on alis executable'
mv -f "$tmp_exe" "$exe" ||
	error 'Failed to install alis executable'
tmp_exe=

tildify() {
	if [[ $1 = $HOME/* ]]; then
		local replacement=\~/

		echo "${1/$HOME\//$replacement}"
	else
		echo "$1"
	fi
}

echo
status_line "Installed" "${Green}$(tildify "$exe")${Color_Off}"
tilde_bin_dir=$(tildify "$bin_dir")
quoted_install_dir=\"${install_dir//\"/\\\"}\"

if [[ $quoted_install_dir = \"$HOME/* ]]; then
	quoted_install_dir=${quoted_install_dir/$HOME\//\$HOME/}
fi

# manual_config_instructions prints the PATH lines for the user to add by
# hand when a shell config could not be updated automatically.
manual_config_instructions() {
	local config=$1
	shift

	echo "Manually add the directory to $(tildify "$config") (or similar):"
	for command in "$@"; do
		info_bold "  $command"
	done
}

# update_shell_config rewrites a shell config with the alis PATH block. The
# binary is already installed by the time this runs, so nothing here may fail
# the install: any error warns, prints the manual lines, and sets
# shell_config_failed=true for the caller instead of aborting.
shell_config_failed=false
update_shell_config() {
	local config=$1
	shift
	shell_config_failed=false

	local temp
	if ! temp=$(mktemp); then
		warn "Failed to create temporary file for \"$config\""
		manual_config_instructions "$config" "$@"
		shell_config_failed=true
		return 0
	fi

	if awk '
		function is_alis_line(line) {
			return line ~ /^export ALIS_INSTALL=/ ||
				line ~ /^export PATH="\$ALIS_INSTALL\/bin:\$PATH"$/ ||
				line ~ /^set --export ALIS_INSTALL / ||
				line ~ /^set --export PATH \$ALIS_INSTALL\/bin \$PATH$/ ||
				line == ""
		}

		skip {
			if (is_alis_line($0)) {
				next
			}

			skip = 0
		}

		pending_marker {
			if (is_alis_line($0)) {
				pending_marker = 0
				skip = 1
				next
			}

			print "# alis"
			pending_marker = 0
		}

		$0 == "# alis" {
			pending_marker = 1
			next
		}

		{
			print
		}

		END {
			if (pending_marker) {
				print "# alis"
			}
		}
	' "$config" >"$temp" &&
		{
			echo
			echo '# alis'

			for command in "$@"; do
				echo "$command"
			done
		} >>"$temp" &&
		cat "$temp" >"$config"; then
		rm -f "$temp"
		return 0
	fi

	rm -f "$temp"
	warn "Failed to update \"$config\""
	manual_config_instructions "$config" "$@"
	shell_config_failed=true
	return 0
}

echo

# ${SHELL:-} tolerates environments with no SHELL set (editor task runners,
# containers): they fall through to the manual-instructions branch instead of
# aborting a completed install under set -u.
case $(basename "${SHELL:-}") in
fish)
	commands=(
		"set --export $install_env $quoted_install_dir"
		"set --export PATH $bin_env \$PATH"
	)

	fish_config=$HOME/.config/fish/config.fish
	tilde_fish_config=$(tildify "$fish_config")

	if [[ -w $fish_config ]]; then
		update_shell_config "$fish_config" "${commands[@]}"

		if [[ $shell_config_failed = false ]]; then
			status_line "PATH" "Configured \"$tilde_bin_dir\" in \"$tilde_fish_config\""

			refresh_command="source $tilde_fish_config"
		fi
	else
		manual_config_instructions "$fish_config" "${commands[@]}"
	fi
	;;
zsh)
	commands=(
		"export $install_env=$quoted_install_dir"
		"export PATH=\"$bin_env:\$PATH\""
	)

	zsh_config=$HOME/.zshrc
	tilde_zsh_config=$(tildify "$zsh_config")

	if [[ -w $zsh_config ]]; then
		update_shell_config "$zsh_config" "${commands[@]}"

		if [[ $shell_config_failed = false ]]; then
			status_line "PATH" "Configured \"$tilde_bin_dir\" in \"$tilde_zsh_config\""

			refresh_command="exec $SHELL"
		fi
	else
		manual_config_instructions "$zsh_config" "${commands[@]}"
	fi
	;;
bash)
	commands=(
		"export $install_env=$quoted_install_dir"
		"export PATH=\"$bin_env:\$PATH\""
	)

	bash_configs=(
		"$HOME/.bashrc"
		"$HOME/.bash_profile"
	)

	if [[ ${XDG_CONFIG_HOME:-} ]]; then
		bash_configs+=(
			"$XDG_CONFIG_HOME/.bash_profile"
			"$XDG_CONFIG_HOME/.bashrc"
			"$XDG_CONFIG_HOME/bash_profile"
			"$XDG_CONFIG_HOME/bashrc"
		)
	fi

	set_manually=true
	for bash_config in "${bash_configs[@]}"; do
		tilde_bash_config=$(tildify "$bash_config")

		if [[ -w $bash_config ]]; then
			update_shell_config "$bash_config" "${commands[@]}"

			if [[ $shell_config_failed = false ]]; then
				status_line "PATH" "Configured \"$tilde_bin_dir\" in \"$tilde_bash_config\""

				refresh_command="source $bash_config"
				set_manually=false
			fi
			break
		fi
	done

	if [[ $set_manually = true && $shell_config_failed = false ]]; then
		manual_config_instructions "${bash_configs[0]}" "${commands[@]}"
	fi
	;;
*)
	echo 'Manually add the directory to ~/.bashrc (or similar):'
	info_bold "  export $install_env=$quoted_install_dir"
	info_bold "  export PATH=\"$bin_env:\$PATH\""
	;;
esac

echo
info "To get started, restart your terminal session and run 'alis'"
echo

} # end download guard — see the opening brace at the top of this file
