#!/usr/bin/env sh
set -e

# ── 1. Test-file convention ────────────────────────────────────────────────────
# Tests must live in *_tests.rs files, not inline #[cfg(test)] blocks.
# A colocated reference is fine: `#[cfg(test)] mod foo_tests;` (semicolon).
# Inline blocks have the form `#[cfg(test)] mod foo {` (opening brace).
echo "Checking test-file convention (tests must be in *_tests.rs files)..."
fail=0

check_file() {
  file="$1"
  prev_was_cfg_test=0
  while IFS= read -r line; do
    case "$line" in
      *'#[cfg(test)]'*)
        prev_was_cfg_test=1
        continue
        ;;
    esac
    if [ "$prev_was_cfg_test" = "1" ]; then
      case "$line" in
        *'mod '*'{'*)
          echo "  FAIL: $file: inline test block found (use *_tests.rs instead)"
          return 1
          ;;
      esac
      prev_was_cfg_test=0
    fi
  done < "$file"
  return 0
}

for f in $(find src -name '*.rs' | grep -v '_tests\.rs$'); do
  if ! check_file "$f"; then
    fail=1
  fi
done

if [ "$fail" = "1" ]; then
  echo "Test-convention check FAILED. Move inline test blocks to *_tests.rs siblings."
  exit 1
fi
echo "Test-convention check passed."

# ── 2. Format ─────────────────────────────────────────────────────────────────
cargo fmt --check

# ── 3. Lint ───────────────────────────────────────────────────────────────────
# Match the CI clippy gate (.github/workflows/lint.yml) exactly: lint every
# target (tests, examples, benches), and treat warnings as errors. Bare
# `cargo clippy` skips test code and only warns, so it can pass here yet fail
# CI — keep the two in lockstep so a green local push stays green in CI.
cargo clippy --all-targets -- -D warnings

# ── 4. Coverage ───────────────────────────────────────────────────────────────
# Requires: cargo install cargo-llvm-cov && rustup component add llvm-tools-preview
if ! command -v cargo-llvm-cov >/dev/null 2>&1; then
  echo ""
  echo "cargo-llvm-cov is not installed. Run:"
  echo "  cargo install cargo-llvm-cov"
  echo "  rustup component add llvm-tools-preview"
  exit 1
fi

echo "Running coverage (100% line coverage required, excluding main.rs)..."
cargo llvm-cov --fail-under-lines 100 --ignore-filename-regex 'src/main\.rs'

# ── 5. Changelog ────────────────────────────────────────────────────────────────
# Mirror the CI `Changelog` workflow (.github/workflows/changelog.yml) locally so
# pushes that would fail the changelog-enforcer check are caught before they hit
# CI. If the commits being pushed touch `src/` or `ui/`, CHANGELOG.md must also be
# touched in the same range. Set SKIP_CHANGELOG=1 to bypass (the local equivalent
# of the `skip-changelog` PR label) for deliberately undocumented src/ui changes.
if [ "${SKIP_CHANGELOG:-0}" != "1" ]; then
  echo "Checking CHANGELOG.md update for src/ or ui/ changes..."
  # Range being pushed: everything on HEAD not yet on the upstream main branch,
  # via the merge-base with origin/main.
  base="$(git merge-base HEAD origin/main 2>/dev/null || true)"
  if [ -z "$base" ]; then
    echo "  Skipping changelog check (no origin/main to diff against)."
  else
    changed="$(git diff --name-only "$base"...HEAD)"
    code_changed=0
    changelog_changed=0
    for path in $changed; do
      case "$path" in
        src/*|ui/*) code_changed=1 ;;
        CHANGELOG.md) changelog_changed=1 ;;
      esac
    done
    if [ "$code_changed" = "1" ] && [ "$changelog_changed" = "0" ]; then
      echo ""
      echo "  FAIL: changes under src/ or ui/ but CHANGELOG.md was not updated."
      echo "  Add a bullet under '## [Unreleased]', or set SKIP_CHANGELOG=1 to bypass"
      echo "  (the local equivalent of the 'skip-changelog' PR label)."
      exit 1
    fi
    echo "Changelog check passed."
  fi
fi
