#!/usr/bin/env bash
set -u

status=0

fail() {
  status=1
  printf '[FAIL] %s\n' "$1"
}

pass() {
  printf '[PASS] %s\n' "$1"
}

skip() {
  printf '[SKIP] %s\n' "$1"
}

run_required() {
  local label="$1"
  shift

  if "$@"; then
    pass "$label"
  else
    fail "$label"
  fi
}

repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || {
  printf '[FAIL] git worktree detection\n' >&2
  exit 1
}

export PATH="${BIOMCP_TOOL_DIR:-$repo_root/.cache/tools/bin}:$PATH"
if [ -n "${CARGO_HOME:-${HOME:-}}" ]; then
  export PATH="$PATH:${CARGO_HOME:-$HOME/.cargo}/bin"
fi

if [ "$PWD" != "$repo_root" ]; then
  printf '[FAIL] bin/lint must run from the repo root: %s\n' "$repo_root" >&2
  exit 1
fi

# Only a landing candidate with a verified, preceding main can skip live
# advisory lookup. Comparing to the worktree includes staged and unstaged edits.
advisories_required() {
  [ "${BIOMCP_ADVISORY_SCOPE:-always}" = landing ] || return 0
  local base head
  base="$(git rev-parse --verify 'refs/remotes/origin/main^{commit}' 2>/dev/null)" || return 0
  head="$(git rev-parse --verify 'HEAD^{commit}' 2>/dev/null)" || return 0
  [ "$base" != "$head" ] || return 0
  git merge-base --is-ancestor "$base" "$head" 2>/dev/null || return 0
  local inputs=(Cargo.lock Cargo.toml uv.lock pyproject.toml)
  git ls-files --error-unmatch -- "${inputs[@]}" >/dev/null 2>&1 || return 0
  if git diff --quiet "$base" -- "${inputs[@]}"; then
    return 1
  fi
  return 0
}

run_osv_advisories() {
  local advisory_tool_dir
  advisory_tool_dir="$(tools/bootstrap-lint-tools --advisories)" || return 1
  uv run --no-sync python tools/check-dependency-advisories "$advisory_tool_dir/osv-scanner"
}

run_tracked_text_checks() {
  if ! python3 "$repo_root/tools/check-tracked-text" "$repo_root"; then
    status=1
  fi
}

has_tracked_files() {
  git ls-files -- "$1" | grep -q .
}

run_tracked_text_checks
capture_checker='tools/check-source-capture-receipts.py'
capture_manifest='testdata/sources/capture-receipts.json'
if [ -f "$capture_checker" ] && [ -f "$capture_manifest" ]; then
  run_required 'Source capture and fixture-key contract' \
    python3 "$capture_checker" --root testdata/sources
elif [ ! -f "$capture_checker" ] && [ ! -f "$capture_manifest" ]; then
  skip 'Source capture and fixture-key contract (not configured)'
else
  fail 'Source capture and fixture-key contract (incomplete configuration; checker and manifest are both required)'
fi
if [ -f scripts/check-mcp-registry-server.sh ]; then
  run_required 'MCP registry metadata check' bash scripts/check-mcp-registry-server.sh
else
  skip 'MCP registry metadata check (not configured)'
fi

if [ -f scripts/check-changelog-coverage.py ]; then
  run_required 'Branch changelog coverage' python3 scripts/check-changelog-coverage.py --branch
else
  skip 'Branch changelog coverage (not configured)'
fi
if [ -f scripts/check-no-climb-tracked.sh ]; then
  run_required 'Tracked scratch check' bash scripts/check-no-climb-tracked.sh
else
  skip 'Tracked scratch check (not configured)'
fi

if has_tracked_files '*.py'; then
  python_tool="$repo_root/.venv/bin/python"
  ruff_version="$(sed -nE 's/^[[:space:]]*"ruff==([^"]+)",?$/\1/p' pyproject.toml)"
  if [[ "$ruff_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [ -x "$python_tool" ] && \
      actual_ruff_version="$("$python_tool" -m ruff --version)" && \
      [ "$actual_ruff_version" = "ruff $ruff_version" ]; then
    run_required 'Python lint (ruff check .)' "$python_tool" -m ruff check .
  else
    fail 'Python lint (run make sync-python-dev for the declared Ruff pin)'
  fi
else
  skip 'Python lint (no tracked Python files)'
fi

if [ -f tools/check-shell-workflows ]; then
  run_required 'Bash and workflow lint' python3 tools/check-shell-workflows
else
  skip 'Bash and workflow lint (checker not configured)'
fi

if [ -f build.zig ] || has_tracked_files '*.zig'; then
  run_required 'Zig lint (zig fmt --check .)' zig fmt --check .
else
  skip 'Zig lint (not detected)'
fi

if [ -f Cargo.toml ]; then
  read -r -a routine_cargo_features <<< "${ROUTINE_CARGO_FEATURES:---no-default-features}"
  run_required 'Rust lint (cargo fmt --check)' cargo fmt --check
  run_required 'Rust lint (cargo clippy --all-targets -- -D warnings)' tools/with-build-identity cargo clippy --all-targets "${routine_cargo_features[@]}" -- -D warnings
  # Ask cargo, not PATH. Cargo finds its own subcommands in ~/.cargo/bin
  # whatever PATH says, so `command -v cargo-deny` reports missing on any
  # caller with a trimmed PATH — which is how the factory runs us.
  if cargo deny --version >/dev/null 2>&1; then
    run_required 'Rust license lint (cargo deny check licenses)' cargo deny check licenses
    if advisories_required; then
      run_required 'Rust advisory lint (cargo deny check advisories)' cargo deny check advisories
      run_required 'Dependency advisory lint (OSV Cargo.lock and uv.lock)' run_osv_advisories
    else
      skip 'Rust advisory lint (dependency inputs unchanged since origin/main)'
      skip 'Dependency advisory lint (dependency inputs unchanged since origin/main)'
    fi
  else
    printf 'Install pinned lint tools with: tools/bootstrap-lint-tools\n'
    fail 'Rust license lint (cargo-deny missing)'
  fi
else
  skip 'Rust lint (not detected)'
fi

if [ -f package.json ]; then
  run_required 'JS/TS lint (npm run lint --if-present)' npm run lint --if-present
else
  skip 'JS/TS lint (not detected)'
fi

exit "$status"
