name: PR Validation

on:
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
      - reopened

permissions:
  contents: read

jobs:
  # Detect changed paths to gate expensive test jobs
  changes:
    name: Detect Changes
    runs-on: ubuntu-latest
    permissions:
      contents: read
    outputs:
      training: ${{ steps.filter.outputs.training }}
      dm_tools: ${{ steps.filter.outputs.dm_tools }}
      data_pipeline: ${{ steps.filter.outputs.data_pipeline }}
      inference: ${{ steps.filter.outputs.inference }}
      dv_backend: ${{ steps.filter.outputs.dv_backend }}
      dv_frontend: ${{ steps.filter.outputs.dv_frontend }}
      fuzz: ${{ steps.filter.outputs.fuzz }}
      docusaurus: ${{ steps.filter.outputs.docusaurus }}
      pester: ${{ steps.filter.outputs.pester }}
    steps:
      - name: Checkout
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd  # v6.0.2
        with:
          fetch-depth: 0
          persist-credentials: false
      - name: Compute path filters
        id: filter
        env:
          BASE_SHA: ${{ github.event.pull_request.base.sha }}
          HEAD_SHA: ${{ github.event.pull_request.head.sha }}
        run: |
          set -euo pipefail
          FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")
          echo "Changed files:"
          echo "$FILES"
          match() { echo "$FILES" | grep -qE "$1" && echo true || echo false; }
          {
            echo "training=$(match '^training/')"
            echo "dm_tools=$(match '^data-management/tools/')"
            echo "data_pipeline=$(match '^data-pipeline/capture/')"
            echo "inference=$(match '^fleet-deployment/inference/')"
            echo "dv_backend=$(match '^data-management/viewer/backend/')"
            echo "dv_frontend=$(match '^data-management/viewer/frontend/')"
            echo "fuzz=$(match '^(tests/fuzz|tests/fuzz_harness\\.py|tests/generate_fuzz_corpus\\.py|data-management/viewer/backend/src/|training/)')"
            echo "docusaurus=$(match '^docs/docusaurus/')"
            echo "pester=$(match '(\\.ps1|\\.psm1|\\.psd1)$')"
          } >> "$GITHUB_OUTPUT"

  # Validate that all `uses: ./.github/workflows/*.yml` references resolve
  workflow-refs-check:
    name: Workflow Refs Check
    uses: ./.github/workflows/workflow-refs-check.yml
    permissions:
      contents: read

  # Spell checking using cspell
  spell-check:
    name: Spell Check
    uses: ./.github/workflows/spell-check.yml
    permissions:
      contents: read

  # Markdown linting using markdownlint-cli2
  markdown-lint:
    name: Markdown Lint
    uses: ./.github/workflows/markdown-lint.yml
    permissions:
      contents: read

  # Markdown table formatting check
  table-format:
    name: Table Format
    uses: ./.github/workflows/table-format.yml
    permissions:
      contents: read

  # Frontmatter validation for markdown files
  frontmatter-validation:
    name: Frontmatter Validation
    uses: ./.github/workflows/frontmatter-validation.yml
    with:
      changed-files-only: true
    permissions:
      contents: read

  # ms.date Freshness Check
  msdate-freshness:
    name: ms.date Freshness Check
    uses: ./.github/workflows/msdate-freshness-check.yml
    with:
      staleness-threshold-days: 90
      changed-files-only: true
      soft-fail: false
    permissions:
      contents: read

  # PowerShell script analysis
  psscriptanalyzer:
    name: PSScriptAnalyzer
    uses: ./.github/workflows/ps-script-analyzer.yml
    with:
      changed-files-only: true
    permissions:
      contents: read

  # YAML/actionlint workflow linting
  yaml-lint:
    name: YAML Lint
    uses: ./.github/workflows/yaml-lint.yml
    with:
      changed-files-only: true
    permissions:
      contents: read

  # Link language locale check
  link-lang-check:
    name: Link Language Check
    uses: ./.github/workflows/link-lang-check.yml
    permissions:
      contents: read

  # Markdown link validation
  markdown-link-check:
    name: Markdown Link Check
    uses: ./.github/workflows/markdown-link-check.yml
    permissions:
      contents: read

  # Dependency review for security vulnerabilities
  dependency-review:
    name: Dependency Review
    uses: ./.github/workflows/dependency-review.yml
    permissions:
      contents: read
      pull-requests: write

  # SHA pinning compliance for GitHub Actions and dependencies
  dependency-pinning:
    name: Dependency Pinning
    uses: ./.github/workflows/dependency-pinning-scan.yml
    permissions:
      contents: read
      security-events: write  # Required for SARIF upload to Security tab
    with:
      upload-sarif: true
      exclude-paths: 'scripts/tests/Fixtures/**,shared/ci/tests/Fixtures/**'

  # PowerShell Pester test execution
  pester-tests:
    name: Pester Tests
    needs: changes
    if: needs.changes.outputs.pester == 'true'
    uses: ./.github/workflows/pester-tests.yml
    with:
      code-coverage: true
      changed-files-only: false
      soft-fail: false
    permissions:
      contents: read
      id-token: write

  # Dataviewer frontend lint, type-check, and unit tests
  dataviewer-frontend-tests:
    name: Dataviewer Frontend Tests
    needs: changes
    if: needs.changes.outputs.dv_frontend == 'true'
    uses: ./.github/workflows/dataviewer-frontend-tests.yml
    with:
      code-coverage: true
    permissions:
      contents: read
      id-token: write

  # Docusaurus documentation site tests and build verification
  docusaurus-tests:
    name: Docusaurus Tests
    needs: changes
    if: needs.changes.outputs.docusaurus == 'true'
    uses: ./.github/workflows/docusaurus-tests.yml
    with:
      soft-fail: false
    permissions:
      contents: read

  # Pytest: training component
  pytest-training:
    name: Pytest Training
    needs: changes
    if: needs.changes.outputs.training == 'true'
    uses: ./.github/workflows/pytest-training.yml
    with:
      code-coverage: true
    permissions:
      contents: read
      id-token: write

  # Pytest: data-management/tools component
  pytest-dm-tools:
    name: Pytest DM Tools
    needs: changes
    if: needs.changes.outputs.dm_tools == 'true'
    uses: ./.github/workflows/pytest-dm-tools.yml
    with:
      code-coverage: true
    permissions:
      contents: read
      id-token: write

  # Pytest: data-pipeline component
  pytest-data-pipeline:
    name: Pytest Data Pipeline
    needs: changes
    if: needs.changes.outputs.data_pipeline == 'true'
    uses: ./.github/workflows/pytest-data-pipeline.yml
    with:
      code-coverage: true
    permissions:
      contents: read
      id-token: write

  # Pytest: inference component
  pytest-inference:
    name: Pytest Inference
    needs: changes
    if: needs.changes.outputs.inference == 'true'
    uses: ./.github/workflows/pytest-inference.yml
    with:
      code-coverage: true
    permissions:
      contents: read
      id-token: write

  # Dataviewer backend pytest execution
  dataviewer-backend-pytests:
    name: Dataviewer Backend Pytest
    needs: changes
    if: needs.changes.outputs.dv_backend == 'true'
    uses: ./.github/workflows/dataviewer-backend-pytests.yml
    with:
      code-coverage: true
    permissions:
      contents: read
      id-token: write

  # Evaluation domain pytest execution
  evaluation-pytests:
    name: Evaluation Pytest
    uses: ./.github/workflows/evaluation-pytests.yml
    with:
      code-coverage: true
    permissions:
      contents: read
      id-token: write

  # Fuzz regression via deterministic corpus-based tests
  fuzz-regression-tests:
    name: Fuzz Regression Tests
    needs: changes
    if: needs.changes.outputs.fuzz == 'true'
    uses: ./.github/workflows/fuzz-regression-tests.yml
    with:
      code-coverage: true
    permissions:
      contents: read
      id-token: write

  # Python linting using ruff
  python-lint:
    name: Python Lint
    uses: ./.github/workflows/python-lint.yml
    permissions:
      contents: read

  # Terraform linting using TFLint
  terraform-lint:
    name: Terraform Lint
    uses: ./.github/workflows/terraform-lint.yml
    with:
      soft-fail: false
    permissions:
      contents: read

  # Terraform format and validate checks
  terraform-validation:
    name: Terraform Validation
    uses: ./.github/workflows/terraform-validation.yml
    with:
      soft-fail: false
      changed-files-only: true
    permissions:
      contents: read

  # Terraform security scanning via Checkov (soft-fail until matrix is fully addressed)
  terraform-security:
    name: Terraform Security
    uses: ./.github/workflows/terraform-security.yml
    with:
      soft-fail: true
      working-directory: infrastructure/terraform
    permissions:
      contents: read
      security-events: write

  # Terraform test execution with Codecov Test Analytics
  terraform-tests:
    name: Terraform Tests
    uses: ./.github/workflows/terraform-tests.yml
    with:
      soft-fail: true
      changed-files-only: true
      code-coverage: true
    permissions:
      contents: read
      id-token: write

  # Go linting using golangci-lint
  go-lint:
    name: Go Lint
    uses: ./.github/workflows/go-lint.yml
    with:
      soft-fail: false
      changed-files-only: true
    permissions:
      contents: read

  # Terraform documentation freshness check
  terraform-docs-check:
    name: Terraform Docs Check
    uses: ./.github/workflows/terraform-docs-check.yml
    with:
      soft-fail: true
      changed-files-only: true
    permissions:
      contents: read

  # Go tests
  go-tests:
    name: Go Tests
    uses: ./.github/workflows/go-tests.yml
    with:
      soft-fail: true
      changed-files-only: true
      code-coverage: true
    permissions:
      contents: read
      id-token: write

  # ShellCheck linting for shell scripts
  shellcheck:
    name: ShellCheck
    uses: ./.github/workflows/shellcheck.yml
    with:
      soft-fail: false
      changed-files-only: true
    permissions:
      contents: read

  # CodeQL security analysis
  codeql-analysis:
    name: CodeQL Analysis
    uses: ./.github/workflows/codeql-analysis.yml
    permissions:
      contents: read
      security-events: write
      actions: read

  # Single required-status-check aggregator. Set this as the sole required
  # check in branch protection. Skipped jobs (gated out by changes) pass;
  # any failure or cancellation in a needed job fails the summary.
  pr-validation-summary:
    if: always()
    runs-on: ubuntu-latest
    needs:
      - changes
      - workflow-refs-check
      - spell-check
      - markdown-lint
      - table-format
      - frontmatter-validation
      - msdate-freshness
      - psscriptanalyzer
      - yaml-lint
      - link-lang-check
      - markdown-link-check
      - dependency-review
      - dependency-pinning
      - pester-tests
      - dataviewer-frontend-tests
      - docusaurus-tests
      - pytest-training
      - pytest-dm-tools
      - pytest-data-pipeline
      - pytest-inference
      - dataviewer-backend-pytests
      - evaluation-pytests
      - fuzz-regression-tests
      - python-lint
      - terraform-lint
      - terraform-validation
      - terraform-tests
      - go-lint
      - terraform-docs-check
      - go-tests
      - shellcheck
      - codeql-analysis
    permissions:
      contents: read
    steps:
      - name: Evaluate needed job results
        env:
          NEEDS_JSON: ${{ toJSON(needs) }}
        run: |
          set -euo pipefail
          echo "$NEEDS_JSON"
          failed=$(echo "$NEEDS_JSON" | jq -r '
            to_entries
            | map(select(.value.result == "failure" or .value.result == "cancelled"))
            | map(.key)
            | join(",")')
          if [ -n "$failed" ]; then
            echo "::error::Required jobs failed or were cancelled: $failed"
            exit 1
          fi
          echo "All required jobs succeeded or were skipped via path filtering."
