#!/usr/bin/env bash
# Ensure dependencies are installed (worktrees start with no node_modules)
if [ ! -d node_modules ]; then
  echo "node_modules missing, running npm install..."
  npm install --prefer-offline || exit 1
fi

echo "Checking PR status for current branch..."
BRANCH=$(git branch --show-current)
if [ -n "$BRANCH" ] && [ "$BRANCH" != "main" ] && [ "$BRANCH" != "master" ]; then
  PR_STATE=$(gh pr view "$BRANCH" --json state --jq '.state' 2>&1)
  if [ "$PR_STATE" = "MERGED" ] || [ "$PR_STATE" = "CLOSED" ]; then
    echo ""
    echo "============================================================"
    echo "ERROR: PR for branch '$BRANCH' is $PR_STATE."
    echo "Do NOT push to a merged/closed PR branch — commits will be orphaned."
    echo "Run: git checkout main && git pull && git checkout -b fix/new-branch"
    echo "============================================================"
    exit 1
  fi
  echo "  Branch PR state: ${PR_STATE:-unknown}"

  # Guard against branch contamination from a dirty local main.
  # A feature/fix branch should never have more than 20 commits — if it does,
  # the branch was likely created from a local main that had unmerged feature branches.
  COMMIT_COUNT=$(git rev-list --count origin/main..HEAD 2>/dev/null || echo 0)
  if [ "$COMMIT_COUNT" -gt 20 ]; then
    echo ""
    echo "============================================================"
    echo "ERROR: Branch '$BRANCH' is $COMMIT_COUNT commits ahead of origin/main."
    echo "This usually means it was branched from a dirty local main."
    echo "Fix: create branches from a worktree or use: git checkout -b <name> origin/main"
    echo "============================================================"
    exit 1
  fi
fi

echo "Checking scripts/package-lock.json sync..."
if git diff --name-only origin/main -- scripts/package.json | grep -q .; then
  if ! git diff --name-only origin/main -- scripts/package-lock.json | grep -q .; then
    echo ""
    echo "============================================================"
    echo "ERROR: scripts/package.json was modified but scripts/package-lock.json was not committed."
    echo "Run: cd scripts && npm install && cd .."
    echo "Then: git add scripts/package-lock.json && git commit --amend --no-edit"
    echo "============================================================"
    exit 1
  fi
fi

echo "Running type check..."
npm run typecheck || exit 1

echo "Running API type check..."
npm run typecheck:api || exit 1

echo "Running CJS syntax check..."
for f in scripts/*.cjs; do
  [ -f "$f" ] && node -c "$f" || exit 1
done

echo "Running Unicode safety check..."
node scripts/check-unicode-safety.mjs || exit 1

echo "Running architectural boundary check..."
npm run lint:boundaries || exit 1

echo "Running rate-limit policy coverage check..."
npm run lint:rate-limit-policies || exit 1

echo "Running premium-fetch parity check..."
npm run lint:premium-fetch || exit 1

echo "Running edge function bundle check..."
while IFS= read -r f; do
  npx esbuild "$f" --bundle --format=esm --platform=browser --outfile=/dev/null 2>/dev/null || {
    echo "ERROR: esbuild failed to bundle $f — this will break Vercel deployment"
    npx esbuild "$f" --bundle --format=esm --platform=browser --outfile=/dev/null
    exit 1
  }
done < <(find api/ -name "*.js" -not -name "_*"; find api/ -maxdepth 1 -name "*.ts" -not -name "_*")

CHANGED_FILES=$(git diff --name-only origin/main...HEAD 2>/dev/null || echo "")
if [ -z "$CHANGED_FILES" ]; then
  CHANGED_FILES=$(git diff --name-only HEAD~1 2>/dev/null || echo "")
fi
if [ -z "$CHANGED_FILES" ]; then
  echo "WARNING: Could not determine changed files, running full test suite as safety fallback."
  RUN_ALL=true
fi

# Determine which test categories to run based on changed files
RUN_RESILIENCE=false
RUN_SEED=false
RUN_SERVER=false
RUN_FRONTEND=false
${RUN_ALL:=false}

if echo "$CHANGED_FILES" | grep -q "^scripts/"; then RUN_SEED=true; fi
if echo "$CHANGED_FILES" | grep -q "^server/"; then RUN_SERVER=true; fi
if echo "$CHANGED_FILES" | grep -q "resilience"; then RUN_RESILIENCE=true; fi
if echo "$CHANGED_FILES" | grep -q "^src/\|^api/"; then RUN_FRONTEND=true; fi
if echo "$CHANGED_FILES" | grep -q "^tests/\|package.json\|tsconfig"; then RUN_ALL=true; fi

if [ "$RUN_ALL" = true ]; then
  echo "Running full unit test suite (test files or config changed)..."
  timeout 300 npx tsx --test tests/*.test.mjs tests/*.test.mts || exit 1
else
  if [ "$RUN_RESILIENCE" = true ]; then
    echo "Running resilience tests (resilience files changed)..."
    timeout 120 npx tsx --test tests/resilience-*.test.mjs tests/resilience-*.test.mts || exit 1
  fi
  if [ "$RUN_SEED" = true ]; then
    echo "Running seed tests (scripts/ changed)..."
    SEED_TESTS=$(echo "$CHANGED_FILES" | grep "^scripts/" | sed 's|scripts/seed-\(.*\)\.mjs|tests/\1-seed.test.mjs|' | grep "^tests/" | while read -r t; do [ -f "$t" ] && echo "$t"; done)
    if [ -n "$SEED_TESTS" ]; then
      timeout 120 npx tsx --test $SEED_TESTS || exit 1
    else
      echo "  No matching seed tests found, skipping."
    fi
  fi
  if [ "$RUN_SERVER" = true ]; then
    echo "Running server handler tests (server/ changed)..."
    timeout 120 npx tsx --test tests/handlers.test.mts tests/server-handlers.test.mjs || exit 1
  fi
  if [ "$RUN_RESILIENCE" = false ] && [ "$RUN_SEED" = false ] && [ "$RUN_SERVER" = false ]; then
    echo "Skipping unit tests (frontend-only changes)."
  fi
fi

echo "Running edge function tests..."
if [ "$RUN_FRONTEND" = true ] || [ "$RUN_ALL" = true ]; then
  node --test tests/edge-functions.test.mjs || exit 1
else
  echo "  Skipped (no api/ or src/ changes)."
fi

echo "Running markdown lint..."
if echo "$CHANGED_FILES" | grep -qE "\.md$|\.mdx$" || [ "$RUN_ALL" = true ]; then
  npm run lint:md || exit 1
else
  echo "  Skipped (no markdown changes)."
fi

echo "Running MDX lint (Mintlify compatibility)..."
if echo "$CHANGED_FILES" | grep -q "\.mdx$" || [ "$RUN_ALL" = true ]; then
  node --test tests/mdx-lint.test.mjs || exit 1
else
  echo "  Skipped (no MDX changes)."
fi

echo "Running proto freshness check..."
if git diff --name-only origin/main -- proto/ src/generated/ docs/api/ Makefile | grep -q .; then
  # Only prepend $HOME/go/bin when buf is NOT already resolvable on the
  # caller's PATH. An unconditional prepend would shadow a developer's
  # preferred `buf` (e.g. Homebrew) with a potentially stale
  # ~/go/bin/buf left over from an older `go install buf@<old>`, which
  # is the exact mixed-version failure this PR is trying to eliminate.
  # Plugin resolution is handled inside `make generate` via a pinned
  # PLUGIN_DIR, so the hook no longer needs to prepend for plugin
  # discovery — only for the `buf` binary itself, and only when there's
  # no other candidate.
  if ! command -v buf >/dev/null 2>&1 && [ -x "$HOME/go/bin/buf" ]; then
    export PATH="$HOME/go/bin:$PATH"
  fi
  # Plugin-presence check: consider a plugin "available" if it's on
  # PATH OR at the default `go install` location ($HOME/go/bin).
  # `make generate` resolves plugins via its own PLUGIN_DIR (GOBIN,
  # then first-entry GOPATH/bin), which matches the $HOME/go/bin
  # default on almost every dev setup. Anything exotic (custom GOBIN
  # not on PATH) still runs `make generate` cleanly because the
  # Makefile's own plugin-executable guard fires there.
  if command -v buf &>/dev/null && { command -v protoc-gen-ts-client &>/dev/null || [ -x "$HOME/go/bin/protoc-gen-ts-client" ]; }; then
    make generate
    if ! git diff --exit-code src/generated/ docs/api/; then
      echo ""
      echo "============================================================"
      echo "ERROR: Proto-generated code is out of date."
      echo "Run 'make generate' locally and commit the updated files."
      echo "============================================================"
      exit 1
    fi
    UNTRACKED=$(git ls-files --others --exclude-standard src/generated/ docs/api/)
    if [ -n "$UNTRACKED" ]; then
      echo ""
      echo "============================================================"
      echo "ERROR: Untracked generated files found:"
      echo "$UNTRACKED"
      echo "Run 'make generate' locally and commit the new files."
      echo "============================================================"
      exit 1
    fi
    echo "Proto-generated code is up to date."
  else
    echo "WARNING: buf or protoc plugins not installed, skipping proto freshness check."
    echo "  Install with: make install-buf install-plugins"
  fi
else
  echo "No proto-related changes, skipping."
fi

echo "Running pro-test bundle freshness check..."
# public/pro/ is NOT built by Vercel — the root build script only runs the
# main app's vite build. The /pro marketing app under pro-test/ must be
# rebuilt manually and its output committed to public/pro/. If source
# changes ship without the rebuild, production /pro serves the old bundle
# (see PR #3227 → #3228: source fix merged but deploy still broken because
# public/pro/ was stale).
#
# Scope to the BRANCH delta ($CHANGED_FILES, computed earlier from
# `git diff origin/main...HEAD`), not the worktree, so unstaged local
# pro-test/ scratch edits don't trigger a slow rebuild on unrelated
# branches. RUN_ALL forces the check when the branch delta couldn't be
# computed (matches the safety fallback used by the test runners above).
#
# Trigger on EITHER pro-test/ OR public/pro/ to match the CI workflow's
# path filter — a bundle-only PR that edits public/pro/ without touching
# pro-test/ (e.g. a fix-forward rebuild, or a hand-edit) is exactly the
# class of change this guard is meant to validate.
if [ "$RUN_ALL" = true ] || echo "$CHANGED_FILES" | grep -qE "^(pro-test|public/pro)/"; then
  if [ ! -d pro-test/node_modules ]; then
    echo "  Installing pro-test deps..."
    (cd pro-test && npm install --prefer-offline) || exit 1
  fi
  (cd pro-test && npm run build >/dev/null) || {
    echo "ERROR: pro-test build failed"
    exit 1
  }
  if ! git diff --exit-code public/pro/; then
    echo ""
    echo "============================================================"
    echo "ERROR: pro-test sources changed but public/pro/ is stale."
    echo "Vercel ships whatever is committed under public/pro/ —"
    echo "it does NOT rebuild pro-test on deploy."
    echo "Fix:"
    echo "  cd pro-test && npm run build && cd .."
    echo "  git add public/pro/ && git commit --amend --no-edit"
    echo "============================================================"
    exit 1
  fi
  UNTRACKED=$(git ls-files --others --exclude-standard public/pro/)
  if [ -n "$UNTRACKED" ]; then
    echo ""
    echo "============================================================"
    echo "ERROR: Untracked files in public/pro/ after rebuild:"
    echo "$UNTRACKED"
    echo "Run: git add public/pro/ && git commit --amend --no-edit"
    echo "============================================================"
    exit 1
  fi
  echo "  pro-test bundle is up to date."
else
  echo "  Skipped (no pro-test/ or public/pro/ changes in branch)."
fi

echo "Running version sync check..."
npm run version:check || exit 1
