name: Auto Tag & Publish on Release PR Merge

on:
  pull_request:
    types: [closed]
    branches: [main]

jobs:
  tag:
    if: >
      github.event.pull_request.merged == true &&
      startsWith(github.event.pull_request.title, 'Release v') &&
      startsWith(github.event.pull_request.head.ref, 'release/')
    runs-on: ubuntu-latest
    permissions:
      contents: write
    outputs:
      tag: ${{ steps.version.outputs.tag }}
    steps:
      - uses: actions/checkout@v4

      - name: Extract version from PR title
        id: version
        run: |
          VERSION=$(echo "${{ github.event.pull_request.title }}" | sed 's/^Release //')
          echo "tag=$VERSION" >> "$GITHUB_OUTPUT"

      - name: Create and push tag
        run: |
          git tag "${{ steps.version.outputs.tag }}"
          git push origin "${{ steps.version.outputs.tag }}"

  publish:
    needs: tag
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.tag.outputs.tag }}

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          registry-url: 'https://registry.npmjs.org'

      - run: npm ci
      - run: npm run build
      - run: npm test

      - name: Determine npm tag
        id: npm-tag
        run: |
          VERSION="${{ needs.tag.outputs.tag }}"
          VERSION="${VERSION#v}"
          echo "version=$VERSION" >> "$GITHUB_OUTPUT"
          if echo "$VERSION" | grep -qE '(alpha|beta|rc|next)'; then
            echo "tag=next" >> "$GITHUB_OUTPUT"
          else
            echo "tag=latest" >> "$GITHUB_OUTPUT"
          fi

      - name: Publish package
        run: npm publish --tag ${{ steps.npm-tag.outputs.tag }}
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Sync next tag on stable release
        if: steps.npm-tag.outputs.tag == 'latest'
        run: |
          PACKAGE_NAME=$(node -p "require('./package.json').name")
          VERSION="${{ steps.npm-tag.outputs.version }}"

          for attempt in 1 2 3; do
            if npm dist-tag add "${PACKAGE_NAME}@${VERSION}" next; then
              exit 0
            fi
            if [ "$attempt" -eq 3 ]; then
              echo "Failed to sync next tag after 3 attempts."
              exit 1
            fi
            sleep $((attempt * 5))
          done
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Verify dist-tags
        run: |
          PACKAGE_NAME=$(node -p "require('./package.json').name")

          for attempt in 1 2 3 4 5; do
            LATEST=$(npm view "${PACKAGE_NAME}" dist-tags.latest)
            NEXT=$(npm view "${PACKAGE_NAME}" dist-tags.next || true)

            echo "Attempt ${attempt}: latest=${LATEST}, next=${NEXT}"

            if [ "${{ steps.npm-tag.outputs.tag }}" != "latest" ] || [ "${LATEST}" = "${NEXT}" ]; then
              echo "Dist-tags verified."
              exit 0
            fi

            if [ "$attempt" -eq 5 ]; then
              echo "::warning::dist-tags not synced after 5 attempts (latest=${LATEST}, next=${NEXT}). Registry propagation may be delayed."
              exit 0
            fi
            sleep $((attempt * 10))
          done
