name: CD Production

# Production pipeline — Hetzner k3s.
#
# SECURITY: Two mandatory gates before any production deployment:
#   1. quality-gate  — lint + unit tests on the exact commit being deployed
#   2. verify-image  — confirms preprod-SHA image EXISTS in registry,
#                      which proves this commit passed the full preprod
#                      pipeline (lint + unit + integration + docker build).
#                      If someone merges to main without going through preprod,
#                      this step fails and the deployment is blocked.
#
# Flow: quality-gate → verify-image → promote → deploy → notify
#
# Secrets required:
#   REGISTRY_TOKEN          — Scaleway registry (read/write)
#   HETZNER_KUBECONFIG      — base64: cat ~/.kube/kubeconfig-xpeditis-prod | base64 -w 0
#   PROD_BACKEND_URL        — https://api.xpeditis.com
#   PROD_FRONTEND_URL       — https://app.xpeditis.com
#   DISCORD_WEBHOOK_URL

on:
  push:
    branches: [main]

concurrency:
  group: cd-production
  cancel-in-progress: false

env:
  REGISTRY: rg.fr-par.scw.cloud/weworkstudio
  NODE_VERSION: '20'
  K8S_NAMESPACE: xpeditis-prod

jobs:
  # ── 1. Quality Gate ──────────────────────────────────────────────────
  # Runs on every prod deployment regardless of what happened in preprod.
  backend-quality:
    name: Backend — Lint
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: apps/backend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
          cache-dependency-path: apps/backend/package-lock.json
      - run: npm install --legacy-peer-deps
      - run: npm run lint

  frontend-quality:
    name: Frontend — Lint & Type-check
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: apps/frontend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
          cache-dependency-path: apps/frontend/package-lock.json
      - run: npm ci --legacy-peer-deps
      - run: npm run lint
      - run: npm run type-check

  backend-tests:
    name: Backend — Unit Tests
    runs-on: ubuntu-latest
    needs: backend-quality
    defaults:
      run:
        working-directory: apps/backend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
          cache-dependency-path: apps/backend/package-lock.json
      - run: npm install --legacy-peer-deps
      - run: npm test -- --passWithNoTests

  frontend-tests:
    name: Frontend — Unit Tests
    runs-on: ubuntu-latest
    needs: frontend-quality
    defaults:
      run:
        working-directory: apps/frontend
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
          cache-dependency-path: apps/frontend/package-lock.json
      - run: npm ci --legacy-peer-deps
      - run: npm test -- --passWithNoTests

  # ── 2. Image Verification ────────────────────────────────────────────
  # Checks that preprod-SHA tags exist for this EXACT commit.
  # This is the security gate: if the preprod pipeline never ran for this
  # commit (or failed before the docker build step), this job fails and
  # the deployment is fully blocked.
  verify-image:
    name: Verify Preprod Image Exists
    runs-on: ubuntu-latest
    needs: [backend-tests, frontend-tests]
    outputs:
      sha: ${{ steps.sha.outputs.short }}
    steps:
      - name: Short SHA
        id: sha
        run: echo "short=$(echo ${{ github.sha }} | cut -c1-7)" >> $GITHUB_OUTPUT

      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: nologin
          password: ${{ secrets.REGISTRY_TOKEN }}

      - name: Check backend image preprod-SHA
        run: |
          TAG="${{ env.REGISTRY }}/xpeditis-backend:preprod-${{ steps.sha.outputs.short }}"
          echo "Verifying: $TAG"
          docker buildx imagetools inspect "$TAG" || {
            echo ""
            echo "BLOCKED: Image $TAG not found in registry."
            echo "This commit was not built by the preprod pipeline."
            echo "Merge to preprod first and wait for the full pipeline to succeed."
            exit 1
          }

      - name: Check frontend image preprod-SHA
        run: |
          TAG="${{ env.REGISTRY }}/xpeditis-frontend:preprod-${{ steps.sha.outputs.short }}"
          echo "Verifying: $TAG"
          docker buildx imagetools inspect "$TAG" || {
            echo ""
            echo "BLOCKED: Image $TAG not found in registry."
            echo "This commit was not built by the preprod pipeline."
            echo "Merge to preprod first and wait for the full pipeline to succeed."
            exit 1
          }

  # ── 3. Promote Images ────────────────────────────────────────────────
  # Re-tags preprod-SHA → latest + prod-SHA within Scaleway.
  # No rebuild. No layer transfer. Manifest-level operation only.
  promote-images:
    name: Promote Images (preprod-SHA → prod)
    runs-on: ubuntu-latest
    needs: verify-image
    steps:
      - uses: docker/setup-buildx-action@v3

      - uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: nologin
          password: ${{ secrets.REGISTRY_TOKEN }}

      - name: Promote backend
        run: |
          SHA="${{ needs.verify-image.outputs.sha }}"
          docker buildx imagetools create \
            --tag ${{ env.REGISTRY }}/xpeditis-backend:latest \
            --tag ${{ env.REGISTRY }}/xpeditis-backend:prod-${SHA} \
            ${{ env.REGISTRY }}/xpeditis-backend:preprod-${SHA}
          echo "Backend promoted: preprod-${SHA} → latest + prod-${SHA}"

      - name: Promote frontend
        run: |
          SHA="${{ needs.verify-image.outputs.sha }}"
          docker buildx imagetools create \
            --tag ${{ env.REGISTRY }}/xpeditis-frontend:latest \
            --tag ${{ env.REGISTRY }}/xpeditis-frontend:prod-${SHA} \
            ${{ env.REGISTRY }}/xpeditis-frontend:preprod-${SHA}
          echo "Frontend promoted: preprod-${SHA} → latest + prod-${SHA}"

  # ── 4. Deploy to k3s ─────────────────────────────────────────────────
  deploy:
    name: Deploy to Production (k3s)
    runs-on: ubuntu-latest
    needs: [verify-image, promote-images]
    environment:
      name: production
      url: https://app.xpeditis.com
    steps:
      - name: Configure kubectl
        run: |
          mkdir -p ~/.kube
          echo "${{ secrets.HETZNER_KUBECONFIG }}" | base64 -d > ~/.kube/config
          chmod 600 ~/.kube/config
          kubectl cluster-info
          kubectl get nodes -o wide

      - name: Deploy backend
        id: deploy-backend
        run: |
          SHA="${{ needs.verify-image.outputs.sha }}"
          IMAGE="${{ env.REGISTRY }}/xpeditis-backend:prod-${SHA}"
          echo "Deploying: $IMAGE"
          kubectl set image deployment/xpeditis-backend backend="$IMAGE" -n ${{ env.K8S_NAMESPACE }}
          kubectl rollout status deployment/xpeditis-backend -n ${{ env.K8S_NAMESPACE }} --timeout=300s
          echo "Backend rollout complete."

      - name: Deploy frontend
        id: deploy-frontend
        run: |
          SHA="${{ needs.verify-image.outputs.sha }}"
          IMAGE="${{ env.REGISTRY }}/xpeditis-frontend:prod-${SHA}"
          echo "Deploying: $IMAGE"
          kubectl set image deployment/xpeditis-frontend frontend="$IMAGE" -n ${{ env.K8S_NAMESPACE }}
          kubectl rollout status deployment/xpeditis-frontend -n ${{ env.K8S_NAMESPACE }} --timeout=300s
          echo "Frontend rollout complete."

      - name: Auto-rollback on deployment failure
        if: failure()
        run: |
          echo "Deployment failed — initiating rollback..."
          kubectl rollout undo deployment/xpeditis-backend -n ${{ env.K8S_NAMESPACE }}
          kubectl rollout undo deployment/xpeditis-frontend -n ${{ env.K8S_NAMESPACE }}
          kubectl rollout status deployment/xpeditis-backend -n ${{ env.K8S_NAMESPACE }} --timeout=120s
          kubectl rollout status deployment/xpeditis-frontend -n ${{ env.K8S_NAMESPACE }} --timeout=120s
          echo "Rollback complete. Previous version is live."

  # ── Notifications ────────────────────────────────────────────────────
  notify-success:
    name: Notify Success
    runs-on: ubuntu-latest
    needs: [verify-image, deploy]
    if: success()
    steps:
      - run: |
          curl -s -H "Content-Type: application/json" -d '{
            "embeds": [{
              "title": "🚀 Production Deployed & Healthy",
              "color": 3066993,
              "fields": [
                {"name": "Author", "value": "${{ github.actor }}", "inline": true},
                {"name": "Version", "value": "`prod-${{ needs.verify-image.outputs.sha }}`", "inline": true},
                {"name": "Cluster", "value": "Hetzner k3s — `xpeditis-prod`", "inline": false},
                {"name": "Workflow", "value": "[${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})", "inline": false}
              ],
              "footer": {"text": "Xpeditis CI/CD • Production"}
            }]
          }' ${{ secrets.DISCORD_WEBHOOK_URL }}

  notify-failure:
    name: Notify Failure
    runs-on: ubuntu-latest
    needs: [backend-quality, frontend-quality, backend-tests, frontend-tests, verify-image, promote-images, deploy]
    if: failure()
    steps:
      - run: |
          curl -s -H "Content-Type: application/json" -d '{
            "content": "@here PRODUCTION PIPELINE FAILED",
            "embeds": [{
              "title": "🔴 Production Pipeline Failed",
              "description": "Check the workflow for details. Auto-rollback was triggered if the failure was during deploy.",
              "color": 15158332,
              "fields": [
                {"name": "Author", "value": "${{ github.actor }}", "inline": true},
                {"name": "Workflow", "value": "[${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})", "inline": false},
                {"name": "Rollback", "value": "[Run rollback workflow](${{ github.server_url }}/${{ github.repository }}/actions/workflows/rollback.yml)", "inline": false}
              ],
              "footer": {"text": "Xpeditis CI/CD • Production"}
            }]
          }' ${{ secrets.DISCORD_WEBHOOK_URL }}
