name: Publish Reports to GitHub Pages

on:
  push:
    branches:
      - main
    paths:
      - 'docs/**'
      - 'mkdocs.yml'
      - 'src/main/**'
      - 'src/test/**'
      - 'src/jmh/**'
      - 'models/**'
      - 'build-logic/**'
      - 'build.gradle'
      - 'gradle.properties'
      - 'gradle.lockfile'
      - 'settings.gradle'
      - 'gradle/**'
      - 'dependency-suppression.xml'
      - 'gradlew'
      - 'gradlew.bat'
      - '.github/workflows/pages.yml'
      - '.github/workflows/benchmarks.yml'
      - 'tools/generate-pages-badges.py'
  workflow_dispatch:

permissions:
  contents: write

concurrency:
  group: pages-${{ github.ref }}
  cancel-in-progress: true

jobs:
  publish-pages:
    name: Publish static reports
    runs-on: ubuntu-latest

    steps:
      - name: Check out source repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Validate Gradle wrapper
        uses: gradle/actions/wrapper-validation@v4

      - name: Set up Temurin JDK 21
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'

      - name: Set up Gradle caching and instrumentation
        uses: gradle/actions/setup-gradle@v4

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.x'

      - name: Install MkDocs Material
        run: python -m pip install --upgrade pip mkdocs-material

      - name: Verify reproducibility inputs
        shell: bash
        run: |
          set -euo pipefail
          test -f gradle.lockfile
          test -f gradle.properties
          test -f gradle/verification-metadata.xml

      - name: Build reports for publication
        run: ./gradlew --no-daemon clean ciRelease pmdMain javadoc jacocoCiReleaseReport pitest jmh -Pjmh.includes='.*EnglishStemmerComparisonBenchmark.*' :cyclonedxDirectBom

      - name: Prepare gh-pages worktree
        shell: bash
        run: |
          set -euo pipefail

          if git ls-remote --exit-code --heads origin gh-pages > /dev/null 2>&1; then
            git fetch origin gh-pages:gh-pages
            git worktree add .gh-pages gh-pages
          else
            git worktree add --detach .gh-pages
            cd .gh-pages
            git checkout --orphan gh-pages
            git rm -rf . > /dev/null 2>&1 || true
            cd ..
          fi

      - name: Prepare staged MkDocs source
        run: ./gradlew --no-daemon prepareMkDocsSource verifyModelCatalogDocumentation

      - name: Stage published reports
        shell: bash
        run: |
          set -euo pipefail

          TEST_REPORT_DIR="build/reports/tests/ciRelease"
          JACOCO_REPORT_DIR="build/reports/jacoco/jacocoCiReleaseReport"

          SITE_DIR=".gh-pages"
          RUN_DIR="${SITE_DIR}/builds/${GITHUB_RUN_NUMBER}"
          RUN_METRICS_DIR="${RUN_DIR}/metrics"
          LATEST_DIR="${SITE_DIR}/builds/latest"
          LATEST_METRICS_DIR="${LATEST_DIR}/metrics"

          mkdir -p "${RUN_DIR}"
          rm -rf "${LATEST_DIR}"
          mkdir -p "${LATEST_DIR}" "${RUN_METRICS_DIR}" "${LATEST_METRICS_DIR}"

          cp -R build/docs/javadoc "${RUN_DIR}/javadoc"
          cp -R build/docs/javadoc "${LATEST_DIR}/javadoc"

          cp -R "${TEST_REPORT_DIR}" "${RUN_DIR}/test"
          cp -R "${TEST_REPORT_DIR}" "${LATEST_DIR}/test"

          cp -R build/reports/pmd "${RUN_DIR}/pmd"
          cp -R build/reports/pmd "${LATEST_DIR}/pmd"

          cp -R "${JACOCO_REPORT_DIR}/html" "${RUN_DIR}/coverage"
          cp -R "${JACOCO_REPORT_DIR}/html" "${LATEST_DIR}/coverage"

          cp -R build/reports/pitest "${RUN_DIR}/pitest"
          cp -R build/reports/pitest "${LATEST_DIR}/pitest"

          JMH_TXT_LINK=''
          JMH_CSV_LINK=''
          JMH_TXT_LATEST_LINK=''
          JMH_CSV_LATEST_LINK=''
          JMH_TXT_REPORT_MD='- Benchmark results (TXT): not currently available'
          JMH_CSV_REPORT_MD='- Benchmark results (CSV): not currently available'
          DEPENDENCY_CHECK_LINK=''
          DEPENDENCY_CHECK_LATEST_LINK=''
          DEPENDENCY_CHECK_REPORT_MD='- Dependency vulnerability report: not currently available'
          SBOM_JSON_LINK=''
          SBOM_XML_LINK=''
          SBOM_JSON_LATEST_LINK=''
          SBOM_XML_LATEST_LINK=''
          SBOM_JSON_REPORT_MD='- SBOM (JSON): not currently available'
          SBOM_XML_REPORT_MD='- SBOM (XML): not currently available'

          if [ -d "build/reports/jmh" ]; then
            cp -R build/reports/jmh "${RUN_DIR}/jmh"
            cp -R build/reports/jmh "${LATEST_DIR}/jmh"

            if [ -f "${RUN_DIR}/jmh/jmh-results.txt" ]; then
              JMH_TXT_LINK='<li><a href="./jmh/jmh-results.txt">Benchmark Results (TXT)</a></li>'
              JMH_TXT_LATEST_LINK='<li><a href="./builds/latest/jmh/jmh-results.txt">Benchmark Results (TXT)</a></li>'
              JMH_TXT_REPORT_MD='- [JMH benchmark results (TXT)](https://leogalambos.github.io/Radixor/builds/latest/jmh/jmh-results.txt)'
            fi
            if [ -f "${RUN_DIR}/jmh/jmh-results.csv" ]; then
              JMH_CSV_LINK='<li><a href="./jmh/jmh-results.csv">Benchmark Results (CSV)</a></li>'
              JMH_CSV_LATEST_LINK='<li><a href="./builds/latest/jmh/jmh-results.csv">Benchmark Results (CSV)</a></li>'
              JMH_CSV_REPORT_MD='- [JMH benchmark results (CSV)](https://leogalambos.github.io/Radixor/builds/latest/jmh/jmh-results.csv)'
            fi

            HAS_JMH="true"
          else
            HAS_JMH="false"
          fi

          if [ -d "build/reports/dependency-check" ]; then
            cp -R build/reports/dependency-check "${RUN_DIR}/dependency-check"
            cp -R build/reports/dependency-check "${LATEST_DIR}/dependency-check"

            if [ -f "${RUN_DIR}/dependency-check/dependency-check-report.html" ]; then
              DEPENDENCY_CHECK_LINK='<li><a href="./dependency-check/dependency-check-report.html">Dependency Vulnerability Report</a></li>'
              DEPENDENCY_CHECK_LATEST_LINK='<li><a href="./builds/latest/dependency-check/dependency-check-report.html">Dependency Vulnerability Report</a></li>'
              DEPENDENCY_CHECK_REPORT_MD='- [Dependency vulnerability report](https://leogalambos.github.io/Radixor/builds/latest/dependency-check/dependency-check-report.html)'
            fi
          fi

          if [ -d "build/reports/sbom" ]; then
            cp -R build/reports/sbom "${RUN_DIR}/sbom"
            cp -R build/reports/sbom "${LATEST_DIR}/sbom"
            SBOM_JSON_LINK='<li><a href="./sbom/radixor-sbom.json">SBOM (JSON)</a></li>'
            SBOM_XML_LINK='<li><a href="./sbom/radixor-sbom.xml">SBOM (XML)</a></li>'
            SBOM_JSON_LATEST_LINK='<li><a href="./builds/latest/sbom/radixor-sbom.json">SBOM (JSON)</a></li>'
            SBOM_XML_LATEST_LINK='<li><a href="./builds/latest/sbom/radixor-sbom.xml">SBOM (XML)</a></li>'
            SBOM_JSON_REPORT_MD='- [SBOM (JSON)](https://leogalambos.github.io/Radixor/builds/latest/sbom/radixor-sbom.json)'
            SBOM_XML_REPORT_MD='- [SBOM (XML)](https://leogalambos.github.io/Radixor/builds/latest/sbom/radixor-sbom.xml)'
          fi

          python3 \
            ./tools/generate-pages-badges.py \
            --jacoco-xml "${JACOCO_REPORT_DIR}/jacocoCiReleaseReport.xml" \
            --pit-xml build/reports/pitest/mutations.xml \
            --jmh-csv build/reports/jmh/jmh-results.csv \
            --run-metrics-dir "${RUN_METRICS_DIR}" \
            --latest-metrics-dir "${LATEST_METRICS_DIR}"

          COVERAGE_BADGE_LINK='<li><a href="./metrics/coverage-badge.json">Coverage Badge Metadata</a></li>'
          COVERAGE_BADGE_LATEST_LINK='<li><a href="./builds/latest/metrics/coverage-badge.json">Coverage Badge Metadata</a></li>'
          MUTATION_BADGE_LINK='<li><a href="./metrics/pitest-badge.json">Mutation Badge Metadata</a></li>'
          MUTATION_BADGE_LATEST_LINK='<li><a href="./builds/latest/metrics/pitest-badge.json">Mutation Badge Metadata</a></li>'
          COVERAGE_BADGE_REPORT_MD='- [Coverage badge metadata](https://leogalambos.github.io/Radixor/builds/latest/metrics/coverage-badge.json)'
          MUTATION_BADGE_REPORT_MD='- [Mutation badge metadata](https://leogalambos.github.io/Radixor/builds/latest/metrics/pitest-badge.json)'

          if [ ! -f "${RUN_METRICS_DIR}/coverage-badge.json" ]; then
            COVERAGE_BADGE_LINK='<li>Coverage Badge Metadata: not available</li>'
            COVERAGE_BADGE_LATEST_LINK='<li>Coverage Badge Metadata: not available</li>'
            COVERAGE_BADGE_REPORT_MD='- Coverage badge metadata: not currently available'
          fi

          if [ ! -f "${RUN_METRICS_DIR}/pitest-badge.json" ]; then
            MUTATION_BADGE_REPORT_MD='- Mutation badge metadata: not currently available'
          fi

          cat > "${RUN_DIR}/index.html" <<EOF
          <!doctype html>
          <html lang="en">
          <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title>Radixor Reports - Build ${GITHUB_RUN_NUMBER}</title>
            <style>
              body { font-family: Arial, sans-serif; max-width: 900px; margin: 2rem auto; padding: 0 1rem; line-height: 1.5; }
              h1, h2 { margin-bottom: 0.5rem; }
              ul { padding-left: 1.25rem; }
              code { background: #f4f4f4; padding: 0.1rem 0.3rem; }
              .meta { color: #555; }
            </style>
          </head>
          <body>
            <h1>Radixor Reports</h1>
            <p class="meta">Build ${GITHUB_RUN_NUMBER} from commit ${GITHUB_SHA}</p>
            <ul>
              <li><a href="./javadoc/">Javadoc</a></li>
              <li><a href="./test/">Release Verification Test Report (ciRelease)</a></li>
              <li><a href="./pmd/main.html">PMD Report</a></li>
              <li><a href="./coverage/">Coverage Report</a></li>
              ${DEPENDENCY_CHECK_LINK:-<li>Dependency Vulnerability Report: not available</li>}
              ${SBOM_JSON_LINK:-<li>SBOM (JSON): not available</li>}
              ${SBOM_XML_LINK:-<li>SBOM (XML): not available</li>}
              ${COVERAGE_BADGE_LINK}
              ${MUTATION_BADGE_LINK}
              <li><a href="./pitest/">Mutation Testing Report</a></li>
              $(
                [ "${HAS_JMH}" = "true" ] && { echo "${JMH_TXT_LINK:-<li>Benchmark Results (TXT): not available</li>}"; echo "${JMH_CSV_LINK:-<li>Benchmark Results (CSV): not available</li>}"; } \
                || echo '<li>Benchmark results: not available in this build</li>'
              )
            </ul>
            <p><a href="../latest/">Open latest report set</a></p>
            <p><a href="../../">Back to report home</a></p>
          </body>
          </html>
          EOF

          cp "${RUN_DIR}/index.html" "${LATEST_DIR}/index.html"

          cat > build/mkdocs-source/reports.md <<EOF
          # CI Reports

          Radixor publishes durable CI artifacts to GitHub Pages on every qualifying run of \`.github/workflows/pages.yml\`.

          ## Primary report entry points

          - [Latest build summary](https://leogalambos.github.io/Radixor/builds/latest/)
          - [Javadoc](https://leogalambos.github.io/Radixor/builds/latest/javadoc/)
          - [Release verification test report (ciRelease)](https://leogalambos.github.io/Radixor/builds/latest/test/)
          - [PMD report](https://leogalambos.github.io/Radixor/builds/latest/pmd/main.html)
          - [JaCoCo coverage report](https://leogalambos.github.io/Radixor/builds/latest/coverage/)
          - [PIT mutation testing report](https://leogalambos.github.io/Radixor/builds/latest/pitest/)
          ${DEPENDENCY_CHECK_REPORT_MD}
          ${SBOM_JSON_REPORT_MD}
          ${SBOM_XML_REPORT_MD}

          ## Benchmark reports and badge metadata

          ${JMH_TXT_REPORT_MD}
          ${JMH_CSV_REPORT_MD}
          ${COVERAGE_BADGE_REPORT_MD}
          ${MUTATION_BADGE_REPORT_MD}

          ## Historical runs

          - [Browse historical build reports](https://leogalambos.github.io/Radixor/builds/)
          EOF

          # Retain only the 10 most recent numbered builds to stay within
          # GitHub Pages capacity limits. The "latest" alias is kept separately.
          mapfile -t EXPIRED_BUILDS < <(
            find "${SITE_DIR}/builds" -mindepth 1 -maxdepth 1 -type d -printf '%P\n' \
              | grep -E '^[0-9]+$' \
              | sort -r -n \
              | tail -n +11
          )

          for build in "${EXPIRED_BUILDS[@]}"; do
            rm -rf "${SITE_DIR}/builds/${build}"
          done

          {
            echo "# Historical Build Reports"
            echo
            echo "The following build report sets are currently published on GitHub Pages."
            echo
            echo "To stay within GitHub Pages capacity limits, only the 10 most recent build report sets are retained."
            echo
            echo "| Build | Published | Link |"
            echo "|---:|---|---|"

            find "${SITE_DIR}/builds" -mindepth 1 -maxdepth 1 -type d ! -name latest -printf '%P\n' \
              | grep -E '^[0-9]+$' \
              | while read -r build; do
                  ts="$(git -C "${SITE_DIR}" log --diff-filter=A --format='%ct' --reverse -- "builds/${build}/index.html" | head -n 1)"
                  if [ -n "${ts}" ]; then
                    published="$(date -u -d "@${ts}" '+%Y-%m-%d %H:%M')"
                  else
                    published="unknown"
                    ts="0"
                  fi
                  printf '%s\t%s\t%s\n' "${ts}" "${build}" "${published}"
                done \
              | sort -r -n -k1,1 \
              | while IFS=$'\t' read -r _ts build published; do
                  echo "| ${build} | ${published} | [Open](../builds/${build}/) |"
                done
          } > build/mkdocs-source/builds.md

      - name: Build documentation site (MkDocs Material)
        shell: bash
        run: |
          set -euo pipefail
          mkdocs build --strict --config-file build/mkdocs/mkdocs.yml
          rsync -a --delete --exclude '.git' --exclude '.git/' --exclude 'builds/' build/mkdocs-site/ .gh-pages/
          mkdir -p .gh-pages/builds
          cp build/mkdocs-site/builds/index.html .gh-pages/builds/index.html
          cat > .gh-pages/.nojekyll <<EOF
          EOF

      - name: Commit and push gh-pages
        shell: bash
        run: |
          set -euo pipefail
          cd .gh-pages

          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

          git add .
          if git diff --cached --quiet; then
            echo "No changes to publish."
            exit 0
          fi

          git commit -m "Publish reports for run ${GITHUB_RUN_NUMBER}"
          git push origin gh-pages
