#!/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).
# Scans `ui/src` too (same reason as the clippy/test/linecheck steps below):
# this is a non-virtual workspace and `ui` has its own ~250 tests, so limiting
# this scan to `src` let an inline test block in `ui/src` slip past silently.
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 ui/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) across the whole workspace (including the
# `ui` member crate), and treat warnings as errors. Bare `cargo clippy` skips
# test code and only lints the root package, so it can pass here yet fail CI —
# keep the two in lockstep so a green local push stays green in CI.
cargo clippy --workspace --all-targets -- -D warnings

# ── 4. Test (workspace) ────────────────────────────────────────────────────────
# `--workspace`, same reason as the clippy step above: this is a non-virtual
# workspace (root Cargo.toml declares both a [package] and a [workspace]), so
# a bare `cargo test` only runs the root `moadim` package's tests and
# silently skips the `ui` member crate's ~250 tests. The coverage step below
# also only instruments the root package (see its comment), so without this
# step a broken `ui` unit test could pass the hook entirely.
cargo test --workspace

# ── 5. 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)..."
# Deliberately scoped to the root package (no --workspace): the `ui` crate is
# a Yew/WASM UI, and much of it is only meaningfully exercisable through a
# browser, so it isn't held to this 100% line floor. Its own test suite still
# runs above, via the workspace-wide `cargo test`.
cargo llvm-cov --fail-under-lines 100 --ignore-filename-regex 'src/main\.rs'

# ── 6. Changelog ────────────────────────────────────────────────────────────────
# Mirror the CI `Changelog` workflow (.github/workflows/changelog.yml) locally so
# pushes that would fail the `changeset status` check are caught before they hit
# CI. If the commits being pushed touch `src/` or `ui/`, a new `.changeset/*.md`
# file must be present 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.

# ── 7. Line-count gate ────────────────────────────────────────────────────────
# Requires: cargo install linecheck
if ! command -v linecheck >/dev/null 2>&1; then
  echo ""
  echo "linecheck is not installed. Run:"
  echo "  cargo install linecheck"
  exit 1
fi

echo "Checking .rs files do not exceed 500 lines..."
# shellcheck disable=SC2046
linecheck --max-lines 500 $(find src ui/src -name '*.rs')
echo "Line-count check passed."

if [ "${SKIP_CHANGELOG:-0}" != "1" ]; then
  echo "Checking for a changeset covering 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 changeset check (no origin/main to diff against)."
  else
    changed="$(git diff --name-only "$base"...HEAD)"
    code_changed=0
    changeset_added=0
    for path in $changed; do
      case "$path" in
        src/*|ui/*) code_changed=1 ;;
        .changeset/*.md)
          [ "$(basename "$path")" != "README.md" ] && changeset_added=1
          ;;
      esac
    done
    if [ "$code_changed" = "1" ] && [ "$changeset_added" = "0" ]; then
      echo ""
      echo "  FAIL: changes under src/ or ui/ but no changeset was added."
      echo "  Run 'pnpm changeset', or set SKIP_CHANGELOG=1 to bypass (the local"
      echo "  equivalent of the 'skip-changelog' PR label)."
      exit 1
    fi
    echo "Changeset check passed."
  fi
fi
