# Shared primitives; sourced only by the reviewed install/recovery entrypoints.
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
export LC_ALL=C
umask 077

readonly SC_TELEMETRY_ENDPOINT='https://data.supercode.sh/api/s/track'
readonly SC_TELEMETRY_WRITE_KEY='ylrCfjZIBayGrGoPF0mB9VnmUcQio25c:n4A7pjX1p4h3DuvWg4i0BXMjuhXaIeMb'

sc_installation_attempt_valid() {
	local candidate=$1 compact=${1//-/}
	[[ ${#candidate} == 36 && ${candidate[9]} == - && ${candidate[14]} == - && ${candidate[15]} == [1-8] &&
		${candidate[19]} == - && ${candidate[20]} == [89aAbB] && ${candidate[24]} == - &&
		${#compact} == 32 && "$compact" == [[:xdigit:]]## ]]
}

sc_installation_attempt_initialize() {
	local candidate=${SUPERCODE_CLAUDE_INSTALLATION_ATTEMPT_ID:-}
	if ! sc_installation_attempt_valid "$candidate"; then
		candidate=$(/usr/bin/uuidgen 2>/dev/null | /usr/bin/tr '[:upper:]' '[:lower:]') || return 0
	fi
	export SUPERCODE_CLAUDE_INSTALLATION_ATTEMPT_ID=$candidate
	sc_installation_attempt_id=$candidate
}

sc_analytics_identity_valid() {
	sc_installation_attempt_valid "$1"
}

sc_analytics_identity_initialize() {
	local path directory candidate created temporary
	if [[ -n ${SUPERCODE_CLAUDE_STORAGE_ROOT:-} || "${sc_root:-}" != "$HOME/.supercode/claude" ]]; then
		[[ "${sc_root:-}" == /* ]] || return 0
		path="${sc_root%/}/analytics-id-v1"
	else
		path="$HOME/.supercode/analytics-id-v1"
	fi
	directory=${path:h}
	(umask 077 && /bin/mkdir -p "$directory") 2>/dev/null || return 0
	if [[ -f "$path" && ! -L "$path" ]]; then
		candidate=$(<"$path")
		if sc_analytics_identity_valid "$candidate"; then
			sc_analytics_id=$candidate
		fi
		return 0
	fi
	[[ ! -e "$path" && ! -L "$path" ]] || return 0
	created=$(/usr/bin/uuidgen 2>/dev/null | /usr/bin/tr '[:upper:]' '[:lower:]') || return 0
	sc_analytics_identity_valid "$created" || return 0
	temporary=$(/usr/bin/mktemp "${path}.XXXXXXXX" 2>/dev/null) || return 0
	print -r -- "$created" >"$temporary" 2>/dev/null || {
		/bin/rm -f "$temporary"
		return 0
	}
	/bin/chmod 600 "$temporary" 2>/dev/null || {
		/bin/rm -f "$temporary"
		return 0
	}
	/bin/ln "$temporary" "$path" 2>/dev/null || true
	/bin/rm -f "$temporary"
	if [[ -f "$path" && ! -L "$path" ]]; then
		candidate=$(<"$path")
		sc_analytics_identity_valid "$candidate" && sc_analytics_id=$candidate
	fi
	return 0
}

sc_installation_attempt_publish() {
	[[ -n ${sc_installation_attempt_id:-} ]] || return 0
	local target="$sc_root/installation-attempt-v1.json" temporary
	[[ ! -L "$target" ]] || return 0
	temporary=$(/usr/bin/mktemp "${target}.XXXXXXXX" 2>/dev/null) || return 0
	print -r -- "{\"schemaVersion\":1,\"installationAttemptId\":\"$sc_installation_attempt_id\",\"startedAt\":$(/bin/date +%s)}" >"$temporary" 2>/dev/null || {
		/bin/rm -f "$temporary"
		return 0
	}
	/bin/chmod 600 "$temporary" 2>/dev/null || true
	/bin/mv -f "$temporary" "$target" 2>/dev/null || /bin/rm -f "$temporary"
	return 0
}

# Analytics are observational. Lifecycle boundaries may wait for at most two
# seconds so a process exit cannot silently discard the only terminal fact.
sc_telemetry_send() {
	[[ -n ${sc_installation_attempt_id:-} ]] || return 0
	[[ -n ${sc_analytics_id:-} ]] || sc_analytics_identity_initialize
	[[ -n ${sc_analytics_id:-} ]] || return 0
	[[ ${SUPERCODE_CLAUDE_ANALYTICS_DISABLED:-false} != true ]] || return 0
	[[ ${NODE_ENV:-production} != test ]] || return 0
	[[ ${sc_bootstrap_namespace:-.s} != .e ]] || return 0
	local delivery=$1 event=$2 properties=''
	shift 2
	local pair key value key_json value_json
	for pair in "$@"; do
		key=${pair%%=*}
		value=${pair#*=}
		sc_json_string "$key"; key_json=$REPLY
		sc_json_string "$value"; value_json=$REPLY
		properties+="${properties:+,}$key_json:$value_json"
	done
	local now message_id event_json attempt_json analytics_json payload
	now=$(/bin/date -u '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null) || return 0
	message_id=$(/usr/bin/uuidgen 2>/dev/null | /usr/bin/tr '[:upper:]' '[:lower:]') || return 0
	sc_json_string "$event"; event_json=$REPLY
	sc_json_string "$sc_installation_attempt_id"; attempt_json=$REPLY
	sc_json_string "$sc_analytics_id"; analytics_json=$REPLY
	payload="{\"anonymousId\":$analytics_json,\"context\":{\"arch\":\"$(/usr/bin/uname -m 2>/dev/null)\",\"ide\":\"claude\",\"library\":{\"name\":\"supercode-claude\",\"version\":\"installer\"},\"os\":\"darwin\"},\"event\":$event_json,\"messageId\":\"$message_id\",\"properties\":{\"product\":\"claude\",\"installation_attempt_id\":$attempt_json${properties:+,$properties}},\"sentAt\":\"$now\",\"timestamp\":\"$now\",\"type\":\"track\",\"writeKey\":\"ylrCfjZIBayGrGoPF0mB9VnmUcQio25c:***\"}"
	if [[ "$delivery" == flush ]]; then
		/usr/bin/curl --fail --silent --max-time 2 --proto '=https' --tlsv1.2 \
			-H 'Content-Type: application/json' -H "X-Write-Key: $SC_TELEMETRY_WRITE_KEY" \
			--data-binary "$payload" "$SC_TELEMETRY_ENDPOINT" >/dev/null 2>&1 || true
	else
		(
			/usr/bin/curl --fail --silent --max-time 2 --proto '=https' --tlsv1.2 \
				-H 'Content-Type: application/json' -H "X-Write-Key: $SC_TELEMETRY_WRITE_KEY" \
				--data-binary "$payload" "$SC_TELEMETRY_ENDPOINT" >/dev/null 2>&1 || true
		) </dev/null >/dev/null 2>&1 &!
	fi
	return 0
}

sc_telemetry_track() {
	local event=$1
	shift
	sc_telemetry_send async "$event" "$@"
}

sc_telemetry_track_and_flush() {
	local event=$1
	shift
	sc_telemetry_send flush "$event" "$@"
}

sc_fail() {
	print -ru2 -- "Supercode: $*"
	return 1
}

sc_diagnostic_fail() {
	local code=$1
	[[ "$code" == sc_cl_[a-z0-9_]## ]] || code=sc_cl_unknown
	sc_telemetry_reason=$code
	sc_fail "Installation failed ($code)."
}

sc_sha256_file() {
	[[ -f "$1" ]] || sc_fail "Cannot hash a missing file: $1"
	/usr/bin/openssl dgst -sha256 "$1" | /usr/bin/awk '{print $NF}'
}

sc_assert_path() {
	[[ "$1" == /* && "$1" != *[[:cntrl:]]* ]] || sc_fail 'An absolute path without control characters is required.'
}

sc_json_string() {
	local value=$1
	value=${value//\\/\\\\}
	value=${value//\"/\\\"}
	value=${value//$'\n'/\\n}
	value=${value//$'\r'/\\r}
	value=${value//$'\t'/\\t}
	REPLY="\"$value\""
}

sc_process() {
	[[ -n ${sc_pid:-} ]] || return 0
	sc_verify_process "$sc_app" "$sc_pid"
}

sc_acquire_lock() {
	local owner previous owner_pid
	sc_lock="$sc_root/lifecycle.lock"
	owner="$$:$(/usr/bin/uuidgen)"
	if ! /bin/ln -s "$owner" "$sc_lock" 2>/dev/null; then
		[[ -L "$sc_lock" ]] || sc_fail 'Lifecycle lock is occupied.'
		previous=$(/usr/bin/readlink "$sc_lock")
		owner_pid=${previous%%:*}
		[[ "$owner_pid" == <1-> ]] || sc_fail 'Lifecycle lock has invalid ownership.'
		if kill -0 "$owner_pid" 2>/dev/null; then
			sc_fail 'Another install or recovery is running.'
			return 1
		fi
		[[ "$(/usr/bin/readlink "$sc_lock")" == "$previous" ]] || return 1
		/bin/rm "$sc_lock"
		/bin/ln -s "$owner" "$sc_lock" || return 1
	fi
	sc_lock_owner=$owner
}

sc_cleanup() {
	if [[ -n ${sc_lock_owner:-} && -L ${sc_lock:-} && "$(/usr/bin/readlink "$sc_lock")" == "$sc_lock_owner" ]]; then
		/bin/rm "$sc_lock"
	fi
	if [[ -n ${sc_stage:-} && "$sc_stage" == "$sc_root"/install-stage.* && -d "$sc_stage" && ! -L "$sc_stage" ]]; then
		/bin/rm -r -- "$sc_stage"
	fi
}

sc_copy_atomic() {
	local temporary
	[[ -f "$1" && ! -L "$1" ]] || sc_fail 'Distribution asset is missing or is a symlink.'
	[[ ! -L "$2" ]] || sc_fail 'Refusing to replace a symlinked state file.'
	temporary=$(/usr/bin/mktemp "${2}.XXXXXXXX") || return 1
	/bin/cp "$1" "$temporary" || { /bin/rm -f "$temporary"; return 1; }
	/bin/chmod "${3:-600}" "$temporary" || { /bin/rm -f "$temporary"; return 1; }
	/bin/mv -f "$temporary" "$2" || { /bin/rm -f "$temporary"; return 1; }
}

sc_select_app() {
	local row candidate pid
	local -a matches=()
	if [[ -n "$sc_app" ]]; then
		sc_assert_path "$sc_app"
		sc_app=${sc_app:A}
	fi
	if [[ -z "$sc_app" || -z "$sc_pid" ]]; then
		while IFS= read -r row; do
			row=${row##[[:space:]]#}
			pid=${row%%[[:space:]]*}
			candidate=${row#${pid}}
			candidate=${candidate##[[:space:]]#}
			[[ "$candidate" == */Claude.app/Contents/MacOS/Claude ]] || continue
			[[ -z "$sc_app" || "$candidate" == "$sc_app/Contents/MacOS/Claude" ]] || continue
			[[ -z "$sc_pid" || "$pid" == "$sc_pid" ]] || continue
			matches+=("$pid" "${candidate%/Contents/MacOS/Claude}")
		done < <(/bin/ps -axo pid=,comm=)
		if (( ${#matches} == 2 )); then
			sc_pid=$matches[1]
			sc_app=${matches[2]:A}
		elif (( ${#matches} > 2 )); then
			sc_fail 'More than one Claude Desktop app is running. Close the other copies or select one with --app and --pid.'
		elif [[ -n "$sc_pid" ]]; then
			sc_fail 'The selected Claude Desktop process is not running.'
		elif [[ -z "$sc_app" ]]; then
			sc_app=/Applications/Claude.app
		fi
	fi
	sc_assert_path "$sc_app"
	sc_app=${sc_app:A}
	if [[ ! -d "$sc_app" || "${sc_app:t}" != Claude.app ]]; then
		if [[ "$sc_app" == /Applications/Claude.app ]]; then
			sc_fail 'Claude Desktop could not be found in /Applications.'
		else
			sc_fail 'Invalid Claude application directory.'
		fi
	fi
	sc_process
}
