name: Integration Tests

permissions:
  contents: read

concurrency:
  group: integration-tests-${{ github.ref }}
  cancel-in-progress: true

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main, develop ]

jobs:
  integration-tests:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        keycloak_db: [postgres, dev-file]
    env:
      KEYCLOAK_VERSION: "26.4.2"

    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: meldestelle
          POSTGRES_PASSWORD: meldestelle
          POSTGRES_DB: meldestelle
        ports:
          - 5432:5432
        options: >-
          --health-cmd "pg_isready -U meldestelle -d $${POSTGRES_DB}"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 12
          --health-start-period 20s

      redis:
        image: redis:7-alpine
        ports:
          - 6379:6379
        options: >-
          --health-cmd "redis-cli ping"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5


      zookeeper:
        image: confluentinc/cp-zookeeper:7.5.0
        env:
          ZOOKEEPER_CLIENT_PORT: 2181
        ports:
          - 2181:2181
        options: >-
          --health-cmd "nc -z localhost 2181 || exit 1"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 3
          --health-start-period 10s

      kafka:
        image: confluentinc/cp-kafka:7.5.0
        env:
          KAFKA_BROKER_ID: 1
          KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
          KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
          KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
          KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
          KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
        ports:
          - 9092:9092
        options: >-
          --health-cmd "kafka-topics --bootstrap-server localhost:9092 --list || exit 1"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 3
          --health-start-period 30s

      zipkin:
        image: openzipkin/zipkin:2
        ports:
          - 9411:9411
        options: >-
          --health-cmd "wget -q -O - http://localhost:9411/health || exit 1"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 3
          --health-start-period 10s

    steps:
      - uses: actions/checkout@v6

      - name: Set up JDK 21
        uses: actions/setup-java@v5
        with:
          java-version: 21
          distribution: 'temurin'
          cache: 'gradle'

      - name: Setup Gradle (modern)
        uses: gradle/actions/setup-gradle@v5

      - name: Wait for Postgres to be ready (pg_isready in service network)
        if: ${{ matrix.keycloak_db == 'postgres' }}
        run: |
          echo "Waiting for Postgres..."
          for i in {1..40}; do
            if docker run --rm --network ${{ job.services.postgres.network }} \
              postgres:16-alpine pg_isready -h postgres -p 5432 -U meldestelle -d meldestelle; then
              echo "Postgres is ready"; break; fi; echo -n "."; sleep 3; done

      - name: Start Keycloak with Postgres (dev) and wait for readiness
        if: ${{ matrix.keycloak_db == 'postgres' }}
        run: |
          set -euo pipefail
          echo "Starting Keycloak (DB=postgres)..."
          docker run -d --name keycloak \
            --network ${{ job.services.postgres.network }} \
            -p 8180:8080 \
            -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
            -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
            -e KC_DB=postgres \
            -e KC_DB_URL=jdbc:postgresql://postgres:5432/meldestelle \
            -e KC_DB_USERNAME=meldestelle \
            -e KC_DB_PASSWORD=meldestelle \
            -e KC_HEALTH_ENABLED=true \
            -e JAVA_OPTS="-Xms256m -Xmx1024m -XX:MaxRAMPercentage=60" \
            quay.io/keycloak/keycloak:${{ env.KEYCLOAK_VERSION }} \
            start-dev

          echo "Giving Keycloak 30s to initialize..."; sleep 30

          wait_for() {
            local url="$1"; local label="$2"; local timeout="${3:-180}"; local interval="${4:-5}"
            echo "Waiting for $label ($url) ..."
            if ! timeout ${timeout}s bash -c 'until curl -fsS --output /dev/null '"$url"'; do echo -n "."; sleep '"$interval"'; done'; then
              echo "\n[WAIT] Timeout on $url"
              return 1
            fi
            echo "\n[WAIT] $label is up"
          }

          if ! wait_for http://localhost:8180/ "root" 180 5; then
            docker logs --tail=200 keycloak || true
            exit 1
          fi
          if ! wait_for http://localhost:8180/health "health" 180 5; then
            echo "[INFO] /health nicht erreichbar – versuche /q/health (Quarkus default)"
            wait_for http://localhost:8180/q/health "q-health" 180 5 || true
          fi
          wait_for http://localhost:8180/health/ready "health-ready" 300 5 || true
          wait_for http://localhost:8180/admin/master/console/ "admin-console" 300 5 || (docker logs --tail=400 keycloak && exit 1)

      - name: Start Keycloak with dev-file (no Postgres) and wait for readiness
        if: ${{ matrix.keycloak_db == 'dev-file' }}
        run: |
          set -euo pipefail
          echo "Starting Keycloak (DB=dev-file, no Postgres)..."
          docker run -d --name keycloak \
            -p 8180:8080 \
            -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
            -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
            -e KC_DB=dev-file \
            -e KC_HEALTH_ENABLED=true \
            -e JAVA_OPTS="-Xms256m -Xmx1024m -XX:MaxRAMPercentage=60" \
            quay.io/keycloak/keycloak:${{ env.KEYCLOAK_VERSION }} \
            start-dev

          echo "Giving Keycloak 20s to initialize..."; sleep 20

          wait_for() {
            local url="$1"; local label="$2"; local timeout="${3:-180}"; local interval="${4:-5}"
            echo "Waiting for $label ($url) ..."
            if ! timeout ${timeout}s bash -c 'until curl -fsS --output /dev/null '"$url"'; do echo -n "."; sleep '"$interval"'; done'; then
              echo "\n[WAIT] Timeout on $url"
              return 1
            fi
            echo "\n[WAIT] $label is up"
          }

          if ! wait_for http://localhost:8180/ "root" 180 5; then
            docker logs --tail=200 keycloak || true
            exit 1
          fi
          if ! wait_for http://localhost:8180/health "health" 180 5; then
            echo "[INFO] /health nicht erreichbar – versuche /q/health (Quarkus default)"
            wait_for http://localhost:8180/q/health "q-health" 180 5 || true
          fi
          wait_for http://localhost:8180/health/ready "health-ready" 300 5 || true
          wait_for http://localhost:8180/admin/master/console/ "admin-console" 300 5 || (docker logs --tail=400 keycloak && exit 1)

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Static Analysis
        run: ./gradlew staticAnalysis --no-daemon

      - name: Run integration tests
        run: ./gradlew integrationTest --no-daemon --parallel
        env:
          # Environment variables for Redis connection
          REDIS_HOST: localhost
          REDIS_PORT: 6379
          # Keycloak base URL for integration tests (manual container)
          KEYCLOAK_AUTH_SERVER_URL: http://localhost:8180
          # Spring profile for integration tests
          SPRING_PROFILES_ACTIVE: integration-test

      - name: Upload test reports
        uses: actions/upload-artifact@v5
        if: always()
        with:
          name: integration-test-reports
          path: |
            **/build/reports/tests/integrationTest/
            **/build/test-results/integrationTest/
          retention-days: 7

      - name: Dump service logs (Keycloak, Postgres)
        if: always()
        run: |
          echo "=== Docker ps ===" && docker ps -a || true
          echo "=== Keycloak logs (tail) ===" && docker logs --tail=400 keycloak || true
          echo "=== Postgres logs (tail) ===" && docker logs --tail=200 $(docker ps -a --filter "name=postgres" --format "{{.ID}}") || true
