# STAGED files only — a commit happens 10-30 times as often as a push, so every
# step here is measured in seconds. The whole-repo sweep (full lint, typecheck,
# the test suite) is in `pre-push`; the build and `verify` are in neither,
# because they take minutes and that is CI's job.
#
# To bypass: `HUSKY=0 git commit` or `git commit --no-verify`.

set -e

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"

# fail/step/step_ok/step_skip, count and the timer come from here. TOTAL is
# computed below from the staged list — unlike pre-push, the denominator here
# changes from commit to commit.
. "$ROOT/.husky/lib.sh"

T_ALL="$(now_ms)"

# 0. Branch name. The commit message itself is checked in `commit-msg` — it
#    cannot be done here, because at this point the message does not exist yet.
#    This runs first, and is deliberately NOT a numbered step: it has to happen
#    before the staged list is even read, so a wrongly-named branch costs zero
#    lint seconds and no total exists yet to count against.
#    `main`/`master` are exempt, and so is a detached HEAD (rebase, bisect) —
#    without that, every commit on a trunk-based workflow would fail.
BRANCH="$(git symbolic-ref --quiet --short HEAD || echo HEAD)"
case "$BRANCH" in
  main|master|HEAD) ;;
  *)
    if ! echo "$BRANCH" | grep -qE '^(chore|feat|hotfix|bugfix|reconcile|fix|docs|refactor|test|perf|ci)[/-].+'; then
      echo "" >&2
      echo "✖ Branch name does not follow the convention: $BRANCH" >&2
      echo "" >&2
      echo "  Format:   <prefix>/<description>   (or <prefix>-<description>)" >&2
      echo "  Prefixes: chore feat hotfix bugfix reconcile fix" >&2
      echo "            docs refactor test perf ci" >&2
      echo "" >&2
      echo "  Examples: feat/progress-circle   fix/select-clearable" >&2
      echo "  Rename:   git branch -m <new-name>" >&2
      echo "" >&2
      exit 1
    fi
    ;;
esac

# --diff-filter=ACM: a deleted path must never be handed to the linter.
STAGED="$(git diff --cached --name-only --diff-filter=ACM)"
if [ -z "$STAGED" ]; then
  echo "  nothing staged — skipped"
  exit 0
fi

# One ESLint call covers everything worth linting. `eslint.config.js` matches
# `**/*.{ts,tsx,mjs}` and globally ignores dist, storybook-static, node_modules
# and tools — so src/, showcase/, test/ and scripts/*.mjs all go through here
# and no separate `node --check` step is needed.
FILES="$(echo "$STAGED" | grep -E '\.(ts|tsx|mjs|js)$' || true)"

if [ -z "$FILES" ]; then
  printf '  branch %s · %s staged file(s) · nothing lintable\n' "$BRANCH" "$(count "$STAGED")"
  exit 0
fi

TOTAL=1

printf '  branch %s · %s staged file(s) · %s check\n\n' "$BRANCH" "$(count "$STAGED")" "$TOTAL"

step "lint · $(count "$FILES") file(s)"

# No node_modules → skip. A hook never installs anything, and CI still checks.
if [ ! -d node_modules ]; then
  step_skip "node_modules missing, CI will still check"
else
  # `--no-warn-ignored`: a staged file that ESLint ignores (dist/, tools/) would
  # otherwise fail the entire invocation.
  echo "$FILES" | xargs npx eslint --no-warn-ignored || fail "lint failed"
  step_ok
fi

printf '\n✓ pre-commit passed%s\n' "$(since "$T_ALL")"
