name: test

on:
  push:
    branches:
    - master
  pull_request:
    branches:
    - master

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
  cancel-in-progress: true

env:
  PYTHONUNBUFFERED: "1"
  FORCE_COLOR: "1"

jobs:
  run:
    name: Python ${{ matrix.python-version }} on ${{ startsWith(matrix.os, 'macos-') && 'macOS' || startsWith(matrix.os, 'windows-') && 'Windows' || 'Linux' }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t"]

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v6
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install uv
      uses: astral-sh/setup-uv@v3

    - name: Install ourself
      run: |
        uv pip install --system -e .
        hatch env create

    - name: Run static analysis
      run: hatch fmt --check

    - name: Check types
      run: hatch run types:check

    - name: Run tests
      run: hatch test --python ${{ matrix.python-version }} --cover-quiet --randomize --parallel --retries 5 --retry-delay 3

    - name: Disambiguate coverage filename
      run: mv .coverage ".coverage.${{ matrix.os }}.${{ matrix.python-version }}"

    - name: Upload coverage data
      uses: actions/upload-artifact@v4
      with:
        include-hidden-files: true
        name: coverage-${{ matrix.os }}-${{ matrix.python-version }}
        path: .coverage*

  coverage:
    name: Report coverage
    runs-on: ubuntu-latest
    needs:
    - run

    steps:
    - uses: actions/checkout@v4

    - name: Install Hatch
      uses: pypa/hatch@install

    - name: Trigger build for auto-generated files
      run: hatch build --hooks-only

    - name: Download coverage data
      uses: actions/download-artifact@v4
      with:
        pattern: coverage-*
        merge-multiple: true

    - name: Combine coverage data
      run: hatch run coverage:combine

    - name: Export coverage reports
      run: |
        hatch run coverage:report-xml
        hatch run coverage:report-uncovered-html

    - name: Upload uncovered HTML report
      uses: actions/upload-artifact@v4
      with:
        name: uncovered-html-report
        path: htmlcov

    - name: Generate coverage summary
      run: hatch run coverage:generate-summary

    - name: Write coverage summary report
      if: github.event_name == 'pull_request'
      run: hatch run coverage:write-summary-report

    - name: Update coverage pull request comment
      if: github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork
      uses: marocchino/sticky-pull-request-comment@v2
      with:
        path: coverage-report.md

  downstream:
    name: Downstream builds with Python ${{ matrix.python-version }}
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

    steps:
    - uses: actions/checkout@v4

    - name: Set up Python ${{ matrix.python-version }}
      uses: actions/setup-python@v5
      with:
        python-version: ${{ matrix.python-version }}

    - name: Install tools
      run: pip install --upgrade -r backend/tests/downstream/requirements.txt

    - name: Build downstream projects
      run: python backend/tests/downstream/integrate.py

  # https://github.com/marketplace/actions/alls-green#why
  check: # This job does nothing and is only used for the branch protection
    if: always()

    needs:
    - coverage
    - downstream

    runs-on: ubuntu-latest

    steps:
    - name: Decide whether the needed jobs succeeded or failed
      uses: re-actors/alls-green@release/v1
      with:
        jobs: ${{ toJSON(needs) }}
