name: Lint & Type Check

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

concurrency:
  group: lint-ty-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
  detect-changes:
    runs-on: ubuntu-latest
    outputs:
      code: ${{ steps.filter.outputs.code }}
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 20

      - name: Check for code changes
        id: filter
        run: |
          if [ "${{ github.event_name }}" = "pull_request" ]; then
            BASE=${{ github.event.pull_request.base.sha }}
          else
            BASE=${{ github.event.before }}
          fi
          CHANGED=$(git diff --name-only "$BASE" HEAD 2>/dev/null || echo "FORCE_RUN")

          if echo "$CHANGED" | grep -q "FORCE_RUN"; then
            echo "code=true" >> $GITHUB_OUTPUT
            exit 0
          fi

          CODE_CHANGED="false"
          while IFS= read -r file; do
            case "$file" in
              site/*|docs/*|plan/*|skills/*|*.md|LICENSE|.context/*) ;;
              *) CODE_CHANGED="true"; break ;;
            esac
          done <<< "$CHANGED"

          echo "code=$CODE_CHANGED" >> $GITHUB_OUTPUT

  lint-and-type:
    needs: detect-changes
    if: needs.detect-changes.outputs.code == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - name: Install uv
        uses: astral-sh/setup-uv@v7
        with:
          python-version: "3.14t"

      - name: Cache uv dependencies
        uses: actions/cache@v5
        with:
          path: ~/.cache/uv
          key: uv-${{ runner.os }}-${{ hashFiles('uv.lock') }}
          restore-keys: |
            uv-${{ runner.os }}-

      - name: Install dependencies
        run: uv sync --no-sources --group dev

      - name: Run ruff (lint)
        run: uv run ruff check .

      - name: Run ruff (format check)
        run: uv run ruff format . --check

      - name: Run import-linter (architecture contracts)
        run: uv run lint-imports --config .importlinter

      - name: Run ty type checker
        run: |
          set -o pipefail
          # Bengal currently tracks a gradual ty diagnostic floor. Keep output
          # visible for review, but fail only if the floor regresses.
          uv run ty check bengal/ --color never --exit-zero | tee ty.log
          count=$(awk '/Found [0-9]+ diagnostics/ {print $2}' ty.log | tail -n 1)
          count=${count:-0}
          max=570
          echo "ty diagnostics: $count / $max max"
          if [ "$count" -gt "$max" ]; then
            echo "ty diagnostic floor regressed"
            exit 1
          fi

  # Gate job for branch protection — succeeds when lint passes OR was skipped (docs-only)
  lint-ok:
    if: always()
    needs: [detect-changes, lint-and-type]
    runs-on: ubuntu-latest
    steps:
      - name: Decide status
        run: |
          if [ "${{ needs.detect-changes.outputs.code }}" = "false" ]; then
            echo "Docs-only change — lint skipped, CI passes"
            exit 0
          fi
          if [ "${{ needs.lint-and-type.result }}" = "failure" ]; then
            echo "Lint/type check failed"
            exit 1
          fi
          echo "Lint/type check passed"
