name: CI

on:
  pull_request:
    branches: [main]

jobs:
  check:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4

      - uses: astral-sh/setup-uv@v4
        with:
          version: "latest"

      - run: uv sync --extra dev

      - name: Check lockfile is up to date
        run: git diff --exit-code uv.lock

      - name: Lint
        run: uv run ruff check

      - name: Format check
        run: uv run ruff format --check

      - name: Unit tests with coverage
        run: uv run pytest --cov --cov-report=json:coverage-unit.json

      - name: Upload unit coverage
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: coverage-unit
          path: |
            coverage-unit.json
            .coverage
          retention-days: 7

      - name: Docker build
        run: docker build -t specwright:ci .

  integration-test:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    needs: check
    steps:
      - uses: actions/checkout@v4

      - uses: astral-sh/setup-uv@v4
        with:
          version: "latest"

      - run: uv sync --extra dev

      - name: Integration tests with coverage
        # --override-ini clears the default addopts="-m 'not integration'" so
        # the explicit -m integration filter is not ANDed with its negation
        run: uv run pytest -m integration --override-ini "addopts=" -v --cov --cov-report=json:coverage-integration.json

      - name: Upload integration coverage
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: coverage-integration
          path: |
            coverage-integration.json
            .coverage
          retention-days: 7

  coverage-report:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    needs: [check, integration-test]
    if: always() && github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4

      - uses: astral-sh/setup-uv@v4
        with:
          version: "latest"

      - run: uv sync --extra dev

      - name: Download unit coverage
        uses: actions/download-artifact@v4
        with:
          name: coverage-unit
          path: coverage-unit
        continue-on-error: true

      - name: Download integration coverage
        uses: actions/download-artifact@v4
        with:
          name: coverage-integration
          path: coverage-integration
        continue-on-error: true

      - name: Combine coverage
        run: |
          # Collect .coverage files for combining
          mkdir -p .coverage-parts
          if [ -f coverage-unit/.coverage ]; then cp coverage-unit/.coverage .coverage-parts/.coverage.unit; fi
          if [ -f coverage-integration/.coverage ]; then cp coverage-integration/.coverage .coverage-parts/.coverage.integration; fi

          if ls .coverage-parts/.coverage.* 1>/dev/null 2>&1; then
            # Run combine from the subdirectory; coverage finds pyproject.toml
            # (with [tool.coverage.paths] remapping) via upward directory search
            cd .coverage-parts
            uv run coverage combine
            uv run coverage json -o ../coverage-combined.json
            cd ..
          fi

          # Copy individual JSON reports to working directory
          if [ -f coverage-unit/coverage-unit.json ]; then cp coverage-unit/coverage-unit.json .; fi
          if [ -f coverage-integration/coverage-integration.json ]; then cp coverage-integration/coverage-integration.json .; fi

      - name: Generate coverage comment
        run: |
          uv run python scripts/coverage_report.py \
            --unit coverage-unit.json \
            --integration coverage-integration.json \
            --combined coverage-combined.json \
            --commit "${{ github.event.pull_request.head.sha }}" \
            > coverage-comment.md

      - name: Post or update PR comment
        if: github.event_name == 'pull_request'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
          REPO: ${{ github.repository }}
        run: |
          SENTINEL="<!-- coverage-report -->"

          # Find existing coverage comment
          COMMENT_ID=$(gh api "repos/${REPO}/issues/${PR_NUMBER}/comments" \
            --paginate --jq ".[] | select(.body | startswith(\"${SENTINEL}\")) | .id" \
            | head -1)

          if [ -n "$COMMENT_ID" ]; then
            gh api "repos/${REPO}/issues/comments/${COMMENT_ID}" \
              --method PATCH \
              --field body=@coverage-comment.md
          else
            gh api "repos/${REPO}/issues/${PR_NUMBER}/comments" \
              --method POST \
              --field body=@coverage-comment.md
          fi

      - name: Upload combined coverage
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: coverage-combined
          path: coverage-combined.json
          retention-days: 90
          if-no-files-found: ignore

  helm-lint:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      - uses: actions/checkout@v4

      - name: Set up Helm
        uses: azure/setup-helm@v4

      - name: Lint chart
        run: helm lint chart/specwright/

      - name: Lint with production values
        run: helm lint chart/specwright/ -f chart/specwright/values-production.yaml
