name: Build & Deploy

on:
  push:
    branches:
      - main
    tags:
      - 'v*'
  workflow_dispatch:

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

jobs:
  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 }}
      gatekeeper_host:  ${{ steps.determine.outputs.gatekeeper_host }}
      traefik_rule:     ${{ steps.determine.outputs.traefik_rule }}
      gatekeeper_rule:  ${{ steps.determine.outputs.gatekeeper_rule }}
      project_name:     ${{ steps.determine.outputs.project_name }}
    steps:
      - name: 🔍 Debug Info
        shell: bash
        run: |
          echo "ref_name: ${{ github.ref_name }}"
          echo "ref_type: ${{ github.ref_type }}"
          echo "tag: ${{ github.ref_name }}"

      - name: 🧹 Maintenance (Runner Cleanup)
        continue-on-error: true
        shell: bash
        run: |
          docker image prune -f || true
          docker builder prune -f --filter "until=24h" || true

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

      - name: 🔍 Determine Environment
        id: determine
        shell: bash
        run: |
          REF="${{ github.ref }}"
          REF_NAME="${{ github.ref_name }}"
          REF_TYPE="${{ github.ref_type }}"
          SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
          DOMAIN_BASE="mintel.me"
          PRJ_ID="mintel-me"

          echo "Detecting environment for ref: $REF ($REF_NAME, type: $REF_TYPE)"

          # Fallback for REF_TYPE if missing
          if [[ -z "$REF_TYPE" ]]; then
            if [[ "$REF" == refs/tags/* ]]; then
              REF_TYPE="tag"
            elif [[ "$REF" == refs/heads/* ]]; then
              REF_TYPE="branch"
            fi
          fi

          if [[ "$REF_TYPE" == "branch" && "$REF_NAME" == "main" ]]; then
            TARGET="testing"
            IMAGE_TAG="testing-${SHORT_SHA}"
            ENV_FILE=".env.testing"
            TRAEFIK_HOST="testing.${DOMAIN_BASE}"
            GATEKEEPER_HOST="gatekeeper.testing.${DOMAIN_BASE}"
            NEXT_PUBLIC_BASE_URL="https://testing.${DOMAIN_BASE}"
            DIRECTUS_URL="https://cms.testing.${DOMAIN_BASE}"
            DIRECTUS_HOST="cms.testing.${DOMAIN_BASE}"
          elif [[ "$REF_TYPE" == "tag" ]]; then
            if [[ "$REF_NAME" =~ ^v[0-9]+\.[0-9]+(\.[0-9]+)?$ ]]; then
              TARGET="production"
              IMAGE_TAG="$REF_NAME"
              ENV_FILE=".env.prod"
              TRAEFIK_HOST="${DOMAIN_BASE}" # Primary domain
              GATEKEEPER_HOST="gatekeeper.${DOMAIN_BASE}"
              NEXT_PUBLIC_BASE_URL="https://${DOMAIN_BASE}"
              DIRECTUS_URL="https://cms.${DOMAIN_BASE}"
              DIRECTUS_HOST="cms.${DOMAIN_BASE}"
            elif [[ "$REF_NAME" =~ -rc || "$REF_NAME" =~ -beta || "$REF_NAME" =~ -alpha ]]; then
              TARGET="staging"
              IMAGE_TAG="$REF_NAME"
              ENV_FILE=".env.staging"
              TRAEFIK_HOST="staging.${DOMAIN_BASE}"
              GATEKEEPER_HOST="gatekeeper.staging.${DOMAIN_BASE}"
              NEXT_PUBLIC_BASE_URL="https://staging.${DOMAIN_BASE}"
              DIRECTUS_URL="https://cms.staging.${DOMAIN_BASE}"
              DIRECTUS_HOST="cms.staging.${DOMAIN_BASE}"
            else
              TARGET="skip"
              echo "Tag $REF_NAME did not match any environment pattern."
            fi
          else
            TARGET="skip"
            echo "Ref type $REF_TYPE is not handled for deployment."
          fi

          # Determine Rules based on target (if not skipped)
          if [[ "$TARGET" != "skip" ]]; then
            if [[ "$TARGET" == "production" ]]; then
              TRAEFIK_RULE="Host(\`${DOMAIN_BASE}\`) || Host(\`www.${DOMAIN_BASE}\`)"
              GATEKEEPER_RULE="(Host(\`${DOMAIN_BASE}\`) || Host(\`www.${DOMAIN_BASE}\`)) && PathPrefix(\`/gatekeeper\`) || Host(\`gatekeeper.${DOMAIN_BASE}\`)"
            else
              TRAEFIK_RULE="Host(\`${TRAEFIK_HOST}\`)"
              GATEKEEPER_RULE="(Host(\`${TRAEFIK_HOST}\`) && PathPrefix(\`/gatekeeper\`)) || Host(\`gatekeeper.${TRAEFIK_HOST}\`)"
            fi
          fi

          echo "Target determined: $TARGET"
          echo "Image tag: $IMAGE_TAG"
          
          echo "target=$TARGET" >> "$GITHUB_OUTPUT"
          echo "image_tag=$IMAGE_TAG" >> "$GITHUB_OUTPUT"
          echo "env_file=$ENV_FILE" >> "$GITHUB_OUTPUT"
          echo "traefik_host=$TRAEFIK_HOST" >> "$GITHUB_OUTPUT"
          echo "traefik_rule=$TRAEFIK_RULE" >> "$GITHUB_OUTPUT"
          echo "gatekeeper_rule=$GATEKEEPER_RULE" >> "$GITHUB_OUTPUT"
          echo "gatekeeper_host=$GATEKEEPER_HOST" >> "$GITHUB_OUTPUT"
          echo "next_public_base_url=$NEXT_PUBLIC_BASE_URL" >> "$GITHUB_OUTPUT"
          echo "directus_url=$DIRECTUS_URL" >> "$GITHUB_OUTPUT"
          echo "directus_host=$DIRECTUS_HOST" >> "$GITHUB_OUTPUT"
          echo "project_name=$PRJ_ID-$TARGET" >> "$GITHUB_OUTPUT"

  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
        shell: bash
        run: |
          corepack enable
          pnpm install --frozen-lockfile
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
      - name: 🧪 Lint
        shell: bash
        run: pnpm lint
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
      - name: 🏗️ Build Test
        shell: bash
        run: pnpm build
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
          NEXT_PUBLIC_BASE_URL: https://dummy.test

  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
        run: echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
      
      - name: 🏗️ Build and Push
        shell: bash
        run: |
          docker buildx build \
            --pull \
            --platform linux/arm64 \
            --build-arg NPM_TOKEN=${{ secrets.NPM_TOKEN }} \
            --build-arg NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }} \
            --build-arg NEXT_PUBLIC_TARGET=${{ needs.prepare.outputs.target }} \
            --build-arg DIRECTUS_URL=${{ needs.prepare.outputs.directus_url }} \
            --build-arg UMAMI_API_ENDPOINT=${{ secrets.UMAMI_API_ENDPOINT || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || vars.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me' }} \
            -t registry.infra.mintel.me/mintel/mintel.me:${{ needs.prepare.outputs.image_tag }} \
            --push .

  deploy:
    name: 🚀 Deploy
    needs: [prepare, build, qa]
    if: needs.prepare.outputs.target != 'skip'
    runs-on: docker
    container:
      image: catthehacker/ubuntu:act-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: 🚀 Deploy via SSH
        shell: bash
        run: |
          echo "Deploying to alpha.mintel.me"
          
          mkdir -p ~/.ssh
          echo "${{ secrets.ALPHA_SSH_KEY }}" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H alpha.mintel.me >> ~/.ssh/known_hosts 2>/dev/null

          # Generate Environment File
          cat > .env.deploy << 'EOF'
          ENV_FILE=${{ needs.prepare.outputs.env_file }}
          IMAGE_TAG=${{ needs.prepare.outputs.image_tag }}
          TRAEFIK_HOST=${{ needs.prepare.outputs.traefik_host }}
          TRAEFIK_RULE=${{ needs.prepare.outputs.traefik_rule }}
          GATEKEEPER_RULE=${{ needs.prepare.outputs.gatekeeper_rule }}
          GATEKEEPER_HOST=${{ needs.prepare.outputs.gatekeeper_host }}
          PROJECT_NAME=${{ needs.prepare.outputs.project_name }}
          NEXT_PUBLIC_BASE_URL=${{ needs.prepare.outputs.next_public_base_url }}
          
          # Directus
          DIRECTUS_URL=${{ needs.prepare.outputs.directus_url }}
          DIRECTUS_HOST=${{ needs.prepare.outputs.directus_host }}
          INTERNAL_DIRECTUS_URL=http://directus:8055
          DIRECTUS_API_TOKEN=${{ secrets.DIRECTUS_API_TOKEN || vars.DIRECTUS_API_TOKEN }}
          DIRECTUS_ADMIN_EMAIL=${{ secrets.DIRECTUS_ADMIN_EMAIL || vars.DIRECTUS_ADMIN_EMAIL || 'admin@mintel.me' }}
          DIRECTUS_ADMIN_PASSWORD=${{ secrets.DIRECTUS_ADMIN_PASSWORD || vars.DIRECTUS_ADMIN_PASSWORD }}
          DIRECTUS_DB_NAME=${{ secrets.DIRECTUS_DB_NAME || vars.DIRECTUS_DB_NAME || 'directus' }}
          DIRECTUS_DB_USER=${{ secrets.DIRECTUS_DB_USER || vars.DIRECTUS_DB_USER || 'directus' }}
          DIRECTUS_DB_PASSWORD=${{ secrets.DIRECTUS_DB_PASSWORD || vars.DIRECTUS_DB_PASSWORD }}
          DIRECTUS_KEY=${{ secrets.DIRECTUS_KEY || vars.DIRECTUS_KEY }}
          DIRECTUS_SECRET=${{ secrets.DIRECTUS_SECRET || vars.DIRECTUS_SECRET }}
          
          # SMTP Config
          SMTP_HOST=${{ secrets.SMTP_HOST || vars.SMTP_HOST }}
          SMTP_PORT=${{ secrets.SMTP_PORT || vars.SMTP_PORT || '587' }}
          SMTP_SECURE=${{ secrets.SMTP_SECURE || vars.SMTP_SECURE || 'false' }}
          SMTP_USER=${{ secrets.SMTP_USER || vars.SMTP_USER }}
          SMTP_PASS=${{ secrets.SMTP_PASS || vars.SMTP_PASS }}
          SMTP_FROM=${{ secrets.SMTP_FROM || vars.SMTP_FROM }}
          CONTACT_RECIPIENT=${{ secrets.CONTACT_RECIPIENT || vars.CONTACT_RECIPIENT }}

          # Authentication
          GATEKEEPER_PASSWORD=${{ secrets.GATEKEEPER_PASSWORD || vars.GATEKEEPER_PASSWORD }}
          AUTH_COOKIE_NAME=${{ secrets.AUTH_COOKIE_NAME || vars.AUTH_COOKIE_NAME || 'mintel_gatekeeper_session' }}
          COOKIE_DOMAIN=${{ secrets.COOKIE_DOMAIN || vars.COOKIE_DOMAIN || '.mintel.me' }}
          AUTH_MIDDLEWARE=$( [[ "${{ needs.prepare.outputs.target }}" == "production" ]] && echo "compress" || echo "${{ needs.prepare.outputs.project_name }}-auth,compress" )

          # External Services
          SENTRY_DSN=${{ secrets.SENTRY_DSN || vars.SENTRY_DSN }}
          GOTIFY_URL=${{ secrets.GOTIFY_URL || vars.GOTIFY_URL }}
          GOTIFY_TOKEN=${{ secrets.GOTIFY_TOKEN || vars.GOTIFY_TOKEN }}
          UMAMI_WEBSITE_ID=${{ secrets.UMAMI_WEBSITE_ID || secrets.NEXT_PUBLIC_UMAMI_WEBSITE_ID || vars.UMAMI_WEBSITE_ID || vars.NEXT_PUBLIC_UMAMI_WEBSITE_ID }}
          UMAMI_API_ENDPOINT=${{ secrets.UMAMI_API_ENDPOINT || secrets.NEXT_PUBLIC_UMAMI_SCRIPT_URL || vars.UMAMI_API_ENDPOINT || 'https://analytics.infra.mintel.me' }}
          
          # Project
          PROJECT_COLOR=${{ secrets.PROJECT_COLOR || vars.PROJECT_COLOR || '#ff00ff' }}
          EOF

          APP_DIR="/home/deploy/sites/mintel.me"
          ssh -o StrictHostKeyChecking=accept-new root@alpha.mintel.me "mkdir -p $APP_DIR"
          
          scp -o StrictHostKeyChecking=accept-new .env.deploy root@alpha.mintel.me:$APP_DIR/${{ needs.prepare.outputs.env_file }}
          scp -o StrictHostKeyChecking=accept-new docker-compose.yaml root@alpha.mintel.me:$APP_DIR/docker-compose.yaml
          
          ssh -o StrictHostKeyChecking=accept-new root@alpha.mintel.me bash << 'EOF'
            set -e
            APP_DIR="/home/deploy/sites/mintel.me"
            cd $APP_DIR
            
            echo "${{ secrets.REGISTRY_PASS }}" | docker login registry.infra.mintel.me -u "${{ secrets.REGISTRY_USER }}" --password-stdin
            docker compose -p "${{ needs.prepare.outputs.project_name }}" --env-file ${{ needs.prepare.outputs.env_file }} pull
            docker compose -p "${{ needs.prepare.outputs.project_name }}" --env-file ${{ needs.prepare.outputs.env_file }} up -d --remove-orphans
            docker system prune -f --filter "until=24h"
          EOF

  pagespeed:
    name: ⚡ PageSpeed
    needs: [prepare, deploy]
    if: |
      always() && 
      needs.prepare.outputs.target != 'skip' &&
      needs.deploy.result == 'success'
    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: |
          corepack enable
          pnpm install --frozen-lockfile
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
      - name: 🧪 Run PageSpeed (Lighthouse CI)
        env:
          NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_base_url }}
          GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD }}
        run: pnpm --filter @mintel/web pagespeed:test

  notifications:
    name: 🔔 Notifications
    needs: [prepare, deploy]
    if: always()
    runs-on: docker
    container:
      image: catthehacker/ubuntu:act-latest
    steps:
      - name: Notify Gotify
        shell: bash
        run: |
          STATUS="${{ needs.deploy.result }}"
          [[ "$STATUS" == "success" ]] && PRIORITY=5 || PRIORITY=8
          
          curl -X POST "${{ secrets.GOTIFY_URL }}/message?token=${{ secrets.GOTIFY_TOKEN }}" \
            -F "title=mintel.me Deployment" \
            -F "message=Status: $STATUS for ${{ needs.prepare.outputs.target }} (${{ needs.prepare.outputs.image_tag }})" \
            -F "priority=$PRIORITY"
