name: API Reference Sync

on:
  pull_request:
    types: [closed]

permissions:
  actions: read
  contents: write
  pull-requests: write
  issues: write
  id-token: write

concurrency:
  group: api-reference-sync-${{ github.event.pull_request.number || github.run_id }}
  cancel-in-progress: true

jobs:
  # ─── Job 0: Build API JSON before and after the merge, then diff them ─
  analyze:
    if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'main'
    runs-on: ubuntu-latest

    outputs:
      has_diff: ${{ steps.classify.outputs.has_diff }}
      has_new: ${{ steps.classify.outputs.has_new }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v5
        with:
          fetch-depth: 0
          ref: ${{ github.event.pull_request.merge_commit_sha }}

      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v5
        with:
          node-version-file: '.nvmrc'
          cache: pnpm

      - name: Collect PR metadata
        id: meta
        env:
          PR_TITLE: ${{ github.event.pull_request.title || 'manual run' }}
        run: |
          mkdir -p /tmp/api-sync

          jq -n \
            --arg number "${{ github.event.pull_request.number || '' }}" \
            --arg url "${{ github.event.pull_request.html_url || '' }}" \
            --arg author "${{ github.event.pull_request.user.login || '' }}" \
            --arg title "$PR_TITLE" \
            '{number: $number, url: $url, author: $author, title: $title}' \
            > /tmp/api-sync/pr-meta.json

          cat /tmp/api-sync/pr-meta.json

      - name: Build API JSON at HEAD
        run: |
          pnpm install --frozen-lockfile
          pnpm -C site api-docs

          mkdir -p /tmp/api-sync/after-components /tmp/api-sync/after-utils
          cp -r site/src/content/generated-component-reference/* /tmp/api-sync/after-components/ 2>/dev/null || true
          cp -r site/src/content/generated-util-reference/* /tmp/api-sync/after-utils/ 2>/dev/null || true

      - name: Build API JSON before the merge
        env:
          MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
        run: |
          # This repository uses squash merges, whose parent is the exact pre-merge main tip.
          BEFORE_MERGE_SHA=$(git rev-parse "$MERGE_SHA^1")
          git checkout "$BEFORE_MERGE_SHA"

          # If install or build fails (for example, the builder itself changed),
          # treat everything as new by leaving before dirs empty
          mkdir -p /tmp/api-sync/before-components /tmp/api-sync/before-utils

          if pnpm install --frozen-lockfile && pnpm -C site api-docs; then
            cp -r site/src/content/generated-component-reference/* /tmp/api-sync/before-components/ 2>/dev/null || true
            cp -r site/src/content/generated-util-reference/* /tmp/api-sync/before-utils/ 2>/dev/null || true
          else
            echo "::warning::Pre-merge build failed — treating all components/utils as new"
          fi

          git checkout "$MERGE_SHA"
          pnpm install --frozen-lockfile

      - name: Classify changes
        id: classify
        run: pnpm exec tsx site/scripts/api-reference-sync.ts /tmp/api-sync

      - name: Upload artifact
        if: steps.classify.outputs.has_diff == 'true'
        uses: actions/upload-artifact@v4
        with:
          name: api-diff
          path: /tmp/api-sync/
          retention-days: 1

  # ─── Job 1: Open issues for new components/utils without reference pages
  new-references:
    needs: analyze
    if: needs.analyze.outputs.has_new == 'true'
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v5

      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: api-diff
          path: /tmp/api-sync

      - name: Open issues for missing reference pages
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -euo pipefail

          PR_NUMBER=$(jq -r .number /tmp/api-sync/pr-meta.json)
          PR_URL=$(jq -r .url /tmp/api-sync/pr-meta.json)
          PR_AUTHOR=$(jq -r .author /tmp/api-sync/pr-meta.json)

          REF_DIR="site/src/content/docs/reference"

          # --- New components ---
          if [ -s /tmp/api-sync/new-components.txt ]; then
            while read -r filename; do
              JSON_PATH="/tmp/api-sync/after-components/$filename"
              NAME=$(jq -r .name "$JSON_PATH")
              SLUG="${filename%.json}"

              # Check if a reference page already uses this component
              if grep -rq "<ComponentReference component=\"$NAME\"" "$REF_DIR/"; then
                echo "Reference exists for component $NAME — skipping"
                continue
              fi

              # Check for existing open issue (dedup by drift-type + component name)
              EXISTING=$(gh issue list \
                --label "docs" \
                --search "docs(reference): add $NAME API reference page" \
                --state open \
                --json number \
                --jq 'length')
              if [ "$EXISTING" -gt 0 ]; then
                echo "Open issue already exists for $NAME — skipping"
                continue
              fi

              ASSIGNEE_FLAG=""
              if [ -n "$PR_AUTHOR" ]; then
                ASSIGNEE_FLAG="--assignee $PR_AUTHOR"
              fi

              BODY="$(cat <<EOF
          <!-- drift-type:new-reference -->
          <!-- trigger-pr:$PR_NUMBER -->
          <!-- component:$NAME -->

          ## New Component: \`$NAME\`

          A new component \`$NAME\` was added in #${PR_NUMBER} but has no API reference page yet.

          ### What's needed
          - [ ] Create reference page with anatomy, prose, demos, and \`<ComponentReference component="$NAME" />\`
          - [ ] Add sidebar entry in \`site/src/docs.config.ts\`
          - [ ] Verify build: \`pnpm -C site build\`

          ### Context
          - Triggering PR: $PR_URL
          - Generated JSON: \`site/src/content/generated-component-reference/$filename\`

          > Use the \`/write-api-reference $SLUG\` skill to scaffold this page.
          EOF
          )"

              gh issue create \
                --title "docs(reference): add $NAME API reference page" \
                --body "$BODY" \
                --label "docs,site,components" \
                $ASSIGNEE_FLAG

              echo "Created issue for component $NAME"

            done < /tmp/api-sync/new-components.txt
          fi

          # --- New utils ---
          if [ -s /tmp/api-sync/new-utils.txt ]; then
            while read -r filename; do
              JSON_PATH="/tmp/api-sync/after-utils/$filename"
              NAME=$(jq -r .name "$JSON_PATH")
              SLUG="${filename%.json}"

              # Check if a reference page already uses this util
              if grep -rq "<UtilReference util=\"$NAME\"" "$REF_DIR/"; then
                echo "Reference exists for util $NAME — skipping"
                continue
              fi

              # Check for existing open issue
              EXISTING=$(gh issue list \
                --label "docs" \
                --search "docs(reference): add $NAME API reference page" \
                --state open \
                --json number \
                --jq 'length')
              if [ "$EXISTING" -gt 0 ]; then
                echo "Open issue already exists for $NAME — skipping"
                continue
              fi

              ASSIGNEE_FLAG=""
              if [ -n "$PR_AUTHOR" ]; then
                ASSIGNEE_FLAG="--assignee $PR_AUTHOR"
              fi

              BODY="$(cat <<EOF
          <!-- drift-type:new-reference -->
          <!-- trigger-pr:$PR_NUMBER -->
          <!-- util:$NAME -->

          ## New Utility: \`$NAME\`

          A new utility \`$NAME\` was added in #${PR_NUMBER} but has no API reference page yet.

          ### What's needed
          - [ ] Create reference page with usage examples and \`<UtilReference util="$NAME" />\`
          - [ ] Add sidebar entry in \`site/src/docs.config.ts\`
          - [ ] Verify build: \`pnpm -C site build\`

          ### Context
          - Triggering PR: $PR_URL
          - Generated JSON: \`site/src/content/generated-util-reference/$filename\`

          > Use the \`/write-api-reference $SLUG\` skill to scaffold this page.
          EOF
          )"

              gh issue create \
                --title "docs(reference): add $NAME API reference page" \
                --body "$BODY" \
                --label "docs,site" \
                $ASSIGNEE_FLAG

              echo "Created issue for util $NAME"

            done < /tmp/api-sync/new-utils.txt
          fi

  # ─── Job 2: Detect stale docs (Claude Opus) ─────────────────────────
  stale-docs:
    needs: analyze
    if: needs.analyze.outputs.has_diff == 'true'
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v5

      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: api-diff
          path: /tmp/api-sync

      - name: Detect stale docs
        uses: anthropics/claude-code-action@v1
        with:
          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
          github_token: ${{ secrets.GITHUB_TOKEN }}
          claude_args: |
            --model opus
            --max-turns 25
            --allowedTools "Read" "Glob" "Grep" "Bash(gh:*)" "Bash(cat:*)" "Bash(jq:*)" "Bash(diff:*)" "Bash(ls:*)" "Bash(head:*)" "Bash(tail:*)" "Bash(wc:*)" "Bash(sort:*)"
          prompt: |
            You are the stale-docs detection agent. Your job: given a pre-computed API diff,
            find documentation that may need updating and open ONE GitHub issue summarizing all findings.

            ## Inputs

            The following files are at /tmp/api-sync/:
            - `diff.patch` — unified diff of API JSON before and after the triggering PR was merged
            - `changed-components.txt` — filenames of changed component JSONs (one per line, may be empty)
            - `changed-utils.txt` — filenames of changed util JSONs (one per line, may be empty)
            - `after-components/` — current component JSON files
            - `after-utils/` — current util JSON files
            - `pr-meta.json` — `{number, url, author, title}` of the triggering PR

            Important: Focus on items in `changed-components.txt` and `changed-utils.txt`.
            Skip any components/utils listed in `new-components.txt` or `new-utils.txt` —
            a separate job already opens issues for those.

            ## Process

            Step 1: Read `pr-meta.json` and `diff.patch`. Understand what API surfaces changed and how.

            Step 2: For each changed component/util, read the "after" JSON to understand the current API shape.

            Step 3: Search for documentation that references these APIs. Check ALL of:
            - `site/src/content/docs/reference/` — MDX reference pages (look for stale prop descriptions,
              missing new props/state/data-attributes, outdated type signatures)
            - `site/src/content/docs/concepts/` — concept pages mentioning these APIs
            - `site/src/content/docs/how-to/` — how-to guides mentioning these APIs
            - `site/src/components/docs/demos/` — demo code using changed props/state/signatures
            - `packages/*/README.md` — package READMEs mentioning these APIs

            Use Grep and Glob to search efficiently. Look for:
            - The component/util name (PascalCase, camelCase)
            - Specific prop/state/parameter names that were added, removed, or changed in the diff
            - HTML element names from `platforms.html.tagName` in the JSON (e.g. `media-play-button`)
            - Data attribute names that changed

            Step 4: For each file with hits, read the relevant section and assess:
            - `high` — definitely stale (references a removed/renamed prop by old name, shows old signature)
            - `medium` — likely stale (mentions API whose behavior/type changed)
            - `low` — possibly stale (mentions the component but may not be affected)

            Discard clear false positives. Include borderline cases for human review.

            Step 5: If you found ANY stale references, open exactly ONE issue using `gh issue create`.
            If you found NOTHING stale, do NOT create an issue — just output "No stale docs detected."

            ## Issue format

            Read `pr-meta.json` to get PR_NUMBER, PR_URL, PR_AUTHOR, and PR_TITLE.

            Assign the issue to the PR author: `--assignee $PR_AUTHOR` (skip if author is empty).

            Title: `docs(site): stale docs from #$PR_NUMBER`

            Labels: `docs,site`

            Body (use this structure exactly):
            ```
            <!-- drift-type:stale-docs -->
            <!-- trigger-pr:{PR_NUMBER} -->

            ## Summary
            API changes in #{PR_NUMBER} ({PR_TITLE}) may have made the following documentation stale.

            ## Triggering PR
            {PR_URL}

            ## API Changes
            {Brief summary of what changed in the API — new props, removed props, type changes, etc.}

            ## Stale Documentation Found

            ### High Confidence
            | File | Line(s) | Issue | API Change |
            |---|---|---|---|
            {rows or "None found"}

            ### Medium Confidence
            | File | Line(s) | Issue | API Change |
            |---|---|---|---|
            {rows or "None found"}

            ### Low Confidence
            | File | Line(s) | Issue | API Change |
            |---|---|---|---|
            {rows or "None found"}

            ## Recommended Actions
            {Bulleted list of specific things to fix}
            ```

            ## Rules
            - Open at most ONE issue per run. Group everything into one issue.
            - Do NOT create or modify any files. Do NOT create PRs. Issues only.
            - Do NOT open an issue if there is nothing stale.
            - Before creating an issue, check for existing open issues that have BOTH
              `<!-- drift-type:stale-docs -->` and `<!-- trigger-pr:{PR_NUMBER} -->` in their body.
              If one exists, skip creation to avoid duplicates.
            - Be thorough but efficient — use Grep to find files, then Read only relevant sections.
