name: Merge main into next

on:
  repository_dispatch:
    types: [release-published]
  workflow_dispatch:

permissions: {}

env:
  IMAGE: ghcr.io/${{ github.repository }}/flue-sandbox

jobs:
  merge:
    name: Merge main into next
    if: github.repository_owner == 'withastro'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      contents: write # Push merge branch
      pull-requests: write # Create/update merge PR
      packages: read # Pull sandbox image from GHCR
    steps:
      - name: Lowercase image name
        run: echo "IMAGE=${IMAGE,,}" >> "$GITHUB_ENV"

      - name: Check if next branch exists
        id: check-next
        env:
          GH_TOKEN: ${{ secrets.FREDKBOT_GITHUB_TOKEN }}
        run: |
          if gh api repos/${{ github.repository }}/branches/next --silent 2>/dev/null; then
            echo "exists=true" >> "$GITHUB_OUTPUT"
          else
            echo "exists=false" >> "$GITHUB_OUTPUT"
            echo "No 'next' branch found — nothing to do."
          fi

      # runtime: clean checkout of workflow source, used for AI execution environment

      - name: Checkout runtime
        if: steps.check-next.outputs.exists == 'true'
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          path: runtime
          persist-credentials: false

      - name: Setup PNPM
        if: steps.check-next.outputs.exists == 'true'
        uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8
        with:
          package_json_file: runtime/package.json

      - name: Setup Node
        if: steps.check-next.outputs.exists == 'true'
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: 24.15.0

      - name: Install dependencies in runtime
        if: steps.check-next.outputs.exists == 'true'
        working-directory: runtime
        run: pnpm install --frozen-lockfile

      # operand: dirty merge tree, what the AI operates on

      - name: Checkout operand
        if: steps.check-next.outputs.exists == 'true'
        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          ref: next
          path: operand
          fetch-depth: 0
          token: ${{ secrets.FREDKBOT_GITHUB_TOKEN }}

      - name: Configure Git identity
        if: steps.check-next.outputs.exists == 'true'
        working-directory: operand
        run: |
          git config user.name "astrobot-houston"
          git config user.email "fred+astrobot@astro.build"

      - name: Attempt merge
        if: steps.check-next.outputs.exists == 'true'
        id: merge
        working-directory: operand
        run: |
          BRANCH="ci/merge-main-to-next"

          # Start from the next branch
          git checkout origin/next
          git checkout -B "$BRANCH"

          # Attempt the merge — do NOT commit yet
          if git merge origin/main --no-commit; then
            echo "conflict=false" >> "$GITHUB_OUTPUT"
            # Clean merge: commit and push directly, no AI needed
            git commit --no-edit -m "chore: merge main into next"
            git push -f origin "$BRANCH"
          else
            echo "conflict=true" >> "$GITHUB_OUTPUT"
            # Conflicts detected — leave working tree dirty for AI to resolve
          fi

          echo "branch=$BRANCH" >> "$GITHUB_OUTPUT"

      # AI conflict resolution (only when merge has conflicts)

      - name: Log in to GHCR
        if: steps.check-next.outputs.exists == 'true' && steps.merge.outputs.conflict == 'true'
        uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3.7.0
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Pull sandbox image
        if: steps.check-next.outputs.exists == 'true' && steps.merge.outputs.conflict == 'true'
        run: docker pull $IMAGE:latest

      - name: Start Cloudflare Tunnel
        if: steps.check-next.outputs.exists == 'true' && steps.merge.outputs.conflict == 'true'
        run: |
          curl -fsSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o /usr/local/bin/cloudflared
          chmod +x /usr/local/bin/cloudflared
          cloudflared tunnel --url http://localhost:48765 --no-autoupdate 2>&1 | tee /tmp/cloudflared.log &
          for i in $(seq 1 30); do
            if grep -qo 'https://[^ ]*\.trycloudflare\.com' /tmp/cloudflared.log; then
              break
            fi
            sleep 1
          done
          echo "=========================================="
          echo "TUNNEL URL:"
          grep -o 'https://[^ ]*\.trycloudflare\.com' /tmp/cloudflared.log || echo "WARNING: tunnel URL not found"
          echo "=========================================="
          echo ""
          echo "To attach from your machine:"
          echo "  OPENCODE_API_URL=\$(grep -o 'https://[^ ]*\\.trycloudflare\\.com' /tmp/cloudflared.log) opencode attach"

      - name: Resolve conflicts with AI
        if: steps.check-next.outputs.exists == 'true' && steps.merge.outputs.conflict == 'true'
        working-directory: operand
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          FREDKBOT_GITHUB_TOKEN: ${{ secrets.FREDKBOT_GITHUB_TOKEN }}
          ANTHROPIC_API_KEY: ${{ secrets.CI_ANTHROPIC_API_KEY }}
          BRANCH: ${{ steps.merge.outputs.branch }}
        run: |
          ../runtime/node_modules/.bin/flue run merge-resolve \
            --target node \
            --payload "{\"branch\": \"$BRANCH\", \"hasConflicts\": true}" \
            --root ../runtime \
            --output .

      # Open or update PR (runs for both clean and conflict merges)

      - name: Open or update PR
        if: steps.check-next.outputs.exists == 'true'
        working-directory: operand
        env:
          GH_TOKEN: ${{ secrets.FREDKBOT_GITHUB_TOKEN }}
          CONFLICT: ${{ steps.merge.outputs.conflict }}
          BRANCH: ${{ steps.merge.outputs.branch }}
        run: |
          if [ "$CONFLICT" = "true" ]; then
            BODY="## Merge main into next

          This PR merges \`main\` into \`next\` after a release.

          Merge conflicts were resolved automatically. Review CI results and merge when ready."
          else
            BODY="## Merge main into next

          This PR merges \`main\` into \`next\` after a release.

          The merge was clean — review CI results and merge when ready."
          fi

          # Check if a PR already exists for this branch
          EXISTING_PR=$(gh pr list --head "$BRANCH" --base next --json number --jq '.[0].number' 2>/dev/null || echo "")

          if [ -n "$EXISTING_PR" ]; then
            gh pr edit "$EXISTING_PR" --body "$BODY"
            echo "Updated existing PR #$EXISTING_PR"
          else
            gh pr create \
              --head "$BRANCH" \
              --base next \
              --title "chore: merge main into next" \
              --body "$BODY"
          fi
