name: Deploy Docs to Cloudflare Pages

# Runs in parallel with the existing GitHub Pages deploy workflow during the cutover.
# Once `lumeo.nativ.sh` is pointed at Cloudflare Pages, remove `deploy-docs.yml`.
#
# Required repo secrets (Settings → Secrets and variables → Actions):
#   CLOUDFLARE_API_TOKEN  — create at dash.cloudflare.com/profile/api-tokens using the
#                           "Edit Cloudflare Workers" template and add "Pages:Edit" scope.
#   CLOUDFLARE_ACCOUNT_ID — from any page in the Cloudflare dashboard sidebar.
#   ALGOLIA_APP_ID        - Algolia DocSearch application ID (public; injected into index.html)
#   ALGOLIA_SEARCH_KEY    - Algolia DocSearch search-only API key (public; injected into index.html)
#
# Algolia DocSearch's hosted crawler manages the index — we don't push records
# from CI. If you ever switch off DocSearch onto a regular Algolia Search app,
# add ALGOLIA_ADMIN_KEY and re-enable the indexer step (see scripts/algolia/).

on:
  push:
    branches: [master]
  workflow_dispatch:

permissions:
  contents: read
  deployments: write

concurrency:
  group: "cloudflare-pages"
  cancel-in-progress: false

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '10.0.x'

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: '22'

      - name: Install wasm-tools workload
        run: dotnet workload install wasm-tools

      - name: Install npm deps
        run: |
          npm install --no-audit --no-fund
          (cd docs/Lumeo.Docs && npm install --no-audit --no-fund)

      - name: Generate catalog thumbnails
        # Renders 128 component-catalog PNG thumbnails into
        # docs/Lumeo.Docs/wwwroot/preview-cards/ before dotnet publish so they
        # are picked up as static assets and shipped with the site.
        working-directory: scripts/prerender
        run: |
          npm ci --no-audit --no-fund
          node run-all.mjs

      - name: Publish Blazor WASM
        run: dotnet publish docs/Lumeo.Docs/Lumeo.Docs.csproj -c Release -o release

      - name: Rebuild Tailwind bundles into release
        # Overwrites the stale CSS that dotnet publish's static-web-asset pipeline
        # had copied from the (committed) working tree.
        run: |
          npx @tailwindcss/cli \
            -i ./src/Lumeo/Styles/lumeo-utilities.src.css \
            -o ./release/wwwroot/_content/Lumeo/css/lumeo-utilities.css \
            --minify
          npx @tailwindcss/cli \
            -i ./docs/Lumeo.Docs/Styles/tailwind.css \
            -o ./release/wwwroot/css/tailwind.out.css \
            --minify

      - name: Drop pre-compressed copies
        # Cloudflare compresses at the edge; shipped `.br` / `.gz` copies would go stale
        # the moment we overwrite their uncompressed source above.
        run: |
          find release/wwwroot -name "*.br" -delete
          find release/wwwroot -name "*.gz" -delete

      - name: Stamp CSS cache-busters with commit SHA
        run: |
          SHORT_SHA=$(echo "${GITHUB_SHA}" | cut -c1-7)
          sed -i "s/__LUMEO_BUILD__/${SHORT_SHA}/g" release/wwwroot/index.html

      - name: Inject Algolia keys into index.html
        # Uses | as sed delimiter — Algolia keys can contain / which would
        # break the conventional s/.../.../g form.
        run: |
          sed -i "s|__ALGOLIA_APP_ID__|${{ secrets.ALGOLIA_APP_ID }}|g" release/wwwroot/index.html
          sed -i "s|__ALGOLIA_SEARCH_KEY__|${{ secrets.ALGOLIA_SEARCH_KEY }}|g" release/wwwroot/index.html

      - name: Copy index.html to 404.html
        run: cp release/wwwroot/index.html release/wwwroot/404.html

      - name: Generate sitemap.xml from @page directives
        # Blazor WASM routes only exist in .razor files as `@page "/route"`; crawlers
        # can't discover them from the SPA shell at /. This step greps every @page from
        # docs/Lumeo.Docs/Pages/**/*.razor, assigns a priority + changefreq bucket based
        # on the route prefix, and emits a sitemap.xml that tells Google about all ~190
        # pages.
        run: |
          SITE="https://lumeo.nativ.sh"
          TODAY=$(date -u +%Y-%m-%d)
          SITEMAP=release/wwwroot/sitemap.xml

          {
            echo '<?xml version="1.0" encoding="UTF-8"?>'
            echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">'
            # The index page doesn't have an @page directive (it's the default route)
            # so add it explicitly with top priority.
            echo "  <url><loc>${SITE}/</loc><lastmod>${TODAY}</lastmod><changefreq>weekly</changefreq><priority>1.0</priority></url>"

            # Strip leading BOM via sed first, then grep the @page directives —
            # one-step PCRE with BOM-tolerance proved fragile (grep -P/-E interaction).
            find docs/Lumeo.Docs/Pages/ -name "*.razor" -exec sed -E 's/^\xEF\xBB\xBF//' {} + \
              | grep -hE '^@page\s+"[^"]+"' \
              | sed -E 's|^@page\s+"([^"]+)"|\1|' \
              | grep -v '^/$' \
              | sort -u \
              | while read -r route; do
                  case "$route" in
                    /components/*)  PRIORITY=0.8; FREQ=monthly ;;
                    /blocks/*)      PRIORITY=0.7; FREQ=monthly ;;
                    /patterns/*)    PRIORITY=0.6; FREQ=monthly ;;
                    /docs/*)        PRIORITY=0.7; FREQ=weekly  ;;
                    *)              PRIORITY=0.5; FREQ=monthly ;;
                  esac
                  echo "  <url><loc>${SITE}${route}</loc><lastmod>${TODAY}</lastmod><changefreq>${FREQ}</changefreq><priority>${PRIORITY}</priority></url>"
                done
            echo '</urlset>'
          } > "${SITEMAP}"

          echo "Generated $(grep -c '<url>' ${SITEMAP}) URLs in sitemap.xml"

      - name: Copy registry + raw source for CLI consumers
        run: |
          # Per-component registry snippets — rc.40 moved the canonical source
          # to docs/Lumeo.Docs/wwwroot/registry/ (out of Lumeo.nupkg). This cp
          # is a no-op when docs/Lumeo.Docs's own publish output already
          # includes the registry/ folder via the SDK static-web-asset
          # pipeline, but kept as a safety net in case the publish ordering
          # ever drops it.
          mkdir -p release/wwwroot/registry
          cp -r docs/Lumeo.Docs/wwwroot/registry/* release/wwwroot/registry/ || true
          mkdir -p release/wwwroot/raw
          rsync -a --exclude='wwwroot/_content' --exclude='obj' --exclude='bin' \
                src/Lumeo/ release/wwwroot/raw/

      - name: Install Chromium deps for Puppeteer
        # ubuntu-latest ships most of these, but a few (libxkbcommon0, libgbm1) are
        # missing by default. Listing them explicitly keeps CI green across runner
        # image bumps.
        run: |
          sudo apt-get update
          sudo apt-get install -y libatk-bridge2.0-0 libatk1.0-0 libcups2 \
            libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 \
            libxrandr2 libgbm1 libasound2t64

      - name: Prerender routes
        # Visits every URL in sitemap.xml with headless Chromium, waits for the
        # Blazor WASM app to mount, then saves document.documentElement.outerHTML
        # as <route>/index.html. Cloudflare Pages then serves the prerendered
        # HTML for first paint; the WASM bundle still boots and takes over.
        working-directory: scripts/prerender
        run: |
          npm ci --no-audit --no-fund
          node prerender.mjs ../../release

      - name: Generate per-route OG preview cards
        # For every route: render a 1280×640 social preview card via Puppeteer
        # (Lumeo brand + page title + category pill), save to wwwroot/og/<slug>.png,
        # and rewrite og:image / twitter:image in the prerendered HTML to point
        # at it. Reuses the Chromium already installed by the prerender step.
        working-directory: scripts/prerender
        run: node og-cards.mjs ../../release

      - name: Drop compressed variants of prerendered HTML
        # The prerender step writes uncompressed HTML; any stale .br/.gz left
        # over from dotnet publish (on the root index.html shell we just
        # overwrote) would be served by Cloudflare in preference. Delete them.
        run: |
          find release/wwwroot -name 'index.html.br' -delete
          find release/wwwroot -name 'index.html.gz' -delete

      - name: Deploy to Cloudflare Pages
        uses: cloudflare/pages-action@v1
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          projectName: lumeo
          directory: release/wwwroot
          gitHubToken: ${{ secrets.GITHUB_TOKEN }}
          # Deploy to the `master` branch of the CF Pages project (= production).
          # Other branches show up as preview deployments.
          branch: master

      # NOTE: No "Push search index to Algolia" step — we use Algolia DocSearch,
      # which has its own hosted crawler that scrapes lumeo.nativ.sh on schedule.
      # The scripts/algolia/index-docs.mjs script is kept in the repo for future
      # use if we ever switch off DocSearch onto a regular Algolia Search app.

      - name: Verify prerender on live site
        # Post-deploy smoke test. Fetches a sample of live routes and checks
        # that each response body contains content that only appears in
        # prerendered HTML (not in the 3kB stock shell). Catches regressions
        # where the prerender step silently produces empty pages.
        working-directory: scripts/prerender
        run: |
          # Give CF edge a moment to propagate.
          sleep 20
          node verify.mjs https://lumeo.nativ.sh
