name: Build & Deploy

on:
  push:
    branches:
      - main
    tags:
      - '*'
  workflow_dispatch:
    inputs:
      skip_long_checks:
        description: 'Skip tests? (true/false)'
        required: false
        default: 'false'

concurrency:
  group: ${{ github.workflow }}
  cancel-in-progress: false

jobs:
  # ──────────────────────────────────────────────────────────────────────────────
  # JOB 1: Prepare & Determine Environment
  # ──────────────────────────────────────────────────────────────────────────────
  prepare:
    name: 🔍 Prepare Environment
    runs-on: docker
    container:
      image: catthehacker/ubuntu:act-latest
    outputs:
      target:           ${{ steps.determine.outputs.target }}
      image_tag:        ${{ steps.determine.outputs.image_tag }}
      env_file:         ${{ steps.determine.outputs.env_file }}
      traefik_host:     ${{ steps.determine.outputs.traefik_host }}
      next_public_base_url: ${{ steps.determine.outputs.next_public_base_url }}
      directus_url:     ${{ steps.determine.outputs.directus_url }}
      directus_host:    ${{ steps.determine.outputs.directus_host }}
      project_name:     ${{ steps.determine.outputs.project_name }}
      is_prod:          ${{ steps.determine.outputs.is_prod }}
      gotify_title:     ${{ steps.determine.outputs.gotify_title }}
      gotify_priority:  ${{ steps.determine.outputs.gotify_priority }}
      short_sha:        ${{ steps.determine.outputs.short_sha }}
      commit_msg:       ${{ steps.determine.outputs.commit_msg }}
    steps:
      - name: 🧹 Maintenance (High Density Cleanup)
        shell: bash
        run: |
          echo "Purging old build layers and dangling images..."
          docker image prune -f
          docker builder prune -f --filter "until=6h"

      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: 🔍 Environment & Version ermitteln
        id: determine
        run: |
          TAG="${{ github.ref_name }}"
          SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-9)
          COMMIT_MSG=$(git log -1 --pretty=%s || echo "No commit message available")
          
          # Base Domain (e.g. example.com)
          DOMAIN_BASE=$(echo "${{ secrets.NEXT_PUBLIC_BASE_URL }}" | sed -E 's|https?://||' | sed -E 's|/.*||')
          PRJ_ID="${{ github.event.repository.name }}"

          if [[ "${{ github.ref_type }}" == "branch" && "$TAG" == "main" ]]; then
              TARGET="testing"
              IMAGE_TAG="main-${SHORT_SHA}"
              ENV_FILE=".env.testing"
              TRAEFIK_HOST="\`testing.\${DOMAIN_BASE}\`"
              NEXT_PUBLIC_BASE_URL="https://testing.\${DOMAIN_BASE}"
              DIRECTUS_URL="https://cms.testing.\${DOMAIN_BASE}"
              DIRECTUS_HOST="\`cms.testing.\${DOMAIN_BASE}\`"
              PROJECT_NAME="\${PRJ_ID}-testing"
              IS_PROD="false"
              GOTIFY_TITLE="🧪 Testing-Deploy"
              GOTIFY_PRIORITY=4
          elif [[ "${{ github.ref_type }}" == "tag" ]]; then
            if [[ "$TAG" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
              TARGET="production"
              IMAGE_TAG="$TAG"
              ENV_FILE=".env.prod"
              TRAEFIK_HOST="\`\${DOMAIN_BASE}\`, \`www.\${DOMAIN_BASE}\`"
              NEXT_PUBLIC_BASE_URL="https://\${DOMAIN_BASE}"
              DIRECTUS_URL="https://cms.\${DOMAIN_BASE}"
              DIRECTUS_HOST="\`cms.\${DOMAIN_BASE}\`"
              PROJECT_NAME="\${PRJ_ID}-prod"
              IS_PROD="true"
              GOTIFY_TITLE="🚀 Production-Release"
              GOTIFY_PRIORITY=6
            elif [[ "$TAG" =~ -rc || "$TAG" =~ -beta || "$TAG" =~ -alpha ]]; then
              TARGET="staging"
              IMAGE_TAG="$TAG"
              ENV_FILE=".env.staging"
              TRAEFIK_HOST="\`staging.\${DOMAIN_BASE}\`"
              NEXT_PUBLIC_BASE_URL="https://staging.\${DOMAIN_BASE}"
              DIRECTUS_URL="https://cms.staging.\${DOMAIN_BASE}"
              DIRECTUS_HOST="\`cms.staging.\${DOMAIN_BASE}\`"
              PROJECT_NAME="\${PRJ_ID}-staging"
              IS_PROD="false"
              GOTIFY_TITLE="🧪 Staging-Deploy (Pre-Release)"
              GOTIFY_PRIORITY=5
            else
              TARGET="skip"
            fi
          else
            TARGET="skip"
          fi

          {
            echo "target=$TARGET"
            echo "image_tag=$IMAGE_TAG"
            echo "env_file=$ENV_FILE"
            echo "traefik_host=$TRAEFIK_HOST"
            echo "next_public_base_url=$NEXT_PUBLIC_BASE_URL"
            echo "directus_url=$DIRECTUS_URL"
            echo "directus_host=$DIRECTUS_HOST"
            echo "project_name=$PROJECT_NAME"
            echo "is_prod=$IS_PROD"
            echo "gotify_title=$GOTIFY_TITLE"
            echo "gotify_priority=$GOTIFY_PRIORITY"
            echo "short_sha=$SHORT_SHA"
            echo "commit_msg=$COMMIT_MSG"
          } >> "$GITHUB_OUTPUT"

  # ──────────────────────────────────────────────────────────────────────────────
  # JOB 2: Quality Assurance (Lint & Test)
  # ──────────────────────────────────────────────────────────────────────────────
  qa:
    name: 🧪 QA
    needs: prepare
    if: needs.prepare.outputs.target != 'skip'
    runs-on: docker
    container:
      image: catthehacker/ubuntu:act-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      - name: 🧪 Run Checks in Parallel
        if: github.event.inputs.skip_long_checks != 'true'
        run: |
          npm run lint &
          LINT_PID=$!
          npm run typecheck &
          TYPE_PID=$!
          npm run test &
          TEST_PID=$!

          wait $LINT_PID || exit 1
          wait $TYPE_PID || exit 1
          wait $TEST_PID || exit 1

  # ──────────────────────────────────────────────────────────────────────────────
  # JOB 3: Build & Push
  # ──────────────────────────────────────────────────────────────────────────────
  build:
    name: 🏗️ Build
    needs: prepare
    if: needs.prepare.outputs.target != 'skip'
    runs-on: docker
    container:
      image: catthehacker/ubuntu:act-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: 🐳 Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: 🔐 Registry Login
        uses: docker/login-action@v3
        with:
          registry: registry.infra.mintel.me
          username: ${{ secrets.REGISTRY_USER }}
          password: ${{ secrets.REGISTRY_PASS }}

      - name: 🏗️ Docker Build & Push
        uses: docker/build-push-action@v5
        with:
          context: .
          file: packages/infra/docker/Dockerfile.nextjs
          platforms: linux/arm64
          pull: true
          build-args: |
            NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }}
            NEXT_PUBLIC_TARGET=${{ needs.prepare.outputs.target }}
          push: true
          secrets: |
            NPM_TOKEN=${{ secrets.NPM_TOKEN }}
          tags: registry.infra.mintel.me/mintel/${{ github.event.repository.name }}:${{ needs.prepare.outputs.image_tag }}
          cache-from: type=registry,ref=registry.infra.mintel.me/mintel/${{ github.event.repository.name }}:buildcache
          cache-to: type=registry,ref=registry.infra.mintel.me/mintel/${{ github.event.repository.name }}:buildcache,mode=max

  # ──────────────────────────────────────────────────────────────────────────────
  # JOB 4: Deploy
  # ──────────────────────────────────────────────────────────────────────────────
  deploy:
    name: 🚀 Deploy
    needs: [prepare, build, qa]
    if: needs.prepare.outputs.target != 'skip'
    runs-on: docker
    container:
      image: catthehacker/ubuntu:act-latest
    env:
      TARGET: ${{ needs.prepare.outputs.target }}
      IMAGE_TAG: ${{ needs.prepare.outputs.image_tag }}
      PROJECT_NAME: ${{ needs.prepare.outputs.project_name }}
      ENV_FILE: ${{ needs.prepare.outputs.env_file }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: 🚀 Deploy via SSH
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.SSH_KEY }}" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts 2>/dev/null

          # Generate .env from secrets
          cat > /tmp/app.env << EOF
          # Generated by CI - $TARGET - $(date -u)
          NODE_ENV=production
          IMAGE_TAG=$IMAGE_TAG
          TRAEFIK_HOST=${{ needs.prepare.outputs.traefik_host }}
          PROJECT_NAME=$PROJECT_NAME
          ENV_FILE=$ENV_FILE
          
          # App Config
          NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }}
          NEXT_PUBLIC_TARGET=$TARGET
          
          # Directus Config
          DIRECTUS_URL=${{ needs.prepare.outputs.directus_url }}
          DIRECTUS_HOST=${{ needs.prepare.outputs.directus_host }}
          DIRECTUS_KEY=${{ secrets.DIRECTUS_KEY }}
          DIRECTUS_SECRET=${{ secrets.DIRECTUS_SECRET }}
          DIRECTUS_ADMIN_EMAIL=${{ secrets.DIRECTUS_ADMIN_EMAIL }}
          DIRECTUS_ADMIN_PASSWORD=${{ secrets.DIRECTUS_ADMIN_PASSWORD }}
          DIRECTUS_DB_PASSWORD=${{ secrets.DIRECTUS_DB_PASSWORD }}
          
          # Gatekeeper
          GATEKEEPER_PASSWORD=${{ secrets.GATEKEEPER_PASSWORD || 'mintel' }}
          AUTH_MIDDLEWARE=$( [[ "$TARGET" == "production" ]] && echo "compress" || echo "$PROJECT_NAME-auth,compress" )
          EOF

          APP_DIR="/home/deploy/sites/${{ github.event.repository.name }}"
          ssh root@${{ secrets.SSH_HOST }} "mkdir -p $APP_DIR"
          scp /tmp/app.env root@${{ secrets.SSH_HOST }}:$APP_DIR/$ENV_FILE
          scp docker-compose.yml root@${{ secrets.SSH_HOST }}:$APP_DIR/docker-compose.yml

          ssh root@${{ secrets.SSH_HOST }} IMAGE_TAG="$IMAGE_TAG" ENV_FILE="$ENV_FILE" PROJECT_NAME="$PROJECT_NAME" bash << 'EOF'
            set -e
            cd "/home/deploy/sites/${{ github.event.repository.name }}"
            chmod 600 "$ENV_FILE"
            echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
            docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" pull
            docker compose -p "$PROJECT_NAME" --env-file "$ENV_FILE" up -d --remove-orphans
            docker system prune -f --filter "until=24h"
          EOF

      - name: 🧹 Post-Deploy Cleanup (Runner)
        if: always()
        run: docker builder prune -f --filter "until=1h"

  # ──────────────────────────────────────────────────────────────────────────────
  # JOB 5: Notifications
  # ──────────────────────────────────────────────────────────────────────────────
  notifications:
    name: 🔔 Notifications
    needs: [prepare, deploy]
    if: always()
    runs-on: docker
    container:
      image: catthehacker/ubuntu:act-latest
    steps:
      - name: 🔔 Gotify - Success
        if: needs.deploy.result == 'success'
        run: |
          curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
            -F "title=${{ needs.prepare.outputs.gotify_title }}" \
            -F "message=Erfolgreich deployt auf **${{ needs.prepare.outputs.target }}**\n\nVersion: **${{ needs.prepare.outputs.image_tag }}**\nCommit: ${{ needs.prepare.outputs.short_sha }}\nRun: ${{ github.run_id }}" \
            -F "priority=4" || true

      - name: 🔔 Gotify - Failure
        if: |
          needs.prepare.result == 'failure' ||
          needs.qa.result == 'failure' ||
          needs.build.result == 'failure' ||
          needs.deploy.result == 'failure'
        run: |
          curl -s -k -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
            -F "title=❌ Deployment FEHLGESCHLAGEN – ${{ github.event.repository.name }}" \
            -F "message=**Fehler beim Deploy auf ${{ needs.prepare.outputs.target || 'unknown' }}**\n\nRun: ${{ github.run_id }}\nBitte Logs prüfen!" \
            -F "priority=8" || true
