name: Publish Package

# ----------------------------------------------------------------------------------
# 🚀 Enhanced PR-Based Automated Publishing with Detailed Changelogs
#
# This workflow creates detailed git commits and tags with:
# - PR information (title, labels)
# - List of commits included in the release
# - Version bump reasoning
# - Direct npm package links
# ----------------------------------------------------------------------------------

on:
  push:
    branches: [main]
  
  workflow_call:
    inputs:
      node_version:
        description: 'Node.js version to use'
        required: false
        type: string
        default: '22.4.x'
      pnpm_version:
        description: 'pnpm version to use'
        required: false
        type: string
        default: '8'
      package_access:
        description: 'Package access level'
        required: false
        type: string
        default: 'public'
    secrets:
      NPM_PRIVATE_PACKAGE_TOKEN:
        required: true

jobs:
  publish:
    runs-on: ubuntu-latest
    
    # Required permissions for the workflow
    permissions:
      contents: write      # To push commits and tags
      packages: write      # To publish packages (if needed)
      pull-requests: read  # To read PR information
    
    steps:
      - uses: actions/checkout@v4
        with:
          token: ${{ github.token }}
          fetch-depth: 0
          persist-credentials: true  # Ensure credentials persist for git operations

      - uses: pnpm/action-setup@v2
        with:
          version: ${{ inputs.pnpm_version || '8' }}

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node_version || '22.4.x' }}
          registry-url: 'https://registry.npmjs.org'
          cache: 'pnpm'

      - name: Configure Git and npm
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_PRIVATE_PACKAGE_TOKEN }}" > ~/.npmrc

      - name: Install and build
        run: |
          pnpm install
          pnpm build

      # 🛡️ Template package protection
      - name: Check for template package
        id: template_check
        run: |
          echo "🔍 Checking package publishing permissions..."
          
          PACKAGE_NAME=$(node -e "console.log(require('./package.json').name)")
          
          echo "📦 Package: $PACKAGE_NAME"
          
          # Check for explicit disable flag
          DISABLE_PUBLISH=$(node -e "console.log(require('./package.json')['disable-publish'] || 'false')")
          
          if [ "$DISABLE_PUBLISH" = "true" ]; then
            echo "🚫 PUBLISHING DISABLED!"
            echo ""
            echo "This package has 'disable-publish: true' in package.json"
            echo "This is typically used for template packages or packages not ready for publication."
            echo ""
            echo "To enable publishing, remove or set to false:"
            echo '  "disable-publish": false'
            echo ""
            echo "❌ Stopping workflow to prevent publication"
            echo "should_publish=false" >> $GITHUB_OUTPUT
          else
            echo "✅ Publishing enabled - proceeding with release"
            echo "should_publish=true" >> $GITHUB_OUTPUT
          fi

      # 🔍 Get detailed PR and commit information
      - name: Get PR and commit information
        if: steps.template_check.outputs.should_publish == 'true'
        id: pr_info
        run: |
          # Get the merge commit message
          COMMIT_MSG=$(git log -1 --pretty=format:"%s %b")
          echo "Latest commit: $COMMIT_MSG"
          
          # Extract PR number from merge commit
          PR_NUMBER=$(echo "$COMMIT_MSG" | grep -o "#[0-9]\+" | head -1 | sed 's/#//')
          
          if [ -n "$PR_NUMBER" ]; then
            echo "Found PR number: $PR_NUMBER"
            
            # Get PR details using GitHub API
            PR_DATA=$(curl -s -H "Authorization: token ${{ github.token }}" \
              "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER")
            
            # Safely extract PR data with proper escaping
            PR_TITLE=$(echo "$PR_DATA" | jq -r '.title // empty' | sed 's/`//g' | sed "s/'//g")
            PR_LABELS=$(echo "$PR_DATA" | jq -r '.labels[].name' | tr '\n' ',' | sed 's/,$//')
            PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.user.login // empty')
            
            echo "PR #$PR_NUMBER: $PR_TITLE"
            echo "Labels: $PR_LABELS"
            echo "Author: $PR_AUTHOR"
          else
            # Safely handle direct commits
            PR_TITLE=$(echo "$COMMIT_MSG" | sed 's/`//g' | sed "s/'//g")
            PR_LABELS=""
            PR_AUTHOR=$(git log -1 --pretty=format:"%an")
            echo "Direct commit (no PR): $PR_TITLE"
          fi
          
          # Get commit list since last tag
          LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
          if [ -n "$LAST_TAG" ]; then
            echo "Getting commits since $LAST_TAG"
            COMMITS=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s (%an)" --no-merges 2>/dev/null || echo "")
          else
            echo "No previous tags, getting recent commits"
            COMMITS=$(git log -5 --pretty=format:"- %s (%an)" --no-merges)
          fi
          
          # Store outputs with proper escaping
          echo "pr_number=${PR_NUMBER:-'N/A'}" >> $GITHUB_OUTPUT
          echo "pr_title<<EOF" >> $GITHUB_OUTPUT
          echo "$PR_TITLE" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT
          echo "pr_labels=$PR_LABELS" >> $GITHUB_OUTPUT
          echo "pr_author=$PR_AUTHOR" >> $GITHUB_OUTPUT
          echo "last_tag=${LAST_TAG:-'(none)'}" >> $GITHUB_OUTPUT
          
          # Store commits in a file for multi-line handling
          echo "$COMMITS" > commits.txt

      # 🏷️ Determine version bump
      - name: Determine version bump
        if: steps.template_check.outputs.should_publish == 'true'
        id: version_bump
        run: |
          PR_TITLE="${{ steps.pr_info.outputs.pr_title }}"
          PR_LABELS="${{ steps.pr_info.outputs.pr_labels }}"
          
          VERSION_BUMP="patch"
          REASON="Default patch version"
          
          # Check PR labels first
          if echo "$PR_LABELS" | grep -i "breaking\|major"; then
            VERSION_BUMP="major"
            REASON="Breaking change (label: breaking/major)"
          elif echo "$PR_LABELS" | grep -i "feature\|feat\|minor"; then
            VERSION_BUMP="minor"
            REASON="New feature (label: feature/feat/minor)"
          elif echo "$PR_LABELS" | grep -i "fix\|patch\|bug"; then
            VERSION_BUMP="patch"
            REASON="Bug fix (label: fix/patch/bug)"
          # Check PR title
          elif echo "$PR_TITLE" | grep -i "^feat\|^feature"; then
            VERSION_BUMP="minor"
            REASON="New feature (title starts with 'feat:')"
          elif echo "$PR_TITLE" | grep -i "^fix\|^bug"; then
            VERSION_BUMP="patch"
            REASON="Bug fix (title starts with 'fix:')"
          elif echo "$PR_TITLE" | grep -i "breaking\|BREAKING CHANGE"; then
            VERSION_BUMP="major"
            REASON="Breaking change (title contains 'breaking')"
          fi
          
          echo "Version bump: $VERSION_BUMP ($REASON)"
          echo "version_bump=$VERSION_BUMP" >> $GITHUB_OUTPUT
          echo "reason=$REASON" >> $GITHUB_OUTPUT

      # 📦 Check and bump version if needed
      - name: Check and bump version
        if: steps.template_check.outputs.should_publish == 'true'
        id: version_check
        run: |
          echo "🔍 Checking package.json..."
          echo "Current directory: $(pwd)"
          echo "Files in directory:"
          ls -la
          
          if [ ! -f package.json ]; then
            echo "❌ package.json not found!"
            exit 1
          fi
          
          echo "📄 Package.json contents (first 20 lines):"
          head -20 package.json
          
          # Check if Node.js can parse the package.json
          if ! node -e "require('./package.json')"; then
            echo "❌ package.json is not valid JSON!"
            exit 1
          fi
          
          CURRENT_VERSION=$(node -e "console.log(require('./package.json').version)")
          PACKAGE_NAME=$(node -e "console.log(require('./package.json').name)")
          
          echo "📦 Package: $PACKAGE_NAME"
          echo "📋 Current version: $CURRENT_VERSION"
          
          # Check if current version exists on npm
          echo "🔍 Checking if $PACKAGE_NAME@$CURRENT_VERSION exists on npm..."
          if npm view "$PACKAGE_NAME@$CURRENT_VERSION" version 2>/dev/null; then
            echo "📈 Version $CURRENT_VERSION exists on npm, bumping to ${{ steps.version_bump.outputs.version_bump }}"
            npm version ${{ steps.version_bump.outputs.version_bump }} --no-git-tag-version
            NEW_VERSION=$(node -e "console.log(require('./package.json').version)")
            echo "📦 New version: $NEW_VERSION"
            echo "needs_commit=true" >> $GITHUB_OUTPUT
          else
            echo "✅ Version $CURRENT_VERSION not published, will use current version"
            NEW_VERSION="$CURRENT_VERSION"
            echo "needs_commit=false" >> $GITHUB_OUTPUT
          fi
          
          echo "final_version=$NEW_VERSION" >> $GITHUB_OUTPUT

      # 🔁 Create detailed commit and tag
      - name: Create detailed commit and tag
        if: steps.template_check.outputs.should_publish == 'true' && steps.version_check.outputs.needs_commit == 'true'
        run: |
          VERSION="${{ steps.version_check.outputs.final_version }}"
          PACKAGE_NAME=$(node -e "console.log(require('./package.json').name)")
          
          # Read commits from file
          COMMITS=$(cat commits.txt)
          
          # Create comprehensive commit message
          cat > release_message.txt << EOF
          chore: release v$VERSION

          ## Release Information
          
          **Package:** $PACKAGE_NAME@$VERSION
          **Version Bump:** ${{ steps.version_bump.outputs.version_bump }}
          **Reason:** ${{ steps.version_bump.outputs.reason }}
          **Previous Tag:** ${{ steps.pr_info.outputs.last_tag }}
          
          ## Source Changes
          
          **PR #${{ steps.pr_info.outputs.pr_number }}:** ${{ steps.pr_info.outputs.pr_title }}
          **Author:** ${{ steps.pr_info.outputs.pr_author }}
          **Labels:** ${{ steps.pr_info.outputs.pr_labels }}
          
          ## Commits Included
          
          $COMMITS
          
          ## Package Info
          
          📦 **npm:** https://www.npmjs.com/package/$PACKAGE_NAME/v/$VERSION
          🏷️ **Tag:** v$VERSION
          📅 **Released:** $(date -u +"%Y-%m-%d %H:%M:%S UTC")
          EOF
          
          echo "📝 Release message:"
          cat release_message.txt
          
          # Commit with detailed message
          git add package.json
          git commit -F release_message.txt
          
          # Create annotated tag with same info
          git tag -a "v$VERSION" -F release_message.txt
          
          # Push commit and tag
          git push origin main
          git push origin "v$VERSION"

      # 🚀 Publish to npm
      - name: Publish to npm
        if: steps.template_check.outputs.should_publish == 'true'
        run: |
          VERSION="${{ steps.version_check.outputs.final_version }}"
          PACKAGE_NAME=$(node -e "console.log(require('./package.json').name)")
          echo "🚀 Publishing $PACKAGE_NAME@$VERSION to npm..."
          pnpm publish --access ${{ inputs.package_access || 'public' }} --no-git-checks
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_PRIVATE_PACKAGE_TOKEN }}

      # 📊 Summary
      - name: Publish summary
        if: always()
        run: |
          if [ "${{ steps.template_check.outputs.should_publish }}" = "false" ]; then
            echo "🚫 **Publishing Disabled - Publication Blocked**"
            echo ""
            echo "This package has 'disable-publish: true' in package.json"
            echo ""
            echo "📝 **To enable publishing:**"
            echo "Remove the disable-publish flag or set it to false in package.json:"
            echo '  "disable-publish": false'
            echo ""
            echo "This is commonly used for template packages or packages not ready for release."
          else
            VERSION="${{ steps.version_check.outputs.final_version }}"
            PACKAGE_NAME=$(node -e "console.log(require('./package.json').name)")
            echo "✅ **Published:** $PACKAGE_NAME@$VERSION"
            echo "🔗 **npm URL:** https://www.npmjs.com/package/$PACKAGE_NAME"
            echo "📦 **Version bump:** ${{ steps.version_bump.outputs.version_bump }}"
            echo "💡 **Reason:** ${{ steps.version_bump.outputs.reason }}"
            echo "🏷️ **Git tag:** v$VERSION"
          fi