name: Release

# This workflow is the ONLY workflow that runs on main branch pushes.
# It handles:
# 1. Regular commits to main: Only runs release-please to check if a release PR should be created/updated
# 2. release-please PR merge: Creates a release tag and triggers the full release process
# 3. Tag push (auroraview-v*): Publishes to PyPI and creates GitHub Release
#
# Note: release-please creates tags in format "auroraview-v0.3.8" (component-v format)
#
# This design eliminates duplicate CI runs by:
# - Not triggering build-wheels.yml, build-cli.yml, ci.yml, codeql.yml on main push
# - Only running expensive builds when a release is actually created
# - Reusing PR artifacts when available to avoid redundant builds

on:
  push:
    branches:
      - main
    tags:
      - 'auroraview-v*'  # release-please tag format: auroraview-v0.3.8
  workflow_dispatch:
    inputs:
      force-rebuild:
        description: 'Force rebuild wheels instead of reusing PR artifacts'
        required: false
        type: boolean
        default: false

permissions:
  contents: write # Required for creating releases and uploading artifacts
  pull-requests: write # Required for release-please
  issues: write # Required for release-please
  id-token: write # Required for PyPI trusted publishing
  attestations: write # Required for artifact attestation
  actions: read # Required for downloading artifacts from other workflows

jobs:
  # First job: Run release-please to create releases and tags
  # This only runs on main branch pushes (not tag pushes)
  release-please:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    outputs:
      release_created: ${{ steps.release.outputs.release_created }}
      tag_name: ${{ steps.release.outputs.tag_name }}
      releases_created: ${{ steps.release.outputs.releases_created }}
    steps:
      - uses: googleapis/release-please-action@v4
        id: release
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          config-file: release-please-config.json
          manifest-file: .release-please-manifest.json

  # Determine if this is a tag push (for publishing)
  check-tag:
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/auroraview-v')
    outputs:
      is_tag: 'true'
      tag_name: ${{ github.ref_name }}
    steps:
      - name: Tag detected
        run: |
          echo "Tag push detected: ${{ github.ref_name }}"
          echo "This will trigger the full release process"

  # Try to find and download pre-built wheels from PR checks
  # IMPORTANT: Only reuse artifacts that were built from the exact same commit SHA
  check-pr-artifacts:
    name: Check PR Artifacts
    runs-on: ubuntu-latest
    needs: [release-please, check-tag]
    if: |
      always() &&
      ((needs.release-please.result == 'success' && needs.release-please.outputs.release_created == 'true') ||
       (needs.check-tag.result == 'success' && needs.check-tag.outputs.is_tag == 'true')) &&
      !inputs.force-rebuild
    outputs:
      artifacts-available: ${{ steps.check.outputs.available }}
      pr-run-id: ${{ steps.check.outputs.run_id }}
      source-sha: ${{ steps.check.outputs.source_sha }}
    steps:
      - uses: actions/checkout@v6

      - name: Find PR workflow run for this commit
        id: check
        uses: actions/github-script@v8
        with:
          script: |
            // Get the current commit SHA that we're releasing
            const targetSha = context.sha;
            console.log(`Looking for artifacts built from commit: ${targetSha}`);

            // Find successful PR Checks workflow runs for this exact commit
            const { data: runs } = await github.rest.actions.listWorkflowRuns({
              owner: context.repo.owner,
              repo: context.repo.repo,
              workflow_id: 'pr-checks.yml',
              status: 'success',
              head_sha: targetSha,  // Only match runs from this exact commit
              per_page: 10
            });

            if (runs.workflow_runs.length === 0) {
              console.log(`No PR workflow found for commit ${targetSha}`);
              core.setOutput('available', 'false');
              core.setOutput('run_id', '');
              core.setOutput('source_sha', '');
              return;
            }

            // Check if any matching run has our expected artifacts
            for (const run of runs.workflow_runs) {
              console.log(`Checking run ${run.id} (commit: ${run.head_sha})`);

              const { data: artifacts } = await github.rest.actions.listWorkflowRunArtifacts({
                owner: context.repo.owner,
                repo: context.repo.repo,
                run_id: run.id
              });

              const hasLinux = artifacts.artifacts.some(a => a.name === 'wheels-linux-x86_64');
              const hasWindows = artifacts.artifacts.some(a => a.name === 'wheels-windows-x64');
              const hasMacos = artifacts.artifacts.some(a => a.name === 'wheels-macos-universal2-apple-darwin');

              if (hasLinux && hasWindows && hasMacos) {
                console.log(`Found matching artifacts from run ${run.id} (commit: ${targetSha})`);
                core.setOutput('available', 'true');
                core.setOutput('run_id', run.id.toString());
                core.setOutput('source_sha', targetSha);
                return;
              }
            }

            console.log(`No suitable artifacts found for commit ${targetSha}, will rebuild`);
            core.setOutput('available', 'false');
            core.setOutput('run_id', '');
            core.setOutput('source_sha', '');

  # Download and re-upload artifacts from PR workflow (if available)
  reuse-pr-wheels:
    name: Reuse PR Wheels
    runs-on: ubuntu-latest
    needs: [release-please, check-tag, check-pr-artifacts]
    if: |
      always() &&
      needs.check-pr-artifacts.result == 'success' &&
      needs.check-pr-artifacts.outputs.artifacts-available == 'true'
    strategy:
      matrix:
        artifact:
          - wheels-linux-x86_64
          - wheels-windows-x64
          - wheels-macos-universal2-apple-darwin
          - wheels-linux-x86_64-py37
          - wheels-windows-x64-py37
    steps:
      - name: Download artifact from PR workflow
        uses: actions/download-artifact@v7
        with:
          name: ${{ matrix.artifact }}
          path: dist/
          github-token: ${{ secrets.GITHUB_TOKEN }}
          run-id: ${{ needs.check-pr-artifacts.outputs.pr-run-id }}
        continue-on-error: true

      - name: Re-upload artifact for release
        uses: actions/upload-artifact@v6
        with:
          name: ${{ matrix.artifact }}
          path: dist/
          if-no-files-found: ignore
          retention-days: 7

  # Fallback: Build wheels if PR artifacts not available
  build-wheels:
    needs: [release-please, check-tag, check-pr-artifacts]
    if: |
      always() &&
      ((needs.release-please.result == 'success' && needs.release-please.outputs.release_created == 'true') ||
       (needs.check-tag.result == 'success' && needs.check-tag.outputs.is_tag == 'true')) &&
      (needs.check-pr-artifacts.result != 'success' || needs.check-pr-artifacts.outputs.artifacts-available != 'true' || inputs.force-rebuild)
    uses: ./.github/workflows/build-wheels.yml
    with:
      test-wheel: 'false'
    permissions:
      contents: read
      actions: write
      id-token: write

  # Build CLI binaries for all platforms
  build-cli:
    needs: [release-please, check-tag]
    if: |
      always() &&
      ((needs.release-please.result == 'success' && needs.release-please.outputs.release_created == 'true') ||
       (needs.check-tag.result == 'success' && needs.check-tag.outputs.is_tag == 'true'))
    uses: ./.github/workflows/build-cli.yml
    permissions:
      contents: read
      actions: write

  # Build Gallery standalone executables for all platforms
  build-gallery:
    needs: [release-please, check-tag]
    if: |
      always() &&
      ((needs.release-please.result == 'success' && needs.release-please.outputs.release_created == 'true') ||
       (needs.check-tag.result == 'success' && needs.check-tag.outputs.is_tag == 'true'))
    uses: ./.github/workflows/build-gallery.yml
    permissions:
      contents: read
      actions: write

  # Update Gallery screenshots in assets/images/
  update-gallery-screenshots:
    name: Update Gallery Screenshots
    runs-on: ubuntu-latest
    needs: [release-please, check-tag]
    if: |
      always() &&
      ((needs.release-please.result == 'success' && needs.release-please.outputs.release_created == 'true') ||
       (needs.check-tag.result == 'success' && needs.check-tag.outputs.is_tag == 'true'))
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v6
        with:
          ref: main
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
          cache-dependency-path: gallery/package-lock.json

      - name: Setup Python
        uses: actions/setup-python@v6
        with:
          python-version: '3.11'

      - name: Build SDK first (Gallery depends on it)
        run: |
          cd packages/auroraview-sdk
          rm -rf node_modules package-lock.json
          npm install
          npm run build

      - name: Build Gallery frontend
        run: |
          cd gallery
          npm ci
          npm run build

      - name: Install Playwright
        run: |
          pip install playwright
          playwright install chromium --with-deps

      - name: Take Gallery screenshots
        run: |
          python scripts/test_gallery_e2e.py --screenshots-only --update-assets

      - name: Commit updated screenshots
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          
          # Check if there are changes
          if git diff --quiet assets/images/; then
            echo "No screenshot changes detected"
          else
            git add assets/images/gallery_*.png
            git commit -m "docs: update gallery screenshots [skip ci]"
            git push
            echo "Screenshots updated and pushed"
          fi

  # Build SDK for npm publishing (uses reusable workflow)
  build-sdk:
    name: SDK CI
    needs: [release-please, check-tag]
    if: |
      always() &&
      ((needs.release-please.result == 'success' && needs.release-please.outputs.release_created == 'true') ||
       (needs.check-tag.result == 'success' && needs.check-tag.outputs.is_tag == 'true'))
    uses: ./.github/workflows/sdk-ci.yml
    with:
      artifact-prefix: 'release-'
    secrets: inherit

  # Publish SDK to npm
  npm-publish:
    name: Publish to npm
    runs-on: ubuntu-latest
    needs: [release-please, check-tag, build-sdk]
    if: |
      always() &&
      ((needs.release-please.result == 'success' && needs.release-please.outputs.release_created == 'true') ||
       (needs.check-tag.result == 'success' && needs.check-tag.outputs.is_tag == 'true')) &&
      needs.build-sdk.result == 'success'
    environment:
      name: npm
      url: https://www.npmjs.com/package/@auroraview/sdk
    steps:
      - uses: actions/checkout@v6

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

      - name: Download SDK artifact
        uses: actions/download-artifact@v7
        with:
          name: release-sdk-package
          path: packages/auroraview-sdk/dist/

      - name: Sync version from release tag
        run: |
          TAG_NAME="${{ needs.release-please.outputs.tag_name || needs.check-tag.outputs.tag_name }}"
          # Extract version from tag (auroraview-v0.3.8 -> 0.3.8)
          VERSION=$(echo "$TAG_NAME" | sed 's/auroraview-v//')
          echo "Setting SDK version to: $VERSION"
          cd packages/auroraview-sdk
          npm version "$VERSION" --no-git-tag-version --allow-same-version

      - name: Publish to npm
        run: |
          cd packages/auroraview-sdk
          # Skip prepublishOnly since we already have pre-built dist from build-sdk job
          # This avoids the "tsup: not found" error
          npm publish --access public --ignore-scripts
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

  # Publish to PyPI - waits for either reused or built wheels
  pypi-publish:
    name: Publish to PyPI
    runs-on: ubuntu-latest
    needs: [release-please, check-tag, check-pr-artifacts, reuse-pr-wheels, build-wheels]
    if: |
      always() &&
      ((needs.release-please.result == 'success' && needs.release-please.outputs.release_created == 'true') ||
       (needs.check-tag.result == 'success' && needs.check-tag.outputs.is_tag == 'true')) &&
      (needs.reuse-pr-wheels.result == 'success' || needs.build-wheels.result == 'success')
    environment:
      name: pypi
      url: https://pypi.org/p/auroraview
    permissions:
      id-token: write
      contents: write
      attestations: write

    steps:
      - name: Download wheel artifacts
        uses: actions/download-artifact@v7
        with:
          # Download wheel artifacts only
          # Artifact names: wheels-linux-x86_64, wheels-windows-x64, wheels-macos-universal2-apple-darwin, etc.
          # Note: build-sdk-assets (renamed from wheels-sdk-assets) won't match this pattern
          pattern: wheels-*
          path: dist
          merge-multiple: true

      - name: List downloaded artifacts
        run: |
          ls -la dist/
          echo "Total files: $(ls -1 dist/ | wc -l)"

      - name: Remove non-wheel files and Linux wheels (not accepted by PyPI)
        run: |
          echo "Before cleanup:"
          ls -la dist/
          
          # Remove any directories that might have been accidentally included
          find dist -mindepth 1 -type d -exec rm -rf {} + 2>/dev/null || true
          
          # Remove any .js files (shouldn't be here, but just in case)
          rm -f dist/*.js 2>/dev/null || true
          
          # Remove CLI tar.gz/zip files (they don't have version numbers in the name)
          # CLI files: auroraview-cli-linux-x64.tar.gz, auroraview-cli-macos-arm64.tar.gz, etc.
          # sdist file: auroraview-0.3.1.tar.gz (has version number)
          rm -f dist/auroraview-cli-*.tar.gz || true
          rm -f dist/auroraview-cli-*.zip || true
          
          # Remove any other non-wheel, non-sdist files
          # Keep only .whl files and versioned .tar.gz (sdist)
          find dist -maxdepth 1 -type f ! -name "*.whl" ! -name "auroraview-[0-9]*.tar.gz" -delete || true
          
          # Remove linux_x86_64 wheels as PyPI doesn't accept them
          # These wheels require webkit2gtk at runtime and use non-standard platform tags
          # Linux users should install from GitHub Releases or build from source
          rm -f dist/*-linux_x86_64.whl || true
          
          echo "After cleanup (files for PyPI):"
          ls -la dist/

      - name: Generate artifact attestation
        uses: actions/attest-build-provenance@v3
        with:
          subject-path: 'dist/*'

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          packages-dir: dist
          verbose: true
          print-hash: true
          skip-existing: true  # Skip files that already exist on PyPI

  # Create GitHub Release
  github-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    needs: [release-please, check-tag, check-pr-artifacts, pypi-publish, build-cli, build-gallery, reuse-pr-wheels, build-wheels]
    if: |
      always() &&
      ((needs.release-please.result == 'success' && needs.release-please.outputs.release_created == 'true') ||
       (needs.check-tag.result == 'success' && needs.check-tag.outputs.is_tag == 'true')) &&
      (needs.reuse-pr-wheels.result == 'success' || needs.build-wheels.result == 'success' || needs.reuse-pr-wheels.result == 'skipped')
    permissions:
      contents: write
      actions: read
      attestations: write

    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Determine tag name
        id: tag
        run: |
          if [ "${{ needs.release-please.outputs.tag_name }}" != "" ]; then
            echo "TAG_NAME=${{ needs.release-please.outputs.tag_name }}" >> $GITHUB_OUTPUT
          else
            echo "TAG_NAME=${{ needs.check-tag.outputs.tag_name }}" >> $GITHUB_OUTPUT
          fi

      - name: Generate changelog
        id: changelog
        uses: jaywcjlove/changelog-generator@main
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          filter-author: (loonghao|renovate\\[bot\\]|dependabot\\[bot\\]|Renovate Bot)
          filter: ''
          template: |
            ## Bugs
            {{fix}}

            ## Feature
            {{feat}}

            ## Improve
            {{refactor,perf,clean}}

            ## Misc
            {{chore,style,ci,build||🔧 Nothing changed}}

            ## Unknown
            {{__unknown__}}

      - name: Download wheel artifacts
        uses: actions/download-artifact@v7
        with:
          pattern: wheels-*
          path: release-artifacts
          merge-multiple: true

      - name: Download CLI artifacts
        uses: actions/download-artifact@v7
        with:
          name: cli-all-platforms
          path: release-artifacts

      - name: Download Gallery artifacts
        uses: actions/download-artifact@v7
        with:
          name: gallery-all-platforms
          path: release-artifacts
        continue-on-error: true

      - name: List release artifacts
        run: |
          echo "Release artifacts:"
          ls -la release-artifacts/

      - name: Document artifact source
        id: artifact-source
        run: |
          if [ "${{ needs.check-pr-artifacts.outputs.artifacts-available }}" == "true" ]; then
            echo "ARTIFACT_SOURCE=✅ **Reused from PR workflow** (Run: [${{ needs.check-pr-artifacts.outputs.pr-run-id }}](https://github.com/${{ github.repository }}/actions/runs/${{ needs.check-pr-artifacts.outputs.pr-run-id }}), SHA: \`${{ needs.check-pr-artifacts.outputs.source-sha }}\`)" >> $GITHUB_OUTPUT
          else
            echo "ARTIFACT_SOURCE=🔨 **Built fresh in release workflow** (SHA: \`${{ github.sha }}\`)" >> $GITHUB_OUTPUT
          fi

      - name: Create GitHub Release
        uses: ncipollo/release-action@v1
        with:
          artifacts: "release-artifacts/*"
          token: ${{ secrets.GITHUB_TOKEN }}
          tag: ${{ steps.tag.outputs.TAG_NAME }}
          name: ${{ steps.tag.outputs.TAG_NAME }}
          body: |
            Comparing Changes: ${{ steps.changelog.outputs.compareurl }}

            ${{ steps.changelog.outputs.changelog }}

            ---

            ### Build Artifacts Source
            ${{ steps.artifact-source.outputs.ARTIFACT_SOURCE }}
          draft: false
          prerelease: false
          allowUpdates: true
          skipIfReleaseExists: false
          updateOnlyUnreleased: false
          makeLatest: true

