name: Deploy to Production

on:
  # Manual trigger with approval - required for production
  workflow_dispatch:
    inputs:
      skip_tests:
        description: 'Skip pre-deployment tests'
        required: false
        default: 'false'
        type: boolean
      skip_backup:
        description: 'Skip database backup (not recommended)'
        required: false
        default: 'false'
        type: boolean
      skip_migrations:
        description: 'Skip database migrations'
        required: false
        default: 'false'
        type: boolean
      deploy_tag:
        description: 'Git tag or commit to deploy (leave empty for latest main)'
        required: false
        type: string

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

env:
  NODE_VERSION: '22'
  PNPM_VERSION: '9'
  PRODUCTION_HOST: '162.55.85.18'
  PRODUCTION_USER: 'payload'
  PRODUCTION_PATH: '/home/payload/payload-cms'
  PRODUCTION_URL: 'https://cms.c2sgmbh.de'

jobs:
  # ===========================================================================
  # Pre-flight Checks
  # ===========================================================================
  preflight:
    name: Pre-flight Checks
    runs-on: ubuntu-latest
    outputs:
      deploy_sha: ${{ steps.get-sha.outputs.sha }}
      deploy_tag: ${{ steps.get-sha.outputs.tag }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          ref: ${{ inputs.deploy_tag || 'main' }}
          fetch-depth: 0

      - name: Get deployment SHA
        id: get-sha
        run: |
          SHA=$(git rev-parse HEAD)
          TAG="${{ inputs.deploy_tag }}"
          if [ -z "$TAG" ]; then
            TAG="main ($(git rev-parse --short HEAD))"
          fi
          echo "sha=$SHA" >> $GITHUB_OUTPUT
          echo "tag=$TAG" >> $GITHUB_OUTPUT
          echo "Deploying: $TAG"
          echo "SHA: $SHA"

      - name: Verify branch is up to date with develop
        run: |
          git fetch origin develop
          MAIN_SHA=$(git rev-parse HEAD)
          DEVELOP_SHA=$(git rev-parse origin/develop)

          # Check if main contains the latest develop commits
          if ! git merge-base --is-ancestor origin/develop HEAD 2>/dev/null; then
            echo "::warning::main is not up to date with develop. Consider merging develop first."
          fi

      - name: Check for pending migrations
        run: |
          # List migration files that might need to be run
          if [ -d "src/migrations" ]; then
            MIGRATION_COUNT=$(find src/migrations -name "*.ts" -type f | wc -l)
            echo "Found $MIGRATION_COUNT migration files"
          fi

  # ===========================================================================
  # Pre-deployment Tests (optional, recommended)
  # ===========================================================================
  pre-tests:
    name: Pre-deployment Tests
    runs-on: ubuntu-latest
    needs: [preflight]
    if: ${{ inputs.skip_tests != true }}
    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_USER: payload
          POSTGRES_PASSWORD: payload_test_password
          POSTGRES_DB: payload_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          ref: ${{ needs.preflight.outputs.deploy_sha }}

      - name: Setup pnpm
        uses: pnpm/action-setup@v3
        with:
          version: ${{ env.PNPM_VERSION }}

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'pnpm'

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

      - name: Run ESLint
        run: pnpm lint

      - name: Run Unit Tests
        run: pnpm test:unit
        timeout-minutes: 10
        env:
          CSRF_SECRET: test-csrf-secret
          PAYLOAD_SECRET: test-payload-secret
          PAYLOAD_PUBLIC_SERVER_URL: https://test.example.com
          NEXT_PUBLIC_SERVER_URL: https://test.example.com
          EMAIL_DELIVERY_DISABLED: 'true'
          DATABASE_URI: postgresql://payload:payload_test_password@localhost:5432/payload_test

      - name: Build Test
        run: pnpm build
        env:
          PAYLOAD_SECRET: build-secret-placeholder
          DATABASE_URI: postgresql://placeholder:placeholder@localhost:5432/placeholder
          NEXT_PUBLIC_SERVER_URL: https://build.example.com
          PAYLOAD_PUBLIC_SERVER_URL: https://build.example.com
          CONSENT_LOGGING_API_KEY: ci-consent-api-key-placeholder
          IP_ANONYMIZATION_PEPPER: ci-anonymization-pepper-placeholder

  # ===========================================================================
  # Database Backup (before deployment)
  # ===========================================================================
  backup:
    name: Pre-deployment Backup
    runs-on: ubuntu-latest
    needs: [preflight, pre-tests]
    if: always() && (needs.pre-tests.result == 'success' || needs.pre-tests.result == 'skipped') && inputs.skip_backup != true
    steps:
      - name: Configure SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.PRODUCTION_SSH_KEY }}" > ~/.ssh/production_key
          chmod 600 ~/.ssh/production_key
          cat >> ~/.ssh/config << EOF
          Host production
            HostName ${{ env.PRODUCTION_HOST }}
            User ${{ env.PRODUCTION_USER }}
            IdentityFile ~/.ssh/production_key
            StrictHostKeyChecking no
            UserKnownHostsFile /dev/null
          EOF

      - name: Create database backup
        run: |
          ssh production << 'ENDSSH'
            set -e
            echo "=== Creating pre-deployment backup ==="

            BACKUP_DIR="$HOME/backups/pre-deploy"
            BACKUP_FILE="$BACKUP_DIR/payload_db_$(date +%Y-%m-%d_%H-%M-%S).sql.gz"

            mkdir -p "$BACKUP_DIR"

            # Create backup using pg_dump
            PGPASSWORD=$(grep 'payload_db' ~/.pgpass | cut -d: -f5) \
              pg_dump -h localhost -U payload payload_db | gzip > "$BACKUP_FILE"

            # Keep only last 5 pre-deploy backups
            ls -t "$BACKUP_DIR"/payload_db_*.sql.gz 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null || true

            echo "Backup created: $BACKUP_FILE"
            ls -lh "$BACKUP_FILE"
          ENDSSH

  # ===========================================================================
  # Deploy to Production
  # ===========================================================================
  deploy:
    name: Deploy to Production
    runs-on: ubuntu-latest
    needs: [preflight, pre-tests, backup]
    if: always() && (needs.pre-tests.result == 'success' || needs.pre-tests.result == 'skipped') && (needs.backup.result == 'success' || needs.backup.result == 'skipped')
    environment:
      name: production
      url: ${{ env.PRODUCTION_URL }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          ref: ${{ needs.preflight.outputs.deploy_sha }}

      - name: Configure SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.PRODUCTION_SSH_KEY }}" > ~/.ssh/production_key
          chmod 600 ~/.ssh/production_key
          cat >> ~/.ssh/config << EOF
          Host production
            HostName ${{ env.PRODUCTION_HOST }}
            User ${{ env.PRODUCTION_USER }}
            IdentityFile ~/.ssh/production_key
            StrictHostKeyChecking no
            UserKnownHostsFile /dev/null
          EOF

      - name: Deploy to Production
        id: deploy
        run: |
          DEPLOY_SHA="${{ needs.preflight.outputs.deploy_sha }}"
          SKIP_MIGRATIONS="${{ inputs.skip_migrations }}"

          ssh production << ENDSSH
            set -e

            echo "=============================================="
            echo "  PRODUCTION DEPLOYMENT"
            echo "=============================================="
            echo "Time: \$(date)"
            echo "SHA: $DEPLOY_SHA"
            echo "Skip Migrations: $SKIP_MIGRATIONS"
            echo ""

            cd ${{ env.PRODUCTION_PATH }}

            # Save current commit for rollback
            PREVIOUS_SHA=\$(git rev-parse HEAD)
            echo "\$PREVIOUS_SHA" > /tmp/previous_deploy_sha
            echo "Previous commit: \$PREVIOUS_SHA"

            # Stash any local changes
            git stash --include-untracked 2>/dev/null || true

            # Fetch and checkout
            echo ""
            echo "=== Fetching code ==="
            git fetch origin main
            git checkout main
            git reset --hard $DEPLOY_SHA

            # Install dependencies
            echo ""
            echo "=== Installing dependencies ==="
            pnpm install --frozen-lockfile

            # Run migrations (unless skipped)
            if [ "$SKIP_MIGRATIONS" != "true" ]; then
              echo ""
              echo "=== Running migrations ==="
              pnpm payload migrate || echo "No migrations to run"
            else
              echo ""
              echo "=== Skipping migrations (as requested) ==="
            fi

            # Stop services before build to free memory
            echo ""
            echo "=== Stopping services for build ==="
            pm2 stop payload 2>/dev/null || true
            pm2 stop queue-worker 2>/dev/null || true

            # Build
            echo ""
            echo "=== Building application ==="
            NODE_OPTIONS="--max-old-space-size=2048" pnpm build

            # Restart services
            echo ""
            echo "=== Starting services ==="
            pm2 restart payload --update-env 2>/dev/null || pm2 start ecosystem.config.cjs --only payload
            pm2 restart queue-worker --update-env 2>/dev/null || pm2 start ecosystem.config.cjs --only queue-worker

            # Wait for startup
            echo ""
            echo "=== Waiting for services to start ==="
            sleep 10

            # Show status
            pm2 status

            echo ""
            echo "=== Deployment Complete ==="
            echo "Time: \$(date)"
          ENDSSH

      - name: Health Check
        id: health-check
        run: |
          echo "Waiting for service to be fully ready..."
          sleep 10

          MAX_RETRIES=5
          RETRY_COUNT=0

          while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
            HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${{ env.PRODUCTION_URL }}/admin" 2>/dev/null || echo "000")

            if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 400 ]; then
              echo "Health check passed (HTTP $HTTP_STATUS)"
              echo "status=success" >> $GITHUB_OUTPUT
              exit 0
            fi

            RETRY_COUNT=$((RETRY_COUNT + 1))
            echo "Health check attempt $RETRY_COUNT failed (HTTP $HTTP_STATUS), retrying in 10s..."
            sleep 10
          done

          echo "Health check failed after $MAX_RETRIES attempts"
          echo "status=failed" >> $GITHUB_OUTPUT
          exit 1

      - name: Rollback on failure
        if: failure() && steps.health-check.outputs.status == 'failed'
        run: |
          echo "Deployment failed, initiating rollback..."

          ssh production << 'ENDSSH'
            set -e
            cd ${{ env.PRODUCTION_PATH }}

            if [ -f /tmp/previous_deploy_sha ]; then
              PREVIOUS_SHA=$(cat /tmp/previous_deploy_sha)
              echo "Rolling back to: $PREVIOUS_SHA"

              git checkout main
              git reset --hard $PREVIOUS_SHA

              pnpm install --frozen-lockfile
              pm2 stop payload 2>/dev/null || true
              NODE_OPTIONS="--max-old-space-size=2048" pnpm build
              pm2 restart payload --update-env
              pm2 restart queue-worker --update-env

              echo "Rollback complete"
              pm2 status
            else
              echo "No previous SHA found, manual intervention required"
              exit 1
            fi
          ENDSSH

  # ===========================================================================
  # Post-deployment Verification
  # ===========================================================================
  verify:
    name: Post-deployment Verification
    runs-on: ubuntu-latest
    needs: [deploy]
    if: success()
    steps:
      - name: Full Health Check
        run: |
          echo "Running comprehensive health checks..."

          # Check Admin Panel
          ADMIN_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${{ env.PRODUCTION_URL }}/admin")
          echo "Admin Panel: HTTP $ADMIN_STATUS"

          # Check API
          API_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "${{ env.PRODUCTION_URL }}/api")
          echo "API: HTTP $API_STATUS"

          # Summary
          if [ "$ADMIN_STATUS" -ge 200 ] && [ "$ADMIN_STATUS" -lt 400 ] && \
             [ "$API_STATUS" -ge 200 ] && [ "$API_STATUS" -lt 400 ]; then
            echo "All health checks passed!"
          else
            echo "Some health checks failed"
            exit 1
          fi

  # ===========================================================================
  # Deployment Summary
  # ===========================================================================
  summary:
    name: Deployment Summary
    runs-on: ubuntu-latest
    needs: [preflight, pre-tests, backup, deploy, verify]
    if: always()
    steps:
      - name: Create deployment summary
        run: |
          echo "## Production Deployment Summary" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "| Step | Status |" >> $GITHUB_STEP_SUMMARY
          echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
          echo "| Pre-flight | ${{ needs.preflight.result }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Pre-tests | ${{ needs.pre-tests.result }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Backup | ${{ needs.backup.result }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Deploy | ${{ needs.deploy.result }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Verify | ${{ needs.verify.result }} |" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### Details" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "| Property | Value |" >> $GITHUB_STEP_SUMMARY
          echo "|----------|-------|" >> $GITHUB_STEP_SUMMARY
          echo "| Environment | Production |" >> $GITHUB_STEP_SUMMARY
          echo "| URL | ${{ env.PRODUCTION_URL }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Deployed | \`${{ needs.preflight.outputs.deploy_tag }}\` |" >> $GITHUB_STEP_SUMMARY
          echo "| SHA | \`${{ needs.preflight.outputs.deploy_sha }}\` |" >> $GITHUB_STEP_SUMMARY
          echo "| Triggered by | ${{ github.actor }} |" >> $GITHUB_STEP_SUMMARY
          echo "| Time | $(date -u '+%Y-%m-%d %H:%M:%S UTC') |" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY

          # Overall status
          if [ "${{ needs.verify.result }}" == "success" ]; then
            echo "### Result: Deployment Successful" >> $GITHUB_STEP_SUMMARY
          else
            echo "### Result: Deployment Failed" >> $GITHUB_STEP_SUMMARY
            echo "" >> $GITHUB_STEP_SUMMARY
            echo "Check the logs above for details. If automatic rollback occurred, the previous version should be running." >> $GITHUB_STEP_SUMMARY
          fi

      - name: Notify on failure
        if: needs.deploy.result == 'failure' || needs.verify.result == 'failure'
        run: |
          echo "::error::Production deployment failed! Check logs for details."
