name: Deploy Staging

# ─────────────────────────────────────────────────────────────────────────────
# Build + Deploy-Pipeline fuer rebreak-staging.
#
# Warum GitHub-Actions statt Server-Build:
#   - Self-hosted Runner auf raynis-builder (api.trucko.org, 8 GB RAM).
#   - Keine kostenpflichtigen GitHub Actions Minuten mehr fuer Linux-Builds.
#   - Server bleibt unangetastet -- nur Artifact-Extract + pm2 restart.
#
# Pattern (uebernommen aus trucko-monorepo .github/workflows/android.yml):
#   - SSH-Key in GitHub-Secret (STAGING_DEPLOY_KEY)
#   - ssh-keyscan + scp + ssh fuer Server-Side-Trigger
#   - Concurrency-Group verhindert parallele Deploys
#
# Coexistence: scripts/deploy.sh + scripts/deploy-webhook/server.mjs laufen
#   parallel auf dem Server (Failsafe). User entscheidet wann der Webhook
#   abgeschaltet wird (siehe ops/GITHUB_ACTIONS_PIPELINE.md).
# ─────────────────────────────────────────────────────────────────────────────

on:
  push:
    branches: [main]
  workflow_dispatch:

concurrency:
  group: deploy-staging
  cancel-in-progress: false  # parallele deploys queueen, nicht canceln

permissions:
  contents: read

jobs:
  # ── 1. Build auf self-hosted Runner (raynis-builder) ────────────────────────
  build:
    name: Build backend (Nitro)
    runs-on: [self-hosted, raynis-builder]
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # full history fuer migrate-detection im Server-Script

      - uses: pnpm/action-setup@v4
        # version is read from package.json's "packageManager" field (pnpm@10.23.0)

      - uses: actions/setup-node@v4
        with:
          node-version: 24.11.1  # MATCH Hetzner-Node-Version exakt
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Build backend (prisma generate + nitro build)
        working-directory: backend
        # Runner hat 8 GB RAM; Heap auf 4 GB anheben fuer Nitro build.
        env:
          NODE_OPTIONS: "--max-old-space-size=4096"
        run: pnpm build

      - name: Tar artifact
        run: tar czf backend-output.tar.gz -C backend/.output .

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: backend-output
          path: backend-output.tar.gz
          retention-days: 7

      - name: Upload imap-idle artifact
        uses: actions/upload-artifact@v4
        with:
          name: imap-idle
          path: backend/imap-idle/
          retention-days: 7

  # ── 2. Deploy: Artifact zum Hetzner pushen + extract + pm2 restart ──────────
  deploy:
    name: Deploy zu Hetzner
    needs: build
    runs-on: [self-hosted, raynis-builder]
    environment: staging  # GitHub-Environment fuer Secret-Isolation
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v4
        with:
          name: backend-output

      - name: Download imap-idle artifact
        uses: actions/download-artifact@v4
        with:
          name: imap-idle
          path: imap-idle/

      - name: Setup SSH
        env:
          SSH_PRIVATE_KEY: ${{ secrets.STAGING_DEPLOY_KEY }}
          SSH_HOST: ${{ vars.HETZNER_HOST }}
        run: |
          if [ -z "$SSH_PRIVATE_KEY" ] || [ -z "$SSH_HOST" ]; then
            echo "FATAL: STAGING_DEPLOY_KEY (secret) oder HETZNER_HOST (var) nicht gesetzt"
            exit 1
          fi
          echo "Deploying to host: $SSH_HOST"
          mkdir -p ~/.ssh
          printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H "$SSH_HOST" >> ~/.ssh/known_hosts

      - name: Upload artifact zu Hetzner
        env:
          SSH_HOST: ${{ vars.HETZNER_HOST }}
          SSH_USER: ${{ vars.HETZNER_USER }}
        run: |
          scp -i ~/.ssh/id_ed25519 backend-output.tar.gz \
            "$SSH_USER@$SSH_HOST:/srv/rebreak/backend/.output-incoming.tar.gz"

      - name: Upload imap-idle zu Hetzner
        env:
          SSH_HOST: ${{ vars.HETZNER_HOST }}
          SSH_USER: ${{ vars.HETZNER_USER }}
        run: |
          # scp -r kopiert Ordner-Inhalt in Unterverzeichnis wenn Ziel existiert.
          # Loesung: Ziel vorab leeren + neu anlegen, dann Inhalt via glob kopieren.
          ssh -i ~/.ssh/id_ed25519 "$SSH_USER@$SSH_HOST" \
            'rm -rf /srv/rebreak/backend/imap-idle && mkdir -p /srv/rebreak/backend/imap-idle'
          scp -r -i ~/.ssh/id_ed25519 imap-idle/. \
            "$SSH_USER@$SSH_HOST:/srv/rebreak/backend/imap-idle/"

      - name: Server-side deploy (extract + migrate + pm2 restart)
        env:
          SSH_HOST: ${{ vars.HETZNER_HOST }}
          SSH_USER: ${{ vars.HETZNER_USER }}
        run: |
          ssh -i ~/.ssh/id_ed25519 "$SSH_USER@$SSH_HOST" \
            'bash /srv/rebreak/scripts/deploy-from-artifact.sh'

      - name: Health-Check (HTTP 401 = Server erreichbar + auth-protected)
        run: |
          # Infisical-Login + Nitro-Start + Cron-Init braucht auf gestresstem Server bis 30s.
          # Retry-Loop: bis zu 12x mit 5s Pause = max 60s warten.
          for i in $(seq 1 12); do
            STATUS=$(curl -sS -o /dev/null -w '%{http_code}' \
              https://staging.rebreak.org/api/auth/me 2>/dev/null || echo "000")
            echo "Attempt $i: staging.rebreak.org/api/auth/me -> HTTP $STATUS"
            if [ "$STATUS" = "401" ] || [ "$STATUS" = "200" ]; then
              echo "Health-Check PASSED"
              exit 0
            fi
            sleep 5
          done
          echo "FAIL: erwartet 401/200, zuletzt bekommen $STATUS nach 60s"
          exit 1
