name: CI

on:
  push:
    branches:
      - "**"
  pull_request:
    branches:
      - main

env:
  # registry + cosign creds via env, NOT inline ${{ }}: the Harbor robot
  # username contains '$', which sh expands when interpolated into the
  # script (robot$ci-push -> robot-push) => docker login unauthorized.
  REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
  REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
  COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
  COSIGN_PASSWORD: ${{ secrets.COSIGN_PASSWORD }}
  CARGO_TERM_COLOR: always
  RUSTFLAGS: "-D warnings"
  # sccache caches compilation artifacts within a job so that compiling
  # both --features server and --features web shares common crate work.
  RUSTC_WRAPPER: /usr/local/bin/sccache
  SCCACHE_DIR: /tmp/sccache

# Cancel in-progress runs for the same branch/PR
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  # ---------------------------------------------------------------------------
  # Stage 1: Code quality checks (run in parallel)
  # ---------------------------------------------------------------------------
  fmt:
    name: Format
    runs-on: docker
    container:
      image: rust:1.89-bookworm
    steps:
      - name: Checkout
        run: |
          git init
          git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
          git fetch --depth=1 origin "${GITHUB_SHA}"
          git checkout FETCH_HEAD
      - run: rustup component add rustfmt
      # Format check does not compile, so sccache is not needed here.
      - run: cargo fmt --check
        env:
          RUSTC_WRAPPER: ""

  clippy:
    name: Clippy
    runs-on: docker
    container:
      image: rust:1.89-bookworm
    steps:
      - name: Checkout
        run: |
          git init
          git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
          git fetch --depth=1 origin "${GITHUB_SHA}"
          git checkout FETCH_HEAD
      - name: Install sccache
        run: |
          curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \
            | tar xz --strip-components=1 -C /usr/local/bin/ sccache-v0.9.1-x86_64-unknown-linux-musl/sccache
          chmod +x /usr/local/bin/sccache
      - run: rustup component add clippy
      # Lint both feature sets independently.
      # sccache deduplicates shared crates between the two compilations.
      - name: Clippy (server)
        run: cargo clippy --features server --no-default-features -- -D warnings
      - name: Clippy (web)
        run: cargo clippy --features web --no-default-features -- -D warnings
      - name: Show sccache stats
        run: sccache --show-stats
        if: always()

  audit:
    name: Security Audit
    runs-on: docker
    if: github.ref == 'refs/heads/main'
    container:
      image: rust:1.89-bookworm
    steps:
      - name: Checkout
        run: |
          git init
          git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
          git fetch --depth=1 origin "${GITHUB_SHA}"
          git checkout FETCH_HEAD
      - run: cargo install cargo-audit
        env:
          RUSTC_WRAPPER: ""
      - run: cargo audit
        env:
          RUSTC_WRAPPER: ""

  # ---------------------------------------------------------------------------
  # Stage 2: Tests (only after all quality checks pass)
  # ---------------------------------------------------------------------------
  test:
    name: Tests
    runs-on: docker
    needs: [fmt, clippy, audit]
    container:
      image: rust:1.89-bookworm
    steps:
      - name: Checkout
        run: |
          git init
          git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
          git fetch --depth=1 origin "${GITHUB_SHA}"
          git checkout FETCH_HEAD
      - name: Install sccache
        run: |
          curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \
            | tar xz --strip-components=1 -C /usr/local/bin/ sccache-v0.9.1-x86_64-unknown-linux-musl/sccache
          chmod +x /usr/local/bin/sccache
      - name: Run tests (server)
        run: cargo test --features server --no-default-features
      - name: Run tests (web)
        run: cargo test --features web --no-default-features
      - name: Show sccache stats
        run: sccache --show-stats
        if: always()

  # ---------------------------------------------------------------------------
  # Stage 4: E2E tests (only on main, after deploy)
  # ---------------------------------------------------------------------------
  e2e:
    name: E2E Tests
    runs-on: docker
    needs: [deploy]
    if: github.ref == 'refs/heads/main'
    container:
      image: rust:1.89-bookworm
    # MongoDB and SearXNG can start immediately (no repo files needed).
    # Keycloak requires realm-export.json from the repo, so it is started
    # manually after checkout via docker CLI.
    services:
      mongo:
        image: mongo:latest
        env:
          MONGO_INITDB_ROOT_USERNAME: root
          MONGO_INITDB_ROOT_PASSWORD: example
        ports:
          - 27017:27017
      searxng:
        image: searxng/searxng:latest
        env:
          SEARXNG_BASE_URL: http://localhost:8888
        ports:
          - 8888:8080
    env:
      KEYCLOAK_URL: http://localhost:8080
      KEYCLOAK_REALM: certifai
      KEYCLOAK_CLIENT_ID: certifai-dashboard
      MONGODB_URI: mongodb://root:example@mongo:27017
      MONGODB_DATABASE: certifai
      SEARXNG_URL: http://searxng:8080
      LANGGRAPH_URL: ""
      LANGFLOW_URL: ""
      LANGFUSE_URL: ""
    steps:
      - name: Checkout
        run: |
          git init
          git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
          git fetch --depth=1 origin "${GITHUB_SHA}"
          git checkout FETCH_HEAD
      - name: Install system dependencies
        run: |
          apt-get update -qq
          apt-get install -y -qq --no-install-recommends \
            unzip curl docker.io \
            libglib2.0-0 libnss3 libnspr4 libdbus-1-3 libatk1.0-0 \
            libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 \
            libxdamage1 libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 \
            libcairo2 libasound2 libatspi2.0-0 libxshmfence1
      - name: Start Keycloak
        run: |
          docker run -d --name ci-keycloak --network host \
            -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
            -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
            -e KC_DB=dev-mem \
            -e KC_HEALTH_ENABLED=true \
            -v "$PWD/keycloak/realm-export.json:/opt/keycloak/data/import/realm-export.json:ro" \
            -v "$PWD/keycloak/themes/certifai:/opt/keycloak/themes/certifai:ro" \
            quay.io/keycloak/keycloak:26.0 start-dev --import-realm

          echo "Waiting for Keycloak..."
          for i in $(seq 1 60); do
            if curl -sf http://localhost:8080/realms/certifai > /dev/null 2>&1; then
              echo "Keycloak is ready"
              break
            fi
            if [ "$i" -eq 60 ]; then
              echo "Keycloak failed to start within 60s"
              docker logs ci-keycloak
              exit 1
            fi
            sleep 2
          done
      - name: Install sccache
        run: |
          curl -fsSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \
            | tar xz --strip-components=1 -C /usr/local/bin/ sccache-v0.9.1-x86_64-unknown-linux-musl/sccache
          chmod +x /usr/local/bin/sccache
      - name: Install dioxus-cli
        run: cargo install dioxus-cli --locked
      - name: Install bun
        run: |
          curl -fsSL https://bun.sh/install | bash
          echo "$HOME/.bun/bin" >> "$GITHUB_PATH"
      - name: Install Playwright
        run: |
          export PATH="$HOME/.bun/bin:$PATH"
          bun install
          bunx playwright install chromium
      - name: Build app
        run: dx build --release
      - name: Start app and run E2E tests
        run: |
          export PATH="$HOME/.bun/bin:$PATH"
          # Start the app in the background
          dx serve --release --port 8000 &
          APP_PID=$!

          # Wait for the app to be ready
          echo "Waiting for app to start..."
          for i in $(seq 1 60); do
            if curl -sf http://localhost:8000 > /dev/null 2>&1; then
              echo "App is ready"
              break
            fi
            if [ "$i" -eq 60 ]; then
              echo "App failed to start within 60s"
              exit 1
            fi
            sleep 1
          done

          BASE_URL=http://localhost:8000 bunx playwright test --reporter=list

          kill "$APP_PID" 2>/dev/null || true
      - name: Upload test report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 7
      - name: Cleanup Keycloak
        if: always()
        run: docker rm -f ci-keycloak 2>/dev/null || true
      - name: Show sccache stats
        run: sccache --show-stats
        if: always()

  # ---------------------------------------------------------------------------
  # Stage 3: Deploy (only after tests pass, only on main)
  # ---------------------------------------------------------------------------
  deploy:
    name: Deploy
    runs-on: docker
    needs: [test]
    if: github.ref == 'refs/heads/main'
    container:
      image: docker:27-cli
    steps:
      - name: Checkout
        run: |
          apk add --no-cache git curl openssl
          git init
          git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
          git fetch --depth=1 origin "${GITHUB_SHA}"
          git checkout FETCH_HEAD
      - name: Build and push image
        run: |
          IMAGE=repo.breakpilot.com/certifai/certifai-dashboard
          echo "$REGISTRY_PASSWORD" | \
            docker login repo.breakpilot.com -u "$REGISTRY_USERNAME" --password-stdin
          docker build -t "$IMAGE:latest" -t "$IMAGE:${GITHUB_SHA}" .
          docker push "$IMAGE:latest"
          { command -v cosign >/dev/null 2>&1 || curl -sSfLo /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64 || wget -qO /usr/local/bin/cosign https://github.com/sigstore/cosign/releases/download/v2.4.3/cosign-linux-amd64; } || echo "::warning::cosign fetch failed"
          chmod +x /usr/local/bin/cosign 2>/dev/null || true
          cosign sign --yes --key env://COSIGN_KEY "$IMAGE:latest" || echo "::warning::cosign failed"
          docker push "$IMAGE:${GITHUB_SHA}"
      - name: Trigger orca redeploy
        run: |
          PAYLOAD=$(printf '{"ref":"refs/heads/main","repository":{"full_name":"sharang/certifai"},"head_commit":{"id":"%s","message":"CI deploy"}}' "${GITHUB_SHA}")
          SIG=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "${{ secrets.ORCA_WEBHOOK_SECRET }}" | awk '{print $2}')
          echo "Calling orca webhook for sharang/certifai@${GITHUB_SHA}"
          RESP=$(curl -fsS -w "\nHTTP %{http_code}" -X POST "http://46.225.100.82:6880/api/v1/webhooks/github" \
            -H "Content-Type: application/json" \
            -H "X-Hub-Signature-256: sha256=$SIG" \
            -d "$PAYLOAD")
          echo "$RESP"
