#!/usr/bin/env bash
#
# Pre-commit hook that runs CI-equivalent checks locally.
# Classifies staged files by type and only runs relevant make
# targets. Falls back to the full `make pre-commit` when the
# Makefile changed or CODER_HOOK_RUN_ALL=1 is set.
#
# Opt in/out without modifying this file:
#
#   git config coder.pre-commit true    # opt in
#   git config coder.pre-commit false   # opt out (overrides allowlist)
#   git config --unset coder.pre-commit # default (allowlist decides)
#
# Installation (worktree-compatible):
#
#   git config core.hooksPath scripts/githooks
#
# Bypass: git commit --no-verify

set -euo pipefail

# Allowlist of developers who opt in to pre-commit checks by default.
# Matched against CODER_WORKSPACE_OWNER_NAME.
ALLOWLIST=(
	mafredri
	johnstcn
)

cd "$(git rev-parse --show-toplevel)"

# Unset all repo-local Git env vars, not just GIT_DIR. In linked
# worktrees the hook inherits variables like GIT_COMMON_DIR and
# GIT_INDEX_FILE that confuse child processes (notably Go's VCS
# stamping, which shells out to git and gets exit status 128).
# Process substitution (not a pipe) so unset runs in the current shell.
while IFS= read -r var; do
	unset "$var"
done < <(git rev-parse --local-env-vars)

# In linked worktrees, set worktree-scoped hooksPath to override shared config.
if [[ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ]]; then
	git config --worktree core.hooksPath scripts/githooks
fi

# Explicit opt-in/opt-out via git config (overrides allowlist).
run=false
opt_in=$(git config --type=bool coder.pre-commit 2>/dev/null || true)
if [[ $opt_in == true ]]; then
	run=true
elif [[ $opt_in == false ]]; then
	# Explicit opt-out, skip everything including hint.
	exit 0
fi

# Check allowlist.
if ! $run; then
	owner=${CODER_WORKSPACE_OWNER_NAME:-}
	for allowed in "${ALLOWLIST[@]}"; do
		if [[ $owner == "$allowed" ]]; then
			run=true
			break
		fi
	done
fi

if ! $run; then
	echo "pre-commit: checks disabled, opt in with: git config coder.pre-commit true"
	exit 0
fi

if [[ ${CODER_HOOK_RUN_ALL:-} == 1 ]]; then
	exec make pre-commit
fi

staged=$(git diff --cached --name-only --diff-filter=d)
if [[ -z $staged ]]; then
	echo "pre-commit: no staged changes, skipping"
	exit 0
fi

# If Go, TS, or build-system files changed, run the full
# pre-commit. Otherwise run the lightweight target that
# covers everything except gen, Go/TS fmt+lint, and binary build.
if echo "$staged" | grep -qE '\.(go|ts|tsx|sql|proto)$|^go\.(mod|sum)$|^site/|^Makefile$'; then
	exec make pre-commit
fi
exec make pre-commit-light
