#!/bin/sh
# Gortex one-line installer for macOS and Linux.
#
# Usage:
#   curl -fsSL https://get.gortex.dev | sh
#   curl -fsSL https://raw.githubusercontent.com/zzet/gortex/main/scripts/install.sh | sh
#
# Configuration via environment variables (all optional):
#   GORTEX_VERSION        Release tag to install ("latest" or "v0.15.0")
#   GORTEX_INSTALL_DIR    Install directory (default: $HOME/.local/bin)
#   GORTEX_NO_VERIFY      Set to skip checksum + cosign verification
#   GORTEX_NO_PATH        Set to skip PATH update in shell rc
#   GORTEX_NO_BREW        Set to skip Homebrew (macOS) and force the tarball path
#   GORTEX_FORCE          Set to overwrite an existing binary without backup
#   GORTEX_DOWNLOAD_BASE  Override release download base URL (for testing)

set -eu

GORTEX_REPO="zzet/gortex"
GORTEX_BIN="gortex"
DOWNLOAD_BASE="${GORTEX_DOWNLOAD_BASE:-https://github.com/${GORTEX_REPO}/releases}"

if [ -t 1 ] && command -v tput >/dev/null 2>&1 && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]; then
	C_BOLD="$(tput bold)"
	C_DIM="$(tput dim 2>/dev/null || printf '')"
	C_RED="$(tput setaf 1)"
	C_GREEN="$(tput setaf 2)"
	C_YELLOW="$(tput setaf 3)"
	C_BLUE="$(tput setaf 4)"
	C_RESET="$(tput sgr0)"
else
	C_BOLD=""; C_DIM=""; C_RED=""; C_GREEN=""; C_YELLOW=""; C_BLUE=""; C_RESET=""
fi

info() { printf '%s==>%s %s\n' "${C_BLUE}${C_BOLD}" "${C_RESET}" "$1"; }
ok()   { printf '%s ok%s  %s\n' "${C_GREEN}${C_BOLD}" "${C_RESET}" "$1"; }
warn() { printf '%s  !%s  %s\n' "${C_YELLOW}${C_BOLD}" "${C_RESET}" "$1" >&2; }
die()  { printf '%s  x%s  %s\n' "${C_RED}${C_BOLD}" "${C_RESET}" "$1" >&2; exit 1; }

# Pick a downloader once.
DOWNLOADER=""
if command -v curl >/dev/null 2>&1; then
	DOWNLOADER="curl"
elif command -v wget >/dev/null 2>&1; then
	DOWNLOADER="wget"
else
	die "neither curl nor wget is installed; install one and retry"
fi

http_get() {
	# $1=url, $2=dest. Returns non-zero on failure (callers decide whether fatal).
	case "$DOWNLOADER" in
		curl) curl --fail --silent --show-error --location --output "$2" "$1" ;;
		wget) wget --quiet --output-document="$2" "$1" ;;
	esac
}

detect_os() {
	uname_s="$(uname -s)"
	case "$uname_s" in
		Linux) echo linux ;;
		Darwin) echo darwin ;;
		# Native Windows shells (Git Bash / MSYS / Cygwin) can't run this
		# POSIX installer — point users at the PowerShell installer. The
		# Linux build still runs fine under WSL.
		MINGW*|MSYS*|CYGWIN*) die "this POSIX installer can't run on native Windows.
  In PowerShell, run:  irm https://get.gortex.dev/install.ps1 | iex
  (or 'scoop install gortex', or run this script from inside WSL)" ;;
		*) die "unsupported OS: $uname_s (Linux and macOS supported)" ;;
	esac
}

detect_arch() {
	uname_m="$(uname -m)"
	case "$uname_m" in
		x86_64|amd64) echo amd64 ;;
		aarch64|arm64) echo arm64 ;;
		*) die "unsupported architecture: $uname_m (amd64 and arm64 supported)" ;;
	esac
}

verify_sha256() {
	# $1=file, $2=expected_hex
	if command -v sha256sum >/dev/null 2>&1; then
		actual="$(sha256sum "$1" | awk '{print $1}')"
	elif command -v shasum >/dev/null 2>&1; then
		actual="$(shasum -a 256 "$1" | awk '{print $1}')"
	else
		warn "no sha256sum or shasum available; cannot verify checksum"
		return 0
	fi
	if [ "$actual" != "$2" ]; then
		die "checksum mismatch on $(basename "$1")
  expected: $2
  actual:   $actual"
	fi
	ok "sha256 verified ($(basename "$1"))"
}

# Append a PATH export to the user's shell rc file if it isn't already there.
# Uses a marker block so re-runs are idempotent.
update_path_in_rc() {
	install_dir="$1"
	marker_begin="# >>> gortex installer >>>"
	marker_end="# <<< gortex installer <<<"

	case ":${PATH}:" in
		*":${install_dir}:"*)
			ok "$install_dir already on PATH"
			return 0
			;;
	esac

	user_shell="$(basename "${SHELL:-sh}")"
	rcfiles=""
	case "$user_shell" in
		bash)
			[ -f "$HOME/.bashrc" ] && rcfiles="$HOME/.bashrc"
			# macOS terminals start a login bash, which reads .bash_profile.
			if [ "$(uname -s)" = Darwin ] && [ -f "$HOME/.bash_profile" ]; then
				rcfiles="${rcfiles:+$rcfiles }$HOME/.bash_profile"
			fi
			[ -z "$rcfiles" ] && rcfiles="$HOME/.bashrc"
			;;
		zsh)
			rcfiles="${ZDOTDIR:-$HOME}/.zshrc"
			;;
		fish)
			rcfiles="$HOME/.config/fish/config.fish"
			;;
		*)
			rcfiles="$HOME/.profile"
			;;
	esac

	for rc in $rcfiles; do
		[ -n "$rc" ] || continue
		if [ -f "$rc" ] && grep -qF "$marker_begin" "$rc" 2>/dev/null; then
			ok "PATH block already present in $(printf '%s' "$rc" | sed "s|^$HOME|~|")"
			continue
		fi
		mkdir -p "$(dirname "$rc")"
		if [ "$user_shell" = fish ]; then
			{
				printf '\n%s\n' "$marker_begin"
				printf 'fish_add_path -aP %s\n' "$install_dir"
				printf '%s\n' "$marker_end"
			} >> "$rc"
		else
			{
				printf '\n%s\n' "$marker_begin"
				# We want a literal $PATH in the rc file so the user's shell
				# expands it at source time, not us at install time.
				# shellcheck disable=SC2016
				printf 'export PATH="%s:$PATH"\n' "$install_dir"
				printf '%s\n' "$marker_end"
			} >> "$rc"
		fi
		ok "added $install_dir to PATH in $(printf '%s' "$rc" | sed "s|^$HOME|~|")"
	done
}

# Homebrew handoff. Cask is macOS-only and brew handles its own PATH, so we
# only divert when the user has plain `brew` on PATH on macOS, isn't pinning
# a version, and didn't pick a custom install dir.
should_use_brew() {
	[ "$1" = darwin ] || return 1
	[ -z "${GORTEX_NO_BREW:-}" ] || return 1
	[ "${GORTEX_VERSION:-latest}" = latest ] || return 1
	[ -z "${GORTEX_INSTALL_DIR:-}" ] || return 1
	command -v brew >/dev/null 2>&1
}

install_via_brew() {
	if brew list --cask gortex >/dev/null 2>&1; then
		info "gortex cask already installed; upgrading via Homebrew"
		brew upgrade --cask gortex || die "brew upgrade failed"
	else
		info "installing via Homebrew (zzet/tap/gortex)"
		brew install zzet/tap/gortex || die "brew install failed"
	fi

	bin="$(command -v gortex || true)"
	[ -n "$bin" ] || die "brew finished but 'gortex' is not on PATH"
	ok "installed $bin"

	# Best-effort daemon restart, mirroring the tarball path.
	if "$bin" daemon status >/dev/null 2>&1; then
		info "restarting running daemon onto new binary"
		"$bin" daemon restart >/dev/null 2>&1 || warn "daemon restart failed; run 'gortex daemon restart' manually"
	fi

	if version_out="$("$bin" version 2>/dev/null)"; then
		ok "$version_out"
	fi

	printf '\n%sNext steps:%s\n' "${C_BOLD}" "${C_RESET}"
	printf '  - %sgortex install%s   one-time machine setup (MCP, skills, slash commands)\n' "${C_BOLD}" "${C_RESET}"
	printf '  - %sgortex init%s      run inside a repo to wire up your AI assistant\n' "${C_BOLD}" "${C_RESET}"
	printf '\nDocs: https://github.com/%s\n\n' "$GORTEX_REPO"
}

main() {
	os="$(detect_os)"
	arch="$(detect_arch)"
	version="${GORTEX_VERSION:-latest}"

	if should_use_brew "$os"; then
		printf '\n%sGortex installer%s\n' "${C_BOLD}" "${C_RESET}"
		printf '  os:      %s\n' "$os"
		printf '  arch:    %s\n' "$arch"
		printf '  version: %s (via Homebrew — set GORTEX_NO_BREW=1 to use the tarball)\n\n' "$version"
		install_via_brew
		return 0
	fi

	install_dir="${GORTEX_INSTALL_DIR:-$HOME/.local/bin}"

	printf '\n%sGortex installer%s\n' "${C_BOLD}" "${C_RESET}"
	printf '  os:      %s\n' "$os"
	printf '  arch:    %s\n' "$arch"
	printf '  version: %s\n' "$version"
	printf '  target:  %s/%s\n\n' "$install_dir" "$GORTEX_BIN"

	asset="gortex_${os}_${arch}.tar.gz"
	if [ "$version" = latest ]; then
		base_url="${DOWNLOAD_BASE}/latest/download"
	else
		case "$version" in
			v*) tag="$version" ;;
			*)  tag="v$version" ;;
		esac
		base_url="${DOWNLOAD_BASE}/download/${tag}"
	fi
	asset_url="${base_url}/${asset}"
	checksums_url="${base_url}/checksums.txt"

	tmpdir="$(mktemp -d 2>/dev/null || mktemp -d -t gortex-install)"
	# shellcheck disable=SC2064  # we want $tmpdir expanded now, not at trap time.
	trap "rm -rf '$tmpdir'" EXIT INT TERM

	info "downloading $asset"
	http_get "$asset_url" "$tmpdir/$asset" \
		|| die "download failed: $asset_url"

	if [ -z "${GORTEX_NO_VERIFY:-}" ]; then
		info "downloading checksums.txt"
		if http_get "$checksums_url" "$tmpdir/checksums.txt"; then
			# checksums.txt is `<sha256>  <filename>` per goreleaser default.
			# Some versions prefix the filename with `*` for binary mode.
			expected="$(awk -v f="$asset" '$2==f || $2=="*"f {print $1; exit}' "$tmpdir/checksums.txt")"
			if [ -z "$expected" ]; then
				warn "checksums.txt did not contain $asset; skipping verification"
			else
				verify_sha256 "$tmpdir/$asset" "$expected"
			fi
		else
			warn "could not fetch checksums.txt; skipping verification"
		fi

		# Optional cosign verification — only if cosign happens to be installed.
		# Releases ship .sig + .pem bound to GitHub Actions OIDC identity; see
		# README "Verifying releases" for the full standalone command.
		if command -v cosign >/dev/null 2>&1; then
			info "verifying cosign signature"
			if http_get "${asset_url}.sig" "$tmpdir/$asset.sig" \
				&& http_get "${asset_url}.pem" "$tmpdir/$asset.pem"; then
				if cosign verify-blob \
						--certificate "$tmpdir/$asset.pem" \
						--signature "$tmpdir/$asset.sig" \
						--certificate-identity-regexp 'https://github\.com/zzet/gortex/.*' \
						--certificate-oidc-issuer https://token.actions.githubusercontent.com \
						"$tmpdir/$asset" >/dev/null 2>&1; then
					ok "cosign verified"
				else
					die "cosign signature verification failed (set GORTEX_NO_VERIFY=1 to skip)"
				fi
			else
				warn "could not fetch cosign signature; skipping"
			fi
		fi
	else
		warn "verification disabled (GORTEX_NO_VERIFY)"
	fi

	info "extracting"
	( cd "$tmpdir" && tar -xzf "$asset" )
	[ -f "$tmpdir/$GORTEX_BIN" ] || die "archive did not contain a $GORTEX_BIN binary"
	chmod +x "$tmpdir/$GORTEX_BIN"

	mkdir -p "$install_dir" || die "cannot create $install_dir"
	# Probe writability before we trash a partial install.
	if ! ( : > "$install_dir/.gortex-write-test" ) 2>/dev/null; then
		die "no write permission to $install_dir
  Set GORTEX_INSTALL_DIR=\$HOME/.local/bin, or rerun with sudo if you really want a system path."
	fi
	rm -f "$install_dir/.gortex-write-test"

	if [ -e "$install_dir/$GORTEX_BIN" ] && [ -z "${GORTEX_FORCE:-}" ]; then
		backup="$install_dir/${GORTEX_BIN}.previous"
		info "backing up existing binary to $backup"
		mv -f "$install_dir/$GORTEX_BIN" "$backup"
	fi
	mv -f "$tmpdir/$GORTEX_BIN" "$install_dir/$GORTEX_BIN"
	ok "installed $install_dir/$GORTEX_BIN"

	# macOS Gatekeeper sets com.apple.quarantine on browser downloads. curl/wget
	# don't, but a re-run might inherit it from a prior browser fetch — strip
	# unconditionally; failure is harmless.
	if [ "$os" = darwin ] && command -v xattr >/dev/null 2>&1; then
		xattr -d com.apple.quarantine "$install_dir/$GORTEX_BIN" 2>/dev/null || true
	fi

	if [ -z "${GORTEX_NO_PATH:-}" ]; then
		update_path_in_rc "$install_dir"
	fi

	# If the daemon is running an older binary, restart it on the new one.
	# Best-effort: never block install on this.
	if "$install_dir/$GORTEX_BIN" daemon status >/dev/null 2>&1; then
		info "restarting running daemon onto new binary"
		"$install_dir/$GORTEX_BIN" daemon restart >/dev/null 2>&1 || warn "daemon restart failed; run 'gortex daemon restart' manually"
	fi

	# Print version banner. The binary lives at an absolute path so we don't
	# depend on PATH being refreshed in the current shell.
	if version_out="$("$install_dir/$GORTEX_BIN" version 2>/dev/null)"; then
		ok "$version_out"
	fi

	printf '\n%sNext steps:%s\n' "${C_BOLD}" "${C_RESET}"
	case ":${PATH}:" in
		*":${install_dir}:"*) ;;
		*)
			printf '  1. Open a new shell (or %ssource%s your rc file) so PATH picks up %s\n' "${C_DIM}" "${C_RESET}" "$install_dir"
			;;
	esac
	printf '  - %sgortex install%s   one-time machine setup (MCP, skills, slash commands)\n' "${C_BOLD}" "${C_RESET}"
	printf '  - %sgortex init%s      run inside a repo to wire up your AI assistant\n' "${C_BOLD}" "${C_RESET}"
	printf '\nDocs: https://github.com/%s\n\n' "$GORTEX_REPO"
}

main "$@"
