name: Rollback to Previous Version
run-name: 🔙 Rollback by @${{ gitea.actor }}

# ───────────────────────────────────────────────────────
# Owner: DevOps (Architekt)
# Trigger: EXCLUSIVELY manual (workflow_dispatch).
#
# This workflow reverts the deploy path to the code at a
# given git tag/ref, then rebuilds and redeploys the stack.
#
# Strategy: git checkout <tag> → docker compose up -d --build
# This is a "full restart rollback" — safest for containerized
# apps where DB schema changes may need the matching API binary.
#
# DB migrations: the API runs MigrateAsync on startup. If the
# rollback-tag's migration history is a prefix of the current DB,
# EF Core handles this gracefully (no-op for already-applied
# migrations). If the tag predates a destructive migration, manual
# DB intervention is needed — that's an edge case surfaced to DevOps.
# ───────────────────────────────────────────────────────
concurrency:
  group: deploy-production
  cancel-in-progress: false

on:
  workflow_dispatch:
    inputs:
      target_tag:
        description: 'Git tag to roll back to (e.g. v0.2.49)'
        required: true
        type: string
      confirm:
        description: 'Type "ROLLBACK" to confirm'
        required: true
        type: string

jobs:
  rollback:
    name: Rollback Nexus
    runs-on: ubuntu-latest
    env:
      DEPLOY_PATH: /home/projekte_bao/openclaw/data/openclaw/workspace/nexus
      ENV_TMPFILE: /tmp/nexus-rollback-env
      ENV_POSTGRES_PASSWORD: ${{ secrets.ENV_POSTGRES_PASSWORD }}
      ENV_JWT_KEY: ${{ secrets.ENV_JWT_KEY }}
      ENV_OWNER_PASSWORD: ${{ secrets.ENV_OWNER_PASSWORD }}
      ENV_OPENCLAW_TOKEN: ${{ secrets.ENV_OPENCLAW_TOKEN }}

    steps:
      # ═══════════════════════════════════════════════════
      # Step 0: Safety gate — require explicit confirmation
      # ═══════════════════════════════════════════════════
      - name: Safety Gate
        run: |
          if [ "${{ inputs.confirm }}" != "ROLLBACK" ]; then
            echo "❌ Rollback aborted: confirmation string must be 'ROLLBACK'"
            echo "   You entered: '${{ inputs.confirm }}'"
            exit 1
          fi
          echo "✅ Rollback confirmed — proceeding to ${{ inputs.target_tag }}"

      # ═══════════════════════════════════════════════════
      # Step 1: Checkout target tag
      # ═══════════════════════════════════════════════════
      - name: Checkout target tag
        uses: actions/checkout@v4
        with:
          ref: refs/tags/${{ inputs.target_tag }}
          fetch-depth: 0
          fetch-tags: true

      # ═══════════════════════════════════════════════════
      # Step 2: Verify tag exists
      # ═══════════════════════════════════════════════════
      - name: Verify tag
        run: |
          set -euo pipefail

          ACTUAL_TAG=$(git describe --tags --exact-match 2>/dev/null || echo "")
          if [ -z "$ACTUAL_TAG" ]; then
            echo "❌ Tag '${{ inputs.target_tag }}' not found in repository"
            echo "   Available tags:"
            git tag -l 'v*' | sort -V | tail -20
            exit 1
          fi

          echo "✅ Checked out: $ACTUAL_TAG"
          echo "   Commit:      $(git rev-parse --short HEAD)"
          echo "   Message:     $(git log -1 --oneline)"

          # Read version from VERSION file at this tag
          if [ -f VERSION ]; then
            VERSION=$(cat VERSION | tr -d '[:space:]')
            echo "   VERSION:     $VERSION"
          fi

      # ═══════════════════════════════════════════════════
      # Step 3: Prepare .env from secrets (safe temp file)
      # ═══════════════════════════════════════════════════
      - name: Prepare .env (secrets → temp file)
        run: |
          set -euo pipefail

          cat > "${ENV_TMPFILE}" <<EOF
          # Nexus Production Environment — auto-generated by CD pipeline
          POSTGRES_DB=nexus
          POSTGRES_USER=nexus
          POSTGRES_PASSWORD=${ENV_POSTGRES_PASSWORD}
          JWT_KEY=${ENV_JWT_KEY}
          JWT_ISSUER=nexus
          JWT_AUDIENCE=nexus-web
          OWNER_EMAIL=vmbao62@hotmail.de
          OWNER_PASSWORD=${ENV_OWNER_PASSWORD}
          OWNER_DISPLAY_NAME=
          OPENCLAW_BASE_URL=http://host.docker.internal:18789
          OPENCLAW_GATEWAY_TOKEN=${ENV_OPENCLAW_TOKEN}
          OPENCLAW_GATEWAY_PASSWORD=
          EOF

          chmod 600 "${ENV_TMPFILE}"
          echo "✅ .env written to ${ENV_TMPFILE} (mode 600)"

      # ═══════════════════════════════════════════════════
      # Step 4: Sync rollback code to host
      # ═══════════════════════════════════════════════════
      - name: Sync code to host
        run: |
          set -euo pipefail

          docker run --rm \
            -v "${{ gitea.workspace }}:/src:ro" \
            -v "${DEPLOY_PATH}:/dest" \
            alpine:latest \
            sh -c "
              cd /src && \
              find . -mindepth 1 -maxdepth 1 \
                ! -name .git \
                -exec cp -r {} /dest/ \; && \
              DEST_OWNER=\$(stat -c '%u:%g' /dest) && \
              chown -R \"\$DEST_OWNER\" /dest
            "

          echo "✅ Rollback code (${{ inputs.target_tag }}) synced to ${DEPLOY_PATH}"

      # ═══════════════════════════════════════════════════
      # Step 5: Rebuild & Redeploy
      # ═══════════════════════════════════════════════════
      - name: Rebuild & Redeploy
        run: |
          set -euo pipefail

          docker run --rm \
            -v "${DEPLOY_PATH}:/workspace/nexus" \
            -v "/tmp:/tmp-host:ro" \
            -v /var/run/docker.sock:/var/run/docker.sock \
            -w /workspace/nexus \
            docker:cli \
            sh -c "
              set -e
              echo '🔙 Rolling back to ${{ inputs.target_tag }}'
              docker compose --env-file /tmp-host/$(basename "${ENV_TMPFILE}") build --no-cache
              docker compose --env-file /tmp-host/$(basename "${ENV_TMPFILE}") up -d --wait --force-recreate
            "

          echo "✅ Rollback redeploy completed"

      # ═══════════════════════════════════════════════════
      # Step 6: Clean up temp .env
      # ═══════════════════════════════════════════════════
      - name: Clean up temp .env
        if: always()
        run: |
          if [ -f "${ENV_TMPFILE}" ]; then
            shred -u "${ENV_TMPFILE}" 2>/dev/null || rm -f "${ENV_TMPFILE}"
            echo "🧹 Temp .env removed"
          fi

      # ═══════════════════════════════════════════════════
      # Step 7: Health Check
      # ═══════════════════════════════════════════════════
      - name: Health Check
        run: |
          echo "🏥 Health check after rollback..."
          RETRY=0
          MAX=6
          WAIT=1
          while [ $RETRY -lt $MAX ]; do
            RETRY=$((RETRY + 1))
            if curl -sf --max-time 10 https://nexus.noveria.net/health; then
              echo ""
              echo "✅ Health check passed (attempt $RETRY/$MAX)"
              exit 0
            fi
            echo "⏳ Attempt $RETRY/$MAX failed, waiting ${WAIT}s..."
            sleep $WAIT
            NEXT=$((WAIT + RETRY))
            [ $NEXT -le 15 ] && WAIT=$NEXT || WAIT=15
          done
          echo "❌ Health check failed after $MAX attempts"
          exit 1

      # ═══════════════════════════════════════════════════
      # Step 8: Smoke Test
      # ═══════════════════════════════════════════════════
      - name: Smoke Test
        run: |
          echo "🔍 Smoke test after rollback..."
          PASS=0
          FAIL=0
          BASE="https://nexus.noveria.net"

          check() {
            local path="$1" label="$2" expected="${3:-200}"
            local code
            code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "${BASE}${path}")
            printf "  %-25s HTTP %s" "${label}:" "${code}"
            if [ "$code" = "$expected" ]; then
              echo " ✅"
              PASS=$((PASS + 1))
            else
              echo " ❌ (expected $expected)"
              FAIL=$((FAIL + 1))
            fi
          }

          check "/dashboard"                  "Dashboard"              200
          check "/health"                     "Health API"             200
          check "/api/v1/operations/snapshot" "Operations API (auth)" 401

          echo ""
          echo "Results: $PASS passed, $FAIL failed"
          if [ "$FAIL" -gt 0 ]; then
            echo "❌ Smoke test failed!"
            exit 1
          fi
          echo "✅ Rollback to ${{ inputs.target_tag }} successful"

      # ═══════════════════════════════════════════════════
      # Step 9: Rollback Summary
      # ═══════════════════════════════════════════════════
      - name: Rollback Summary
        if: always()
        run: |
          echo ""
          echo "═══════════════════════════════════════"
          echo "  🔙 Rollback Summary"
          echo "═══════════════════════════════════════"
          echo "  Rolled to:  ${{ inputs.target_tag }}"
          echo "  Triggered:  @${{ gitea.actor }}"
          echo "  Status:     ${{ job.status }}"
          echo "═══════════════════════════════════════"

      # ═══════════════════════════════════════════════════
      # Step 10: Failure → Reviewer Handoff
      # ═══════════════════════════════════════════════════
      - name: 🔴 Rollback Failed — Reviewer Handoff
        if: failure()
        run: |
          echo ""
          echo "┌─────────────────────────────────────────────────────────────┐"
          echo "│  🔴 ROLLBACK FAILED — Reviewer muss fixen                   │"
          echo "├─────────────────────────────────────────────────────────────┤"
          echo "│                                                             │"
          echo "│  Target:  ${{ inputs.target_tag }}"
          echo "│  Job:     ${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_id }}"
          echo "│                                                             │"
          echo "│  → DevOps (Architekt) analysiert den Fehler                │"
          echo "│  → Reviewer (Code-Fixer) behebt das Problem                │"
          echo "│  → DevOps verifiziert mit neuem Deploy                     │"
          echo "│                                                             │"
          echo "│  Letzter bekannter funktionierender Stand:                  │"
          echo "│  → 'git log --oneline -5' zeigt letzte Commits             │"
          echo "│  → Manuellen Rollback erwägen:                              │"
          echo "│      cd /home/projekte_bao/openclaw/data/openclaw/workspace/nexus        │"
          echo "│      docker compose up -d (vorheriger Stand)                │"
          echo "│                                                             │"
          echo "└─────────────────────────────────────────────────────────────┘"
