# Zindex - Request flow from handler. Maintains a sequence diagram of an
# HTTP endpoint's request flow by parsing the handler source on every PR
# that touches the relevant code paths. Code-driven, so PR-only - no
# weekly cron (vs. the api-dependency-map example, which catches drift
# from imports added without a spec change).
#
# Drop into .github/workflows/zindex-checkout-flow.yml.

name: Zindex - Checkout request flow

on:
  pull_request:
    paths:
      - "apps/api/src/routes/checkout.ts"
      - "apps/api/src/services/checkout/**"
      - "packages/sdk-stripe/**"
  workflow_dispatch:

permissions:
  contents: read
  pull-requests: write

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
  ZINDEX_API_BASE: https://api.zindex.ai

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

      # 1. Parse the handler source. Walk the function's async/await chain
      #    via the TS compiler API. Emit a normalised
      #    { lifelines: [...], messages: [...], fragments: [...], notes: [...] }
      #    JSON. This step is project-specific - implement parse-handler.mjs
      #    against your handler's import structure and the languages you use.
      - name: Parse handler call graph
        run: |
          mkdir -p out
          node scripts/parse-handler.mjs apps/api/src/routes/checkout.ts > out/flow.json
          echo "::notice::Detected $(jq '.lifelines | length' out/flow.json) lifelines, $(jq '.messages | length' out/flow.json) messages, $(jq '.fragments | length' out/flow.json) fragments"

      # 2. Capture current revision (for the diff later).
      - name: Capture current revision
        id: prev_rev
        env:
          ZINDEX_API_KEY: ${{ secrets.ZINDEX_API_KEY }}
          ZINDEX_SCENE_ID: ${{ secrets.ZINDEX_SCENE_ID }}
        run: |
          echo "rev=$(curl -fsSL -H "Authorization: Bearer $ZINDEX_API_KEY" \
            "$ZINDEX_API_BASE/v1/scenes/$ZINDEX_SCENE_ID" | jq -r '.revision')" >> "$GITHUB_OUTPUT"

      # 3. Compute typed-operation batch. Reuse stable lifeline ids derived
      #    from service names (kebab-case) so a renamed service updates the
      #    existing lifeline rather than producing a delete + create pair.
      - name: Compute applyOps batch
        run: node scripts/handler-to-ops.mjs out/flow.json > out/ops.json

      # 4. Apply atomically.
      - name: Apply ops to persisted scene
        id: apply
        env:
          ZINDEX_API_KEY: ${{ secrets.ZINDEX_API_KEY }}
          ZINDEX_SCENE_ID: ${{ secrets.ZINDEX_SCENE_ID }}
        run: |
          RESP=$(curl -fsSL -X POST \
            -H "Authorization: Bearer $ZINDEX_API_KEY" \
            -H "Content-Type: application/json" \
            --data-binary @out/ops.json \
            "$ZINDEX_API_BASE/v1/scenes/$ZINDEX_SCENE_ID/applyOps")
          echo "rev=$(echo "$RESP" | jq -r '.revision')" >> "$GITHUB_OUTPUT"
          echo "applied=$(echo "$RESP" | jq -r '.applied')" >> "$GITHUB_OUTPUT"

      # 5. Render.
      - name: Render scene
        env:
          ZINDEX_API_KEY: ${{ secrets.ZINDEX_API_KEY }}
          ZINDEX_SCENE_ID: ${{ secrets.ZINDEX_SCENE_ID }}
        run: |
          curl -fsSL -X POST \
            -H "Authorization: Bearer $ZINDEX_API_KEY" \
            -H "Content-Type: application/json" \
            -d '{"format":"svg","theme":"clean"}' \
            "$ZINDEX_API_BASE/v1/scenes/$ZINDEX_SCENE_ID/render" \
            | jq -r '.output.content' > out/checkout-flow.svg

      # 6. Diff against previous revision.
      - name: Diff revisions
        id: diff
        if: steps.apply.outputs.rev != steps.prev_rev.outputs.rev
        env:
          ZINDEX_API_KEY: ${{ secrets.ZINDEX_API_KEY }}
          ZINDEX_SCENE_ID: ${{ secrets.ZINDEX_SCENE_ID }}
          PREV: ${{ steps.prev_rev.outputs.rev }}
          NEW: ${{ steps.apply.outputs.rev }}
        run: |
          curl -fsSL \
            -H "Authorization: Bearer $ZINDEX_API_KEY" \
            "$ZINDEX_API_BASE/v1/scenes/$ZINDEX_SCENE_ID/diff?from=$PREV&to=$NEW" \
            > out/diff.json
          echo "added=$(jq -r '.summary.added' out/diff.json)" >> "$GITHUB_OUTPUT"
          echo "removed=$(jq -r '.summary.removed' out/diff.json)" >> "$GITHUB_OUTPUT"
          echo "modified=$(jq -r '.summary.modified' out/diff.json)" >> "$GITHUB_OUTPUT"

      - uses: actions/upload-artifact@v4
        id: upload
        with:
          name: checkout-flow-svg
          path: out/checkout-flow.svg
          retention-days: 30

      # 7. PR comment.
      - uses: peter-evans/find-comment@v3
        if: github.event_name == 'pull_request'
        id: find_comment
        with:
          issue-number: ${{ github.event.pull_request.number }}
          comment-author: "github-actions[bot]"
          body-includes: "<!-- zindex-bot:request-flow-from-handler -->"

      - uses: peter-evans/create-or-update-comment@v4
        if: github.event_name == 'pull_request'
        with:
          comment-id: ${{ steps.find_comment.outputs.comment-id }}
          issue-number: ${{ github.event.pull_request.number }}
          edit-mode: replace
          body: |
            <!-- zindex-bot:request-flow-from-handler -->
            ### Checkout request flow updated · revision ${{ steps.apply.outputs.rev }}

            | | |
            |---|---|
            | Structural changes | +${{ steps.diff.outputs.added || '0' }} / -${{ steps.diff.outputs.removed || '0' }} / ~${{ steps.diff.outputs.modified || '0' }} |
            | Revision | ${{ steps.prev_rev.outputs.rev }} → ${{ steps.apply.outputs.rev }} |

            <details><summary>Download rendered SVG</summary>

            ${{ steps.upload.outputs.artifact-url }}

            </details>

            <sub>Rendered by Zindex · scene `${{ secrets.ZINDEX_SCENE_ID }}`</sub>
