name: Pre-Release PR Update

on:
  push:
    branches:
      - main

concurrency:
  group: prerelease-pr
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  prerelease:
    name: Pre-Release PR
    # Skip release-please merge commits and pre-release merge commits.
    # Both contain no new developer changes and would re-trigger needlessly.
    if: >
      !startsWith(github.event.head_commit.message, 'chore(main): release')
      && !startsWith(github.event.head_commit.message, 'chore(main): pre-release')
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    steps:
      - name: Generate GitHub App Token
        id: app-token
        uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v2.0.0
        with:
          app-id: ${{ vars.RELEASE_APP_ID }}
          private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}

      - name: Checkout
        uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
        with:
          fetch-depth: 0
          token: ${{ steps.app-token.outputs.token }}
          persist-credentials: false

      - name: Compute pre-release version
        id: version
        run: |
          CURRENT=$(jq -r '.["."]//"0.0.0"' .release-please-manifest.json)
          MAJOR=$(echo "$CURRENT" | cut -d. -f1)
          MINOR=$(echo "$CURRENT" | cut -d. -f2)

          # Find the last stable release tag (even minor) for commit range analysis.
          # Pre-release tags (odd minor) are excluded to prevent version regression
          # when a pre-release tag exists between the last stable tag and HEAD.
          LAST_TAG=$(git tag -l 'hve-core-v*' | awk -F'[v.]' '{if ($(NF-1) % 2 == 0) print}' | sort -V | tail -1)

          # Check for breaking changes since last release.
          # Only scan commit subjects to avoid false positives from
          # Dependabot bodies that embed upstream changelogs.
          HAS_BREAKING=0
          if [ -n "$LAST_TAG" ]; then
            HAS_BREAKING=$(git log "$LAST_TAG"..HEAD --format='%s' | \
              grep -cE '^[a-z]+(\(.+\))?!:|^BREAKING[ -]CHANGE:' || true)
          fi

          # Even/odd convention: odd minor = pre-release, even minor = stable.
          # Compute the next odd minor from the current manifest version.
          if [ "$HAS_BREAKING" -gt 0 ]; then
            PRE_MAJOR=$((MAJOR + 1))
            PRE_MINOR=1
          else
            PRE_MAJOR=$MAJOR
            if (( MINOR % 2 == 0 )); then
              PRE_MINOR=$((MINOR + 1))
            else
              PRE_MINOR=$MINOR
            fi
          fi

          # Use commit count since last tag as patch for unique, monotonic versions
          COMMIT_COUNT=$(git rev-list --count "${LAST_TAG}..HEAD" 2>/dev/null || echo "0")
          PRE_VERSION="${PRE_MAJOR}.${PRE_MINOR}.${COMMIT_COUNT}"

          # Honor manual override via Pre-Release-As trailer on the latest commit.
          # Mirrors release-please's Release-As: trailer for the stable pipeline.
          OVERRIDE=$(git log -1 --format='%(trailers:key=Pre-Release-As,valueonly)' HEAD | tr -d '[:space:]')
          if [ -n "$OVERRIDE" ]; then
            echo "Manual override applied: $PRE_VERSION -> $OVERRIDE"
            PRE_VERSION="$OVERRIDE"
          fi

          echo "version=$PRE_VERSION" >> "$GITHUB_OUTPUT"
          echo "last_tag=$LAST_TAG" >> "$GITHUB_OUTPUT"
          echo "Computed pre-release version: $PRE_VERSION (current: $CURRENT, last stable: $LAST_TAG)"

      - name: Generate changelog
        id: changelog
        env:
          PRE_VERSION: ${{ steps.version.outputs.version }}
          LAST_TAG: ${{ steps.version.outputs.last_tag }}
        run: |
          MAX_COUNT_ARG=""
          if [ -z "$LAST_TAG" ]; then
            RANGE="HEAD"
            MAX_COUNT_ARG="--max-count=100"
          else
            RANGE="${LAST_TAG}..HEAD"
          fi

          format_entries() {
            local prefix="$1"
            while IFS= read -r line; do
              echo "- ${line#*: }"
            done < <(git log $MAX_COUNT_ARG "$RANGE" --format='%s' | grep -E "^${prefix}(\\(.+\\))?[!]?:" || true)
          }

          {
            echo 'body<<CHANGELOG_EOF'
            echo "## Pre-Release ${PRE_VERSION}"
            echo ""

            FEATS=$(format_entries "feat")
            if [ -n "$FEATS" ]; then
              echo "### ✨ Features"
              echo ""
              echo "$FEATS"
              echo ""
            fi

            FIXES=$(format_entries "fix")
            if [ -n "$FIXES" ]; then
              echo "### 🐛 Bug Fixes"
              echo ""
              echo "$FIXES"
              echo ""
            fi

            DOCS=$(format_entries "docs")
            if [ -n "$DOCS" ]; then
              echo "### 📚 Documentation"
              echo ""
              echo "$DOCS"
              echo ""
            fi

            REFACTORS=$(format_entries "refactor")
            if [ -n "$REFACTORS" ]; then
              echo "### ♻️ Refactoring"
              echo ""
              echo "$REFACTORS"
              echo ""
            fi

            CHORES=$(format_entries "chore")
            if [ -n "$CHORES" ]; then
              echo "### 🔧 Maintenance"
              echo ""
              echo "$CHORES"
              echo ""
            fi

            echo "---"
            echo "*Managed automatically by pre-release workflow.*"
            echo 'CHANGELOG_EOF'
          } >> "$GITHUB_OUTPUT"

      - name: Setup Node.js
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: "24"
          cache: "npm"

      - name: Install dependencies
        run: npm ci

      - name: Install PowerShell-Yaml
        shell: pwsh
        run: |
          if (-not (Get-Module -ListAvailable -Name PowerShell-Yaml | Where-Object { $_.Version -eq '0.4.7' })) {
            Install-Module -Name PowerShell-Yaml -RequiredVersion 0.4.7 -Force -Scope CurrentUser
          }

      - name: Update prerelease/next branch with version files
        env:
          GH_TOKEN: ${{ steps.app-token.outputs.token }}
          PRE_VERSION: ${{ steps.version.outputs.version }}
          REPO: ${{ github.repository }}
        run: |
          BRANCH="prerelease/next"

          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git"

          # Build the version-bump commit locally before touching the remote.
          # A single force-push of the final state prevents the server from
          # ever seeing prerelease/next identical to main, which would cause
          # GitHub to auto-close the open PR.
          git checkout -B "$BRANCH"

          # Update all version-tracked files and regenerate plugin outputs
          pwsh -File scripts/release/Update-VersionFiles.ps1 -Version "$PRE_VERSION"

          git add -A
          git commit -m "chore: bump pre-release version to ${PRE_VERSION}" || echo "No version changes to commit"
          git push --force origin "$BRANCH"

      - name: Ensure pre-release label exists
        env:
          GH_TOKEN: ${{ steps.app-token.outputs.token }}
          REPO: ${{ github.repository }}
        run: |
          gh label create "autorelease: prerelease" \
            --description "Pre-release pull request" \
            --color "FBCA04" \
            -R "$REPO" 2>/dev/null || true

      - name: Create or update pre-release PR
        env:
          GH_TOKEN: ${{ steps.app-token.outputs.token }}
          PRE_VERSION: ${{ steps.version.outputs.version }}
          PR_BODY: ${{ steps.changelog.outputs.body }}
          REPO: ${{ github.repository }}
        run: |
          BRANCH="prerelease/next"
          TITLE="chore(main): pre-release ${PRE_VERSION}"

          # Check for existing open PR from prerelease/next
          PR_NUMBER=$(gh pr list --head "$BRANCH" --state open \
            --json number --jq '.[0].number // empty' \
            -R "$REPO")

          if [ -n "$PR_NUMBER" ]; then
            gh pr edit "$PR_NUMBER" \
              --title "$TITLE" \
              --body "$PR_BODY" \
              --add-label "autorelease: prerelease" \
              -R "$REPO"
            echo "Updated PR #$PR_NUMBER"
          else
            gh pr create \
              --head "$BRANCH" \
              --base main \
              --title "$TITLE" \
              --body "$PR_BODY" \
              --label "autorelease: prerelease" \
              -R "$REPO"
            echo "Created new pre-release PR"
          fi

