name: Pipeline

on:
  workflow_run:
    workflows: ["CI"]
    types: [completed]
    branches: [main]

concurrency:
  group: pipeline-main
  cancel-in-progress: false

permissions:
  contents: write
  packages: write

jobs:
  dev-publish:
    name: Dev Publish
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    runs-on: blacksmith-4vcpu-ubuntu-2404
    container:
      image: ghcr.io/singularity-forge/sf-ci-builder:latest
      credentials:
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
      env:
        BLACKSMITH_CACHE_TOKEN: ${{ env.BLACKSMITH_CACHE_TOKEN }}
        BLACKSMITH_CACHE_URL: ${{ env.BLACKSMITH_CACHE_URL }}
        GITHUB_REPO_NAME: ${{ github.repository }}
    outputs:
      dev-version: ${{ steps.stamp.outputs.version }}
    steps:
      - uses: actions/checkout@v6

      - name: Mark workspace safe for git
        run: git config --global --add safe.directory "$GITHUB_WORKSPACE"

      - uses: actions/setup-node@v6
        with:
          node-version: '24.15'
          registry-url: https://registry.npmjs.org
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Install web host dependencies
        run: npm --prefix web ci

      - name: Cache Next.js build
        uses: useblacksmith/cache@v5
        with:
          path: web/.next/cache
          key: nextjs-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }}-${{ hashFiles('web/app/**', 'web/components/**', 'web/lib/**', 'web/hooks/**') }}
          restore-keys: |
            nextjs-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }}-
            nextjs-${{ runner.os }}-

      - name: Build core
        run: npm run build:core

      - name: Build web host
        run: npm run build:web-host

      - name: Stamp dev version and sync platform packages
        id: stamp
        run: |
          npm run pipeline:version-stamp
          npm run sync-platform-versions
          echo "version=$(node -e 'process.stdout.write(require("./package.json").version)')" >> "$GITHUB_OUTPUT"

      - name: Publish @dev
        run: |
          VERSION=$(node -e 'process.stdout.write(require("./package.json").version)')
          if npm view "sf-pi@${VERSION}" version 2>/dev/null; then
            echo "Version ${VERSION} already published — skipping"
          else
            npm publish --tag dev
          fi
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Smoke test (local)
        run: |
          chmod +x dist/loader.js
          export SF_SMOKE_BINARY="$(pwd)/dist/loader.js"
          npm run test:smoke

  test-verify:
    name: Test & Verify
    needs: dev-publish
    runs-on: blacksmith-4vcpu-ubuntu-2404
    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v6
        with:
          node-version: '24.15'
          registry-url: https://registry.npmjs.org
          cache: 'npm'

      - name: Install sf-pi@dev globally (with registry propagation retry)
        run: |
          for i in 1 2 3 4 5 6; do
            npm install -g sf-pi@dev && exit 0
            echo "Attempt $i failed — waiting 10s for npm registry propagation..."
            sleep 10
          done
          echo "Failed to install sf-pi@dev after 6 attempts"
          exit 1

      - name: Run smoke tests (against installed binary)
        run: |
          export SF_SMOKE_BINARY=$(which sf)
          npm run test:smoke
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Install dependencies
        run: npm ci

      - name: Run fixture tests
        run: npm run test:fixtures

      - name: Run live regression tests (against installed binary)
        run: |
          export SF_SMOKE_BINARY=$(which sf)
          npm run test:live-regression

      - name: Promote to @next
        env:
          DEV_VERSION: ${{ needs.dev-publish.outputs.dev-version }}
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
        run: npm dist-tag add "sf-pi@${DEV_VERSION}" next

      - name: Log in to GHCR
        uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push runtime Docker image
        env:
          DEV_VERSION: ${{ needs.dev-publish.outputs.dev-version }}
        run: |
          docker build --target runtime \
            -t "ghcr.io/singularity-forge/sf-run:next" \
            -t "ghcr.io/singularity-forge/sf-run:${DEV_VERSION}" \
            .
          docker push ghcr.io/singularity-forge/sf-run:next
          docker push "ghcr.io/singularity-forge/sf-run:${DEV_VERSION}"

  prod-release:
    name: Production Release
    needs: [dev-publish, test-verify]
    runs-on: blacksmith-4vcpu-ubuntu-2404
    environment: prod
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0
          token: ${{ secrets.RELEASE_PAT }}

      - uses: actions/setup-node@v6
        with:
          node-version: '24.15'
          registry-url: https://registry.npmjs.org
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Cache Next.js build
        uses: useblacksmith/cache@v5
        with:
          path: web/.next/cache
          key: nextjs-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }}-${{ hashFiles('web/app/**', 'web/components/**', 'web/lib/**', 'web/hooks/**') }}
          restore-keys: |
            nextjs-${{ runner.os }}-${{ hashFiles('web/package-lock.json') }}-
            nextjs-${{ runner.os }}-

      - name: Run live LLM tests (optional)
        continue-on-error: true
        run: npm run test:live || echo "::warning::Live LLM tests failed — non-blocking, but worth investigating"
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          SF_LIVE_TESTS: "1"

      - name: Generate changelog and determine version
        id: release
        run: |
          OUTPUT=$(node scripts/generate-changelog.mjs)
          echo "$OUTPUT" | jq .
          echo "version=$(echo "$OUTPUT" | jq -r '.newVersion')" >> "$GITHUB_OUTPUT"
          echo "$OUTPUT" | jq -r '.changelogEntry' > /tmp/changelog-entry.md
          echo "$OUTPUT" | jq -r '.releaseNotes' > /tmp/release-notes.md

      - name: Bump version and sync packages
        env:
          RELEASE_VERSION: ${{ steps.release.outputs.version }}
        run: node scripts/bump-version.mjs "$RELEASE_VERSION"

      - name: Validate package files after version bump
        run: |
          node -e "require('./package.json')" && \
          node -e "require('./packages/pi-coding-agent/package.json')" && \
          node -e "require('./pkg/package.json')" && \
          echo "All package.json files are valid"

      - name: Update CHANGELOG.md
        run: node scripts/update-changelog.mjs /tmp/changelog-entry.md

      - name: Commit and tag release
        env:
          RELEASE_VERSION: ${{ steps.release.outputs.version }}
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add package.json package-lock.json web/package-lock.json CHANGELOG.md native/npm/*/package.json pkg/package.json packages/pi-coding-agent/package.json
          git commit -m "release: v${RELEASE_VERSION}"
          git tag "v${RELEASE_VERSION}"
          git pull --rebase origin main

      - name: Build release
        run: npm run build

      - name: Publish release to npm @latest
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
          RELEASE_VERSION: ${{ steps.release.outputs.version }}
        run: |
          OUTPUT=$(npm publish 2>&1) && echo "$OUTPUT" || {
            if echo "$OUTPUT" | grep -q "cannot publish over the previously published"; then
              echo "Version already published — promoting to latest"
              npm dist-tag add "sf-pi@${RELEASE_VERSION}" latest
            else
              echo "$OUTPUT"
              exit 1
            fi
          }

      - name: Push release commit and tag
        env:
          RELEASE_VERSION: ${{ steps.release.outputs.version }}
        run: |
          git push origin main
          git push origin "v${RELEASE_VERSION}"

      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          RELEASE_VERSION: ${{ steps.release.outputs.version }}
        run: |
          gh release create "v${RELEASE_VERSION}" \
            --title "v${RELEASE_VERSION}" \
            --notes-file /tmp/release-notes.md \
            --latest

      - name: Post to Discord
        if: ${{ env.DISCORD_WEBHOOK != '' }}
        env:
          DISCORD_WEBHOOK: ${{ secrets.DISCORD_CHANGELOG_WEBHOOK }}
          RELEASE_VERSION: ${{ steps.release.outputs.version }}
        run: |
          NOTES=$(cat /tmp/release-notes.md)
          curl -s -X POST "$DISCORD_WEBHOOK" \
            -H "Content-Type: application/json" \
            -d "$(jq -n --arg c "**SF v${RELEASE_VERSION} Released**\n\n${NOTES}\n\n\`npm i sf-pi@${RELEASE_VERSION}\`" '{content:$c}')"

      - name: Log in to GHCR
        uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Tag runtime Docker image as latest
        env:
          DEV_VERSION: ${{ needs.dev-publish.outputs.dev-version }}
        run: |
          docker pull "ghcr.io/singularity-forge/sf-run:${DEV_VERSION}"
          docker tag "ghcr.io/singularity-forge/sf-run:${DEV_VERSION}" ghcr.io/singularity-forge/sf-run:latest
          docker push ghcr.io/singularity-forge/sf-run:latest

  update-builder:
    name: Update CI Builder Image
    if: ${{ github.event.workflow_run.conclusion == 'success' }}
    runs-on: blacksmith-4vcpu-ubuntu-2404
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 2

      - name: Check for Dockerfile changes
        id: check
        env:
          HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
        run: |
          CHANGED=$(git diff --name-only "${HEAD_SHA}~1" "${HEAD_SHA}" -- Dockerfile || echo "")
          echo "changed=$([[ -n "$CHANGED" ]] && echo 'true' || echo 'false')" >> "$GITHUB_OUTPUT"

      - name: Log in to GHCR
        if: steps.check.outputs.changed == 'true'
        uses: docker/login-action@v4
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push CI builder image
        if: steps.check.outputs.changed == 'true'
        run: |
          docker build --target builder \
            -t ghcr.io/singularity-forge/sf-ci-builder:latest \
            .
          docker push ghcr.io/singularity-forge/sf-ci-builder:latest
