name: CI

on:
  push:
    branches: [main]
    tags: ['v*']
  pull_request:

# Least privilege by default; the CodeQL job widens its own scope locally.
permissions:
  contents: read

# A newer push to the same ref cancels the in-flight run.
concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

jobs:
  frontend:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm run lint
      - run: npm run format:check
      - run: npm run check:tokens
      - run: npm test
      # --localize builds every configured locale (nl + en, angular.json's i18n
      # block) in one pass; i18nMissingTranslation:"error" (angular.json) fails
      # this step if messages.en.xlf is missing a unit the source (WP-20) gains.
      - run: npx ng build --localize
      # The shipped bundle must stay clean; dev-only advisories are excluded.
      - run: npm audit --omit=dev

  storybook-a11y:
    # Axe runs against every story in the static build; a violation fails the build.
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: npm run build-storybook
      - run: npm run test-storybook:ci

  backend:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 10.0.x
      - run: dotnet format backend/BigRegister.slnx --verify-no-changes
      - run: dotnet test backend/BigRegister.slnx

  e2e:
    # Smoke-level Playwright run against the REAL FE+backend (WP-19) — a fresh
    # runner checkout per run, so there's no bigregister.db (WP-22, gitignored)
    # left over from a prior run to leak state in; the backend creates + migrates
    # an empty one on this boot, same as a fresh clone always has.
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: npm
      - uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 10.0.x
      - run: npm ci
      - run: npx playwright install --with-deps chromium
      - run: dotnet run --project backend/src/BigRegister.Api --urls http://localhost:5000 &
      - run: npx ng serve --proxy-config proxy.conf.json &
      - run: npx wait-on http://localhost:5000/swagger http://localhost:4200
      - run: npm run e2e

  codeql:
    # Static analysis (SAST) for both sides; results appear under the Security tab.
    runs-on: ubuntu-latest
    timeout-minutes: 20
    permissions:
      security-events: write
      contents: read
      actions: read
    strategy:
      fail-fast: false
      matrix:
        language: [javascript-typescript, csharp]
    steps:
      - uses: actions/checkout@v4
      - if: matrix.language == 'csharp'
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 10.0.x
      - uses: github/codeql-action/init@v3
        with:
          languages: ${{ matrix.language }}
      - uses: github/codeql-action/autobuild@v3
      - uses: github/codeql-action/analyze@v3

  api-client-drift:
    # The committed typed client must match the backend OpenAPI doc.
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: npm
      - uses: actions/setup-dotnet@v4
        with:
          # 8.0 for the bundled NSwag runtime, 10.0 to build/emit the spec.
          dotnet-version: |
            8.0.x
            10.0.x
      - run: npm ci
      - run: npm run gen:api
      - run: git diff --exit-code src/app/shared/infrastructure/api-client.ts backend/swagger.json
