services:
  # ============================================
  # Nginx HTTPS Reverse Proxy
  # Enables secure context for microphone/crypto
  # Access via HTTPS on same ports as HTTP was before
  # ============================================
  nginx:
    image: nginx:alpine
    container_name: breakpilot-pwa-nginx
    ports:
      - "443:443"      # HTTPS Studio v2 (https://macmini/)
      - "80:80"        # HTTP -> HTTPS redirect
      - "3000:3000"    # HTTPS Admin Website (https://macmini:3000/)
      - "3002:3002"    # HTTPS Admin v2 (https://macmini:3002/)
      - "8091:8091"    # HTTPS Voice Service (wss://macmini:8091/)
      - "8000:8000"    # HTTPS Backend API
      - "8086:8086"    # HTTPS Klausur Service
      - "8089:8089"    # HTTPS Edu-Search proxy (edu-search runs on 8088)
      - "8093:8093"    # HTTPS AI Compliance SDK
      - "8443:8443"    # HTTPS Jitsi Meet (https://macmini:8443/)
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - vault_certs:/etc/nginx/certs:ro
    depends_on:
      vault-agent:
        condition: service_started
      studio-v2:
        condition: service_started
      voice-service:
        condition: service_started
      backend:
        condition: service_started
      klausur-service:
        condition: service_started
      website:
        condition: service_started
      ai-compliance-sdk:
        condition: service_started
      admin-v2:
        condition: service_started
      jitsi-web:
        condition: service_started
    extra_hosts:
      - "breakpilot-edu-search:host-gateway"
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # ============================================
  # HashiCorp Vault - Secrets Management
  # Web UI: http://localhost:8200/ui
  # ============================================
  vault:
    image: hashicorp/vault:1.15
    container_name: breakpilot-pwa-vault
    cap_add:
      - IPC_LOCK
    ports:
      - "8200:8200"
    environment:
      - VAULT_DEV_ROOT_TOKEN_ID=${VAULT_DEV_TOKEN:-breakpilot-dev-token}
      - VAULT_DEV_LISTEN_ADDRESS=0.0.0.0:8200
      - VAULT_ADDR=http://127.0.0.1:8200
    volumes:
      - vault_data:/vault/data
    healthcheck:
      test: ["CMD", "vault", "status"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 5s
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Vault PKI Initialization - runs once to set up PKI and issue initial certs
  vault-init:
    image: hashicorp/vault:1.15
    container_name: breakpilot-pwa-vault-init
    environment:
      - VAULT_ADDR=http://vault:8200
      - VAULT_TOKEN=${VAULT_DEV_TOKEN:-breakpilot-dev-token}
    volumes:
      - ./vault/init-pki.sh:/init-pki.sh:ro
      - vault_agent_config:/vault/agent/data
      - vault_certs:/vault/certs
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        echo "Waiting for Vault to be ready..."
        until vault status > /dev/null 2>&1; do sleep 1; done
        echo "Vault is ready. Running PKI initialization..."
        chmod +x /init-pki.sh
        /init-pki.sh
        echo "PKI initialization complete. Exiting."
    depends_on:
      vault:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    restart: "no"

  # Vault Agent - manages certificate renewal for nginx
  vault-agent:
    image: hashicorp/vault:1.15
    container_name: breakpilot-pwa-vault-agent
    environment:
      - VAULT_ADDR=http://vault:8200
    volumes:
      - ./vault/agent/config.hcl:/vault/agent/config.hcl:ro
      - ./vault/agent/templates:/vault/agent/templates:ro
      - ./vault/agent/split-certs.sh:/vault/agent/split-certs.sh:ro
      - vault_agent_config:/vault/agent/data
      - vault_certs:/vault/certs
    entrypoint: ["vault", "agent", "-config=/vault/agent/config.hcl"]
    depends_on:
      vault:
        condition: service_healthy
      vault-init:
        condition: service_completed_successfully
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # PostgreSQL Database with PostGIS Extension
  # PostGIS is required for geo-service (OSM/Terrain features)
  postgres:
    image: postgis/postgis:16-3.4-alpine
    container_name: breakpilot-pwa-postgres
    ports:
      - "5432:5432"
    environment:
      POSTGRES_USER: breakpilot
      POSTGRES_PASSWORD: breakpilot123
      POSTGRES_DB: breakpilot_db
    volumes:
      - breakpilot_pwa_data:/var/lib/postgresql/data
      - ./geo-service/scripts/init_postgis.sql:/docker-entrypoint-initdb.d/10-init-postgis.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U breakpilot -d breakpilot_db"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # ============================================
  # Valkey - Session Cache (Redis-Fork, BSD-3)
  # Redis-compatible, 100% Open Source
  # ============================================
  valkey:
    image: valkey/valkey:8-alpine
    container_name: breakpilot-pwa-valkey
    ports:
      - "6379:6379"
    volumes:
      - valkey_data:/data
    command: valkey-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
    healthcheck:
      test: ["CMD", "valkey-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Go Consent Service
  consent-service:
    build:
      context: ./consent-service
      dockerfile: Dockerfile
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-consent-service
    ports:
      - "8081:8081"
    environment:
      - DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
      - JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
      - JWT_REFRESH_SECRET=${JWT_REFRESH_SECRET:-your-refresh-secret-key-change-in-production}
      - PORT=8081
      - ENVIRONMENT=${ENVIRONMENT:-development}
      - ALLOWED_ORIGINS=http://localhost:8000,http://backend:8000
      # Valkey Session Cache
      - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379}
      - SESSION_TTL_HOURS=${SESSION_TTL_HOURS:-24}
      # E-Mail Konfiguration (Mailpit für Entwicklung)
      - SMTP_HOST=${SMTP_HOST:-mailpit}
      - SMTP_PORT=${SMTP_PORT:-1025}
      - SMTP_USERNAME=${SMTP_USERNAME:-}
      - SMTP_PASSWORD=${SMTP_PASSWORD:-}
      - SMTP_FROM_NAME=${SMTP_FROM_NAME:-BreakPilot}
      - SMTP_FROM_ADDR=${SMTP_FROM_ADDR:-noreply@breakpilot.app}
      - FRONTEND_URL=${FRONTEND_URL:-http://localhost:8000}
    depends_on:
      postgres:
        condition: service_healthy
      valkey:
        condition: service_healthy
      mailpit:
        condition: service_started
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Python Backend
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-backend
    expose:
      - "8000"
    environment:
      - CONSENT_SERVICE_URL=http://consent-service:8081
      - KLAUSUR_SERVICE_URL=http://klausur-service:8086
      - TROCR_SERVICE_URL=${TROCR_SERVICE_URL:-http://host.docker.internal:18084}
      - CAMUNDA_URL=http://camunda:8080/engine-rest
      - DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
      - JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
      - ENVIRONMENT=${ENVIRONMENT:-development}
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
      - DEBUG=${DEBUG:-false}
      # Alerts Agent (Google Alerts Monitoring)
      - ALERTS_AGENT_ENABLED=${ALERTS_AGENT_ENABLED:-true}
      # HashiCorp Vault - Secrets Management
      - VAULT_ADDR=${VAULT_ADDR:-http://vault:8200}
      - VAULT_TOKEN=${VAULT_TOKEN:-breakpilot-dev-token}
      - VAULT_SECRETS_PATH=${VAULT_SECRETS_PATH:-breakpilot}
      - USE_VAULT_SECRETS=${USE_VAULT_SECRETS:-true}
      # Keycloak Authentication (optional - wenn nicht gesetzt wird lokales JWT verwendet)
      - KEYCLOAK_SERVER_URL=${KEYCLOAK_SERVER_URL:-}
      - KEYCLOAK_REALM=${KEYCLOAK_REALM:-}
      - KEYCLOAK_CLIENT_ID=${KEYCLOAK_CLIENT_ID:-}
      - KEYCLOAK_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET:-}
      - KEYCLOAK_VERIFY_SSL=${KEYCLOAK_VERIFY_SSL:-true}
      # Valkey Session Cache
      - VALKEY_URL=${VALKEY_URL:-redis://valkey:6379}
      - SESSION_TTL_HOURS=${SESSION_TTL_HOURS:-24}
      # vast.ai GPU Infrastructure
      - VAST_API_KEY=${VAST_API_KEY:-}
      - VAST_INSTANCE_ID=${VAST_INSTANCE_ID:-}
      - CONTROL_API_KEY=${CONTROL_API_KEY:-}
      - VAST_AUTO_SHUTDOWN=${VAST_AUTO_SHUTDOWN:-true}
      - VAST_AUTO_SHUTDOWN_MINUTES=${VAST_AUTO_SHUTDOWN_MINUTES:-30}
      # vLLM Backend
      - VLLM_BASE_URL=${VLLM_BASE_URL:-}
      - VLLM_ENABLED=${VLLM_ENABLED:-false}
      # Ollama Backend (lokal auf Mac Mini - DSGVO-konform)
      - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
      - OLLAMA_ENABLED=${OLLAMA_ENABLED:-true}
      - OLLAMA_DEFAULT_MODEL=${OLLAMA_DEFAULT_MODEL:-qwen2.5:14b}
      - OLLAMA_VISION_MODEL=${OLLAMA_VISION_MODEL:-qwen2.5vl:32b}
      - OLLAMA_CORRECTION_MODEL=${OLLAMA_CORRECTION_MODEL:-qwen2.5:14b}
      - OLLAMA_TIMEOUT=${OLLAMA_TIMEOUT:-180}
      # Breakpilot Drive Game API
      - GAME_USE_DATABASE=${GAME_USE_DATABASE:-true}
      - GAME_REQUIRE_AUTH=${GAME_REQUIRE_AUTH:-false}
      - GAME_REQUIRE_BILLING=${GAME_REQUIRE_BILLING:-false}
      - GAME_LLM_MODEL=${GAME_LLM_MODEL:-}
      # Compliance LLM Provider Configuration
      # Options: "anthropic" (cloud) or "self_hosted" (Ollama/local)
      - COMPLIANCE_LLM_PROVIDER=${COMPLIANCE_LLM_PROVIDER:-self_hosted}
      - SELF_HOSTED_LLM_URL=${SELF_HOSTED_LLM_URL:-http://host.docker.internal:11434}
      - SELF_HOSTED_LLM_MODEL=${SELF_HOSTED_LLM_MODEL:-llama3.1:70b}
      - COMPLIANCE_LLM_MAX_TOKENS=${COMPLIANCE_LLM_MAX_TOKENS:-4096}
      - COMPLIANCE_LLM_TEMPERATURE=${COMPLIANCE_LLM_TEMPERATURE:-0.3}
      - COMPLIANCE_LLM_TIMEOUT=${COMPLIANCE_LLM_TIMEOUT:-120}
      # E-Mail Konfiguration (Mailpit fuer Entwicklung)
      - SMTP_HOST=${SMTP_HOST:-mailpit}
      - SMTP_PORT=${SMTP_PORT:-1025}
      - SMTP_USERNAME=${SMTP_USERNAME:-}
      - SMTP_PASSWORD=${SMTP_PASSWORD:-}
      - SMTP_FROM_NAME=${SMTP_FROM_NAME:-BreakPilot}
      - SMTP_FROM_ADDR=${SMTP_FROM_ADDR:-noreply@breakpilot.app}
    extra_hosts:
      - "host.docker.internal:host-gateway"
      - "mac-mini:192.168.178.163"
    volumes:
      # Mount Docker socket for container monitoring (Mac Mini Control)
      - /var/run/docker.sock:/var/run/docker.sock:ro
      # Mount SBOM files for Security Dashboard
      - ./docs/sbom:/app/docs/sbom:ro
      # Mount Projekt-Verzeichnis fuer Test-Dashboard (echte Test-Discovery)
      - /Users/benjaminadmin/Projekte/breakpilot-pwa:/app/project:ro
    depends_on:
      - consent-service
      - valkey
      - mailpit
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Automatic Database Backup (runs daily at 2 AM)
  backup:
    image: postgres:16-alpine
    container_name: breakpilot-pwa-backup
    volumes:
      - ./backups:/backups
      - postgres_data:/var/lib/postgresql/data:ro
    environment:
      - PGHOST=postgres
      - PGUSER=breakpilot
      - PGPASSWORD=breakpilot123
      - PGDATABASE=breakpilot_db
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        echo "Backup service started. Running backup every day at 2 AM..."
        while true; do
          # Berechne Sekunden bis 2 Uhr
          current_hour=$$(date +%H)
          current_min=$$(date +%M)
          current_sec=$$(date +%S)

          if [ $$current_hour -lt 2 ]; then
            sleep_hours=$$((2 - current_hour - 1))
          else
            sleep_hours=$$((26 - current_hour - 1))
          fi
          sleep_mins=$$((60 - current_min - 1))
          sleep_secs=$$((60 - current_sec))
          total_sleep=$$((sleep_hours * 3600 + sleep_mins * 60 + sleep_secs))

          echo "Next backup in $$total_sleep seconds (at 2:00 AM)"
          sleep $$total_sleep

          # Backup erstellen
          TIMESTAMP=$$(date +"%Y%m%d_%H%M%S")
          BACKUP_FILE="/backups/breakpilot_$${TIMESTAMP}.sql.gz"

          echo "Creating backup: $$BACKUP_FILE"
          pg_dump | gzip > "$$BACKUP_FILE"

          if [ $$? -eq 0 ]; then
            echo "✓ Backup completed: $$BACKUP_FILE"
            # Alte Backups löschen (älter als 30 Tage)
            find /backups -name "breakpilot_*.sql.gz" -mtime +30 -delete
            echo "✓ Old backups cleaned up"
          else
            echo "✗ Backup failed!"
          fi
        done
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    profiles:
      - backup

  # Mailpit - Lokaler E-Mail-Server für Entwicklung
  # Web UI: http://localhost:8025
  # SMTP: localhost:1025
  mailpit:
    image: axllent/mailpit:latest
    container_name: breakpilot-pwa-mailpit
    ports:
      - "8025:8025"   # Web UI
      - "1025:1025"   # SMTP Server
    environment:
      - MP_SMTP_AUTH_ACCEPT_ANY=true
      - MP_SMTP_AUTH_ALLOW_INSECURE=true
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Matrix Synapse - Schulkommunikation (E2EE Messenger)
  # Admin: http://localhost:8008/_synapse/admin
  synapse:
    image: matrixdotorg/synapse:latest
    container_name: breakpilot-pwa-synapse
    ports:
      - "8008:8008"     # Client-Server API
      - "8448:8448"     # Federation (optional für später)
    volumes:
      - synapse_data:/data
    environment:
      - SYNAPSE_SERVER_NAME=${MATRIX_SERVER_NAME:-breakpilot.local}
      - SYNAPSE_REPORT_STATS=no
      - SYNAPSE_NO_TLS=1
      - SYNAPSE_ENABLE_REGISTRATION=no
      - SYNAPSE_LOG_LEVEL=INFO
      - UID=1000
      - GID=1000
    healthcheck:
      test: ["CMD-SHELL", "curl -fSs http://localhost:8008/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # PostgreSQL für Matrix Synapse (separate DB)
  synapse-db:
    image: postgres:16-alpine
    container_name: breakpilot-pwa-synapse-db
    environment:
      POSTGRES_USER: synapse
      POSTGRES_PASSWORD: ${SYNAPSE_DB_PASSWORD:-synapse_secret_123}
      POSTGRES_DB: synapse
      POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C"
    volumes:
      - synapse_db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U synapse -d synapse"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Go School Service (Klausuren, Noten, Zeugnisse)
  school-service:
    build:
      context: ./school-service
      dockerfile: Dockerfile
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-school-service
    ports:
      - "8084:8084"
    environment:
      - DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
      - JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
      - PORT=8084
      - ENVIRONMENT=${ENVIRONMENT:-development}
      - ALLOWED_ORIGINS=http://localhost:8000,http://backend:8000
      - LLM_GATEWAY_URL=http://backend:8000/llm
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Embedding Service (ML-heavy operations)
  # Handles: embeddings, re-ranking, PDF extraction
  # Separated for faster klausur-service builds
  embedding-service:
    build:
      context: ./klausur-service/embedding-service
      dockerfile: Dockerfile
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-embedding-service
    ports:
      - "8087:8087"
    environment:
      - EMBEDDING_BACKEND=${EMBEDDING_BACKEND:-local}
      - LOCAL_EMBEDDING_MODEL=${LOCAL_EMBEDDING_MODEL:-BAAI/bge-m3}
      - LOCAL_RERANKER_MODEL=${LOCAL_RERANKER_MODEL:-BAAI/bge-reranker-v2-m3}
      - PDF_EXTRACTION_BACKEND=${PDF_EXTRACTION_BACKEND:-auto}
      - OPENAI_API_KEY=${OPENAI_API_KEY:-}
      - COHERE_API_KEY=${COHERE_API_KEY:-}
      - LOG_LEVEL=${LOG_LEVEL:-INFO}
    volumes:
      - embedding_models:/root/.cache/huggingface
    deploy:
      resources:
        limits:
          memory: 4G
    healthcheck:
      test: ["CMD", "python", "-c", "import httpx; httpx.get('http://localhost:8087/health').raise_for_status()"]
      interval: 30s
      timeout: 10s
      start_period: 120s
      retries: 3
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Klausur-Service (Abitur/Vorabitur Klausurkorrektur)
  # React + FastAPI Microservice
  # Web UI: http://localhost:8086
  klausur-service:
    build:
      context: ./klausur-service
      dockerfile: Dockerfile
    platform: linux/arm64  # Native ARM64 - PaddlePaddle 3.3.0 unterstützt ARM64
    container_name: breakpilot-pwa-klausur-service
    expose:
      - "8086"
    environment:
      - JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
      - BACKEND_URL=http://backend:8000
      - SCHOOL_SERVICE_URL=http://school-service:8084
      - ENVIRONMENT=${ENVIRONMENT:-development}
      # PostgreSQL for OCR Labeling & Metrics
      - DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db
      # Embedding Service (ML operations)
      - EMBEDDING_SERVICE_URL=http://embedding-service:8087
      # BYOEH Configuration
      - QDRANT_URL=http://qdrant:6333
      - BYOEH_ENCRYPTION_ENABLED=true
      - BYOEH_CHUNK_SIZE=1000
      - BYOEH_CHUNK_OVERLAP=200
      # MinIO Configuration (RAG Document Storage)
      - MINIO_ENDPOINT=minio:9000
      - MINIO_ACCESS_KEY=${MINIO_ROOT_USER:-breakpilot}
      - MINIO_SECRET_KEY=${MINIO_ROOT_PASSWORD:-breakpilot123}
      - MINIO_BUCKET=breakpilot-rag
      - MINIO_SECURE=false
      # Ollama LLM Configuration
      - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
      - OLLAMA_ENABLED=${OLLAMA_ENABLED:-true}
      - OLLAMA_DEFAULT_MODEL=${OLLAMA_DEFAULT_MODEL:-qwen2.5:14b}
      - OLLAMA_VISION_MODEL=${OLLAMA_VISION_MODEL:-qwen2.5vl:32b}
      - OLLAMA_CORRECTION_MODEL=${OLLAMA_CORRECTION_MODEL:-qwen2.5:14b}
      # PaddleOCR Service (x86_64 via Rosetta)
      - PADDLEOCR_SERVICE_URL=http://paddleocr-service:8095
      # HashiCorp Vault - Anthropic API Key for Loesung E
      - VAULT_ADDR=http://vault:8200
      - VAULT_TOKEN=${VAULT_DEV_TOKEN:-breakpilot-dev-token}
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
    volumes:
      - klausur_uploads:/app/uploads
      - eh_uploads:/app/eh-uploads
      - ocr_labeling:/app/ocr-labeling
      - paddle_models:/root/.paddlex  # Persist PaddleOCR models across restarts
      - ./docs:/app/docs  # NIBIS Abitur-Dateien
    depends_on:
      - backend
      - school-service
      - embedding-service
      - postgres
      - qdrant
      - minio
      - paddleocr-service
      - vault
    networks:
      - breakpilot-pwa-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8086/health"]
      interval: 30s
      timeout: 30s
      retries: 3
      start_period: 10s
    restart: unless-stopped

  # ============================================
  # PaddleOCR Service - x86_64 via Rosetta
  # Runs in emulation to avoid ARM64 crashes
  # ============================================
  paddleocr-service:
    build:
      context: ./paddleocr-service
      dockerfile: Dockerfile
    platform: linux/amd64  # Force x86_64 emulation via Rosetta
    container_name: breakpilot-pwa-paddleocr
    expose:
      - "8095"
    environment:
      - PADDLE_PDX_DISABLE_MODEL_SOURCE_CHECK=True
    volumes:
      - paddleocr_models:/root/.paddlex  # Cache PaddleX models
      - paddleocr_models:/root/.paddleocr  # Cache PaddleOCR 3.x models
    networks:
      - breakpilot-pwa-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8095/health"]
      interval: 30s
      timeout: 60s
      retries: 5
      start_period: 180s  # Models need time to load in emulation
    restart: unless-stopped

  # Qdrant Vector Database (BYOEH - Erwartungshorizont RAG)
  # REST API: http://localhost:6333
  # gRPC: localhost:6334
  qdrant:
    image: qdrant/qdrant:v1.12.1
    container_name: breakpilot-pwa-qdrant
    ports:
      - "6333:6333"
      - "6334:6334"
    volumes:
      - qdrant_data:/qdrant/storage
    environment:
      - QDRANT__SERVICE__GRPC_PORT=6334
    healthcheck:
      test: ["CMD-SHELL", "bash -c '</dev/tcp/localhost/6333'"]
      interval: 10s
      timeout: 5s
      retries: 3
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # MinIO Object Storage (RAG Document Storage)
  # Web Console: http://localhost:9001
  # S3 API: http://localhost:9000
  minio:
    image: minio/minio:latest
    container_name: breakpilot-pwa-minio
    ports:
      - "9000:9000"   # S3 API
      - "9001:9001"   # Web Console
    environment:
      - MINIO_ROOT_USER=${MINIO_ROOT_USER:-breakpilot}
      - MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD:-breakpilot123}
    volumes:
      - minio_data:/data
    command: server /data --console-address ":9001"
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 10s
      timeout: 5s
      retries: 3
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Go Billing Service
  billing-service:
    build:
      context: ./billing-service
      dockerfile: Dockerfile
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-billing-service
    ports:
      - "8083:8083"
    environment:
      - DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
      - JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
      - PORT=8083
      - ENVIRONMENT=${ENVIRONMENT:-development}
      - ALLOWED_ORIGINS=http://localhost:8000,http://backend:8000
      # Stripe Configuration
      - STRIPE_SECRET_KEY=${STRIPE_SECRET_KEY:-}
      - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET:-}
      - STRIPE_PUBLISHABLE_KEY=${STRIPE_PUBLISHABLE_KEY:-}
      # URLs
      - BILLING_SUCCESS_URL=${BILLING_SUCCESS_URL:-http://localhost:3000/success}
      - BILLING_CANCEL_URL=${BILLING_CANCEL_URL:-http://localhost:3000/cancel}
      - FRONTEND_URL=${FRONTEND_URL:-http://localhost:8000}
      # Trial
      - TRIAL_PERIOD_DAYS=${TRIAL_PERIOD_DAYS:-7}
      # Internal API
      - INTERNAL_API_KEY=${INTERNAL_API_KEY:-internal-service-key-change-in-production}
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Website - BreakPilot Landing/Pricing Page (Next.js)
  # HTTPS via nginx on port 3000
  website:
    build:
      context: ./website
      dockerfile: Dockerfile
      args:
        - NEXT_PUBLIC_BILLING_API_URL=${NEXT_PUBLIC_BILLING_API_URL:-https://macmini:8083}
        - NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-https://macmini:8000}
        - NEXT_PUBLIC_KLAUSUR_SERVICE_URL=${NEXT_PUBLIC_KLAUSUR_SERVICE_URL:-https://macmini:8086}
        - NEXT_PUBLIC_VOICE_SERVICE_URL=${NEXT_PUBLIC_VOICE_SERVICE_URL:-https://macmini:8091}
        - NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${STRIPE_PUBLISHABLE_KEY:-}
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-website
    expose:
      - "3000"
    environment:
      - NODE_ENV=production
      - VAST_API_KEY=${VAST_API_KEY:-}
      - CONTROL_API_KEY=${CONTROL_API_KEY:-}
      - BACKEND_URL=http://backend:8000
      - CONSENT_SERVICE_URL=http://consent-service:8081
      # Edu-Search Service (for uni-crawler admin)
      - EDU_SEARCH_URL=${EDU_SEARCH_URL:-http://breakpilot-edu-search:8086}
      - EDU_SEARCH_API_KEY=${EDU_SEARCH_API_KEY:-dev-key}
      # Unity AI Bridge (runs on host machine in Unity Editor)
      - UNITY_BRIDGE_URL=http://host.docker.internal:8090
      # Woodpecker CI/CD Status Widget
      - WOODPECKER_URL=${WOODPECKER_URL:-http://woodpecker-server:8000}
      - WOODPECKER_TOKEN=${WOODPECKER_TOKEN:-}
    depends_on:
      - billing-service
      - backend
      - consent-service
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # ============================================
  # Studio v2 - Neue Lehrer-Oberfläche
  # Next.js auf Port 3001
  # ============================================
  studio-v2:
    build:
      context: ./studio-v2
      dockerfile: Dockerfile
      args:
        - NEXT_PUBLIC_VOICE_SERVICE_URL=${NEXT_PUBLIC_VOICE_SERVICE_URL:-https://macmini:8091}
        - NEXT_PUBLIC_KLAUSUR_SERVICE_URL=${NEXT_PUBLIC_KLAUSUR_SERVICE_URL:-https://macmini:8086}
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-studio-v2
    # Port 443 wird von nginx HTTPS bereitgestellt (studio-v2 intern auf 3001)
    # Port 3004 ist HTTP-only für Mobile Uploads (HSTS-Umgehung)
    ports:
      - "3004:3001"  # HTTP-only port for mobile QR uploads
    expose:
      - "3001"
    environment:
      - NODE_ENV=production
      - BACKEND_URL=http://backend:8000
    depends_on:
      - backend
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # ============================================
  # Admin v2 - Neues Admin-Frontend
  # Next.js auf Port 3002
  # ============================================
  admin-v2:
    build:
      context: ./admin-v2
      dockerfile: Dockerfile
      args:
        - NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL:-https://macmini:8000}
        - NEXT_PUBLIC_OLD_ADMIN_URL=${NEXT_PUBLIC_OLD_ADMIN_URL:-https://macmini:3000/admin}
        - NEXT_PUBLIC_SDK_URL=${NEXT_PUBLIC_SDK_URL:-https://macmini:8093}
        - NEXT_PUBLIC_KLAUSUR_SERVICE_URL=${NEXT_PUBLIC_KLAUSUR_SERVICE_URL:-/klausur-api}
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-admin-v2
    # Port 3002 wird über nginx proxied (admin-v2:3000 intern)
    # Port 3005 ist HTTP-only für Mobile Uploads (HSTS-Umgehung)
    ports:
      - "3005:3000"  # HTTP-only port for mobile QR uploads
    expose:
      - "3000"
    environment:
      - NODE_ENV=production
      - BACKEND_URL=http://backend:8000
      - CONSENT_SERVICE_URL=http://consent-service:8081
      - KLAUSUR_SERVICE_URL=http://klausur-service:8086
      - SDK_URL=http://ai-compliance-sdk:8090
      # Woodpecker CI Status
      - WOODPECKER_URL=${WOODPECKER_URL:-http://woodpecker-server:8000}
      - WOODPECKER_TOKEN=${WOODPECKER_TOKEN:-}
    volumes:
      # Mount Docker socket for Mac Mini monitoring dashboard
      - /var/run/docker.sock:/var/run/docker.sock:ro
    depends_on:
      - backend
      - consent-service
      - ai-compliance-sdk
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # ============================================
  # AI Compliance SDK - Multi-Tenant RBAC & LLM Gateway
  # Go auf Port 8090 (intern), 8093 (extern)
  # CFO Use-Case: Namespace-isolierte KI-Nutzung
  # ============================================
  ai-compliance-sdk:
    build:
      context: ./ai-compliance-sdk
      dockerfile: Dockerfile
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-ai-compliance-sdk
    # Port 8093 wird über nginx proxied (ai-compliance-sdk:8090 intern)
    environment:
      - PORT=8090
      - ENVIRONMENT=${ENVIRONMENT:-development}
      # PostgreSQL for RBAC, Policies, Audit
      - DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
      - JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
      # LLM Provider Configuration
      - LLM_PROVIDER=${SDK_LLM_PROVIDER:-ollama}
      - LLM_FALLBACK_PROVIDER=${SDK_LLM_FALLBACK_PROVIDER:-anthropic}
      # Ollama (Mac Mini lokal - DSGVO-konform)
      - OLLAMA_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
      - OLLAMA_DEFAULT_MODEL=${OLLAMA_DEFAULT_MODEL:-qwen2.5:14b}
      # Anthropic (Cloud-Fallback via Syseleven BSI-Cloud)
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
      - ANTHROPIC_DEFAULT_MODEL=${ANTHROPIC_DEFAULT_MODEL:-claude-sonnet-4-20250514}
      # PII Detection & Redaction
      - PII_REDACTION_ENABLED=${PII_REDACTION_ENABLED:-true}
      - PII_REDACTION_LEVEL=${PII_REDACTION_LEVEL:-strict}
      # Audit Trail
      - AUDIT_RETENTION_DAYS=${AUDIT_RETENTION_DAYS:-365}
      - AUDIT_LOG_PROMPTS=${AUDIT_LOG_PROMPTS:-false}
      # CORS
      - ALLOWED_ORIGINS=http://localhost:3002,https://macmini:3002,http://admin-v2:3000
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8090/health"]
      interval: 30s
      timeout: 3s
      start_period: 10s
      retries: 3
    restart: unless-stopped

  # DSMS Node - Dezentrales Speichersystem (Private IPFS)
  dsms-node:
    build:
      context: ./dsms-node
      dockerfile: Dockerfile
    container_name: breakpilot-pwa-dsms-node
    ports:
      - "4001:4001"   # Swarm P2P
      - "5001:5001"   # IPFS API
      - "8085:8080"   # IPFS Gateway (8085 um Konflikt mit Backend zu vermeiden)
    volumes:
      - dsms_data:/data/ipfs
    environment:
      - IPFS_PROFILE=server
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "ipfs id > /dev/null 2>&1 || exit 1"]
      interval: 30s
      timeout: 10s
      start_period: 30s
      retries: 3

  # DSMS Gateway - REST API für DSMS
  dsms-gateway:
    build:
      context: ./dsms-gateway
      dockerfile: Dockerfile
    container_name: breakpilot-pwa-dsms-gateway
    ports:
      - "8082:8082"
    environment:
      - IPFS_API_URL=http://dsms-node:5001
      - IPFS_GATEWAY_URL=http://dsms-node:8080
      - JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
    depends_on:
      dsms-node:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # ============================================
  # Jitsi Meet - Videokonferenzen für Schulungen
  # Web UI: http://localhost:8443
  # ============================================

  # Jitsi Web Frontend
  jitsi-web:
    image: jitsi/web:stable-9823
    container_name: breakpilot-pwa-jitsi-web
    expose:
      - "80"
    environment:
      - ENABLE_XMPP_WEBSOCKET=1
      - ENABLE_COLIBRI_WEBSOCKET=1
      - XMPP_DOMAIN=meet.jitsi
      - XMPP_BOSH_URL_BASE=http://jitsi-xmpp:5280
      - XMPP_MUC_DOMAIN=muc.meet.jitsi
      - XMPP_GUEST_DOMAIN=guest.meet.jitsi
      - TZ=Europe/Berlin
      - PUBLIC_URL=${JITSI_PUBLIC_URL:-https://macmini}
      - JICOFO_AUTH_USER=focus
      - ENABLE_AUTH=${JITSI_ENABLE_AUTH:-0}
      - ENABLE_GUESTS=${JITSI_ENABLE_GUESTS:-1}
      - ENABLE_RECORDING=${JITSI_ENABLE_RECORDING:-1}
      - ENABLE_LIVESTREAMING=0
      - DISABLE_HTTPS=1
      # Branding
      - APP_NAME=BreakPilot Meet
      - NATIVE_APP_NAME=BreakPilot Meet
      - PROVIDER_NAME=BreakPilot
    volumes:
      - jitsi_web_config:/config
      - jitsi_web_crontabs:/var/spool/cron/crontabs
      - jitsi_transcripts:/usr/share/jitsi-meet/transcripts
    networks:
      breakpilot-pwa-network:
        aliases:
          - meet.jitsi
    restart: unless-stopped
    depends_on:
      - jitsi-xmpp

  # Prosody XMPP Server
  jitsi-xmpp:
    image: jitsi/prosody:stable-9823
    container_name: breakpilot-pwa-jitsi-xmpp
    environment:
      - XMPP_DOMAIN=meet.jitsi
      - XMPP_AUTH_DOMAIN=auth.meet.jitsi
      - XMPP_MUC_DOMAIN=muc.meet.jitsi
      - XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
      - XMPP_GUEST_DOMAIN=guest.meet.jitsi
      - XMPP_RECORDER_DOMAIN=recorder.meet.jitsi
      - XMPP_CROSS_DOMAIN=true
      - TZ=Europe/Berlin
      - JICOFO_AUTH_USER=focus
      - JICOFO_AUTH_PASSWORD=${JITSI_JICOFO_AUTH_PASSWORD:-jicofo_secret_123}
      - JVB_AUTH_USER=jvb
      - JVB_AUTH_PASSWORD=${JITSI_JVB_AUTH_PASSWORD:-jvb_secret_123}
      - JIGASI_XMPP_USER=jigasi
      - JIGASI_XMPP_PASSWORD=${JITSI_JIGASI_XMPP_PASSWORD:-jigasi_secret_123}
      - JIBRI_XMPP_USER=jibri
      - JIBRI_XMPP_PASSWORD=${JITSI_JIBRI_XMPP_PASSWORD:-jibri_secret_123}
      - JIBRI_RECORDER_USER=recorder
      - JIBRI_RECORDER_PASSWORD=${JITSI_JIBRI_RECORDER_PASSWORD:-recorder_secret_123}
      - LOG_LEVEL=info
      - PUBLIC_URL=${JITSI_PUBLIC_URL:-https://macmini}
      - ENABLE_AUTH=${JITSI_ENABLE_AUTH:-0}
      - ENABLE_GUESTS=${JITSI_ENABLE_GUESTS:-1}
    volumes:
      - jitsi_prosody_config:/config
      - jitsi_prosody_plugins:/prosody-plugins-custom
    networks:
      breakpilot-pwa-network:
        aliases:
          - xmpp.meet.jitsi
    restart: unless-stopped

  # Jicofo - Jitsi Conference Focus
  jitsi-jicofo:
    image: jitsi/jicofo:stable-9823
    container_name: breakpilot-pwa-jitsi-jicofo
    environment:
      - XMPP_DOMAIN=meet.jitsi
      - XMPP_AUTH_DOMAIN=auth.meet.jitsi
      - XMPP_MUC_DOMAIN=muc.meet.jitsi
      - XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
      - XMPP_SERVER=jitsi-xmpp
      - JICOFO_AUTH_USER=focus
      - JICOFO_AUTH_PASSWORD=${JITSI_JICOFO_AUTH_PASSWORD:-jicofo_secret_123}
      - TZ=Europe/Berlin
      - ENABLE_AUTH=${JITSI_ENABLE_AUTH:-0}
      - AUTH_TYPE=internal
      - ENABLE_AUTO_OWNER=${JITSI_ENABLE_AUTO_OWNER:-1}
    volumes:
      - jitsi_jicofo_config:/config
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    depends_on:
      - jitsi-xmpp

  # JVB - Jitsi Videobridge (WebRTC SFU)
  jitsi-jvb:
    image: jitsi/jvb:stable-9823
    container_name: breakpilot-pwa-jitsi-jvb
    ports:
      - "10000:10000/udp"    # Video/Audio RTP
      - "8080:8080"          # Colibri REST API (internal)
    environment:
      - XMPP_DOMAIN=meet.jitsi
      - XMPP_AUTH_DOMAIN=auth.meet.jitsi
      - XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
      - XMPP_SERVER=jitsi-xmpp
      - JVB_AUTH_USER=jvb
      - JVB_AUTH_PASSWORD=${JITSI_JVB_AUTH_PASSWORD:-jvb_secret_123}
      - JVB_PORT=10000
      - JVB_STUN_SERVERS=meet-jit-si-turnrelay.jitsi.net:443
      - TZ=Europe/Berlin
      - PUBLIC_URL=${JITSI_PUBLIC_URL:-https://macmini}
      - COLIBRI_REST_ENABLED=true
      - ENABLE_COLIBRI_WEBSOCKET=1
    volumes:
      - jitsi_jvb_config:/config
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    depends_on:
      - jitsi-xmpp

  # ============================================
  # Jibri - Jitsi Recording Service
  # Recordings werden zu MinIO hochgeladen
  # ============================================
  jibri:
    build:
      context: ./docker/jibri
      dockerfile: Dockerfile
    container_name: breakpilot-pwa-jibri
    environment:
      - XMPP_DOMAIN=meet.jitsi
      - XMPP_AUTH_DOMAIN=auth.meet.jitsi
      - XMPP_INTERNAL_MUC_DOMAIN=internal-muc.meet.jitsi
      - XMPP_RECORDER_DOMAIN=recorder.meet.jitsi
      - XMPP_SERVER=jitsi-xmpp
      - XMPP_MUC_DOMAIN=muc.meet.jitsi
      - JIBRI_XMPP_USER=jibri
      - JIBRI_XMPP_PASSWORD=${JITSI_JIBRI_XMPP_PASSWORD:-jibri_secret_123}
      - JIBRI_RECORDER_USER=recorder
      - JIBRI_RECORDER_PASSWORD=${JITSI_JIBRI_RECORDER_PASSWORD:-recorder_secret_123}
      - JIBRI_BREWERY_MUC=jibribrewery
      - JIBRI_RECORDING_DIR=/recordings
      - JIBRI_FINALIZE_SCRIPT=/config/finalize.sh
      - TZ=Europe/Berlin
      # X11 Display Konfiguration (Xvfb)
      - DISPLAY=:0
      - RESOLUTION=1920x1080x24
      # Optional: VNC fuer Debugging (Port 5900)
      # - VNC_PASSWORD=debug123
      # MinIO Upload Konfiguration
      - MINIO_ENDPOINT=minio:9000
      - MINIO_ACCESS_KEY=${MINIO_ROOT_USER}
      - MINIO_SECRET_KEY=${MINIO_ROOT_PASSWORD}
      - MINIO_BUCKET=breakpilot-recordings
      # Backend Webhook (wird nach Upload aufgerufen)
      - BACKEND_WEBHOOK_URL=http://backend:8000/api/recordings/webhook
    volumes:
      - jibri_recordings:/recordings
      - /dev/shm:/dev/shm
    shm_size: '2gb'
    cap_add:
      - SYS_ADMIN
      - NET_BIND_SERVICE
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    depends_on:
      - jitsi-xmpp
      - minio
    profiles:
      - recording

  # ============================================
  # Transcription Worker - Whisper + pyannote
  # Verarbeitet Recordings asynchron
  # ============================================
  transcription-worker:
    build:
      context: ./backend
      dockerfile: Dockerfile.worker
    container_name: breakpilot-pwa-transcription-worker
    environment:
      - DATABASE_URL=postgres://breakpilot:breakpilot123@postgres:5432/breakpilot_db?sslmode=disable
      - REDIS_URL=redis://valkey:6379/1
      - WHISPER_MODEL=${WHISPER_MODEL:-large-v3}
      - WHISPER_DEVICE=${WHISPER_DEVICE:-cpu}
      - WHISPER_COMPUTE_TYPE=${WHISPER_COMPUTE_TYPE:-int8}
      # pyannote.audio Token (HuggingFace)
      - PYANNOTE_AUTH_TOKEN=${PYANNOTE_AUTH_TOKEN:-}
      # MinIO Storage
      - MINIO_ENDPOINT=${MINIO_ENDPOINT:-minio:9000}
      - MINIO_ACCESS_KEY=${MINIO_ROOT_USER:-breakpilot}
      - MINIO_SECRET_KEY=${MINIO_ROOT_PASSWORD:-breakpilot123}
      - MINIO_BUCKET=breakpilot-recordings
      - MINIO_SECURE=false
      - TZ=Europe/Berlin
    volumes:
      - transcription_models:/root/.cache/huggingface
      - transcription_temp:/tmp/transcriptions
    deploy:
      resources:
        limits:
          memory: 8G
        reservations:
          memory: 4G
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
      valkey:
        condition: service_healthy
      minio:
        condition: service_started
    profiles:
      - recording

  # ============================================
  # ERPNext - Open Source ERP System
  # Web UI: http://localhost:8090
  # Default: Administrator / admin
  # ============================================

  # MariaDB for ERPNext
  erpnext-db:
    image: mariadb:10.6
    container_name: breakpilot-pwa-erpnext-db
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --skip-character-set-client-handshake --skip-innodb-read-only-compressed
    environment:
      - MYSQL_ROOT_PASSWORD=${ERPNEXT_DB_ROOT_PASSWORD:-changeit123}
      - MYSQL_DATABASE=erpnext
      - MYSQL_USER=erpnext
      - MYSQL_PASSWORD=${ERPNEXT_DB_PASSWORD:-erpnext123}
    volumes:
      - erpnext_db_data:/var/lib/mysql
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${ERPNEXT_DB_ROOT_PASSWORD:-changeit123}"]
      interval: 5s
      timeout: 5s
      retries: 10

  # Redis Queue for ERPNext
  erpnext-redis-queue:
    image: redis:alpine
    container_name: breakpilot-pwa-erpnext-redis-queue
    volumes:
      - erpnext_redis_queue_data:/data
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # Redis Cache for ERPNext
  erpnext-redis-cache:
    image: redis:alpine
    container_name: breakpilot-pwa-erpnext-redis-cache
    volumes:
      - erpnext_redis_cache_data:/data
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # ERPNext Site Creator (runs once)
  erpnext-create-site:
    image: frappe/erpnext:latest
    container_name: breakpilot-pwa-erpnext-create-site
    command:
      - bash
      - -c
      - |
        wait-for-it -t 120 erpnext-db:3306;
        wait-for-it -t 120 erpnext-redis-cache:6379;
        wait-for-it -t 120 erpnext-redis-queue:6379;
        if [[ ! -f sites/erpnext.local/site_config.json ]]; then
          echo "Creating ERPNext site...";
          bench new-site erpnext.local --db-host=erpnext-db --db-port=3306 --admin-password=admin --db-root-password=${ERPNEXT_DB_ROOT_PASSWORD:-changeit123} --install-app erpnext --set-default;
          echo "Site created successfully!";
        else
          echo "Site already exists, skipping creation.";
        fi;
    environment:
      - DB_HOST=erpnext-db
      - DB_PORT=3306
      - REDIS_CACHE=redis://erpnext-redis-cache:6379
      - REDIS_QUEUE=redis://erpnext-redis-queue:6379
      - SOCKETIO_PORT=9000
    volumes:
      - erpnext_sites:/home/frappe/frappe-bench/sites
      - erpnext_logs:/home/frappe/frappe-bench/logs
    networks:
      - breakpilot-pwa-network
    depends_on:
      erpnext-db:
        condition: service_healthy
      erpnext-redis-cache:
        condition: service_started
      erpnext-redis-queue:
        condition: service_started

  # ERPNext Backend
  erpnext-backend:
    image: frappe/erpnext:latest
    container_name: breakpilot-pwa-erpnext-backend
    command:
      - bash
      - -c
      - |
        wait-for-it -t 120 erpnext-db:3306;
        wait-for-it -t 120 erpnext-redis-cache:6379;
        wait-for-it -t 120 erpnext-redis-queue:6379;
        bench serve --port 8000
    environment:
      - DB_HOST=erpnext-db
      - DB_PORT=3306
      - REDIS_CACHE=redis://erpnext-redis-cache:6379
      - REDIS_QUEUE=redis://erpnext-redis-queue:6379
      - SOCKETIO_PORT=9000
    volumes:
      - erpnext_sites:/home/frappe/frappe-bench/sites
      - erpnext_logs:/home/frappe/frappe-bench/logs
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    depends_on:
      erpnext-db:
        condition: service_healthy
      erpnext-create-site:
        condition: service_completed_successfully

  # ERPNext WebSocket
  erpnext-websocket:
    image: frappe/erpnext:latest
    container_name: breakpilot-pwa-erpnext-websocket
    command:
      - bash
      - -c
      - |
        wait-for-it -t 120 erpnext-db:3306;
        wait-for-it -t 120 erpnext-redis-cache:6379;
        wait-for-it -t 120 erpnext-redis-queue:6379;
        bench watch
    environment:
      - DB_HOST=erpnext-db
      - DB_PORT=3306
      - REDIS_CACHE=redis://erpnext-redis-cache:6379
      - REDIS_QUEUE=redis://erpnext-redis-queue:6379
      - SOCKETIO_PORT=9000
    volumes:
      - erpnext_sites:/home/frappe/frappe-bench/sites
      - erpnext_logs:/home/frappe/frappe-bench/logs
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    depends_on:
      erpnext-db:
        condition: service_healthy
      erpnext-create-site:
        condition: service_completed_successfully

  # ERPNext Scheduler
  erpnext-scheduler:
    image: frappe/erpnext:latest
    container_name: breakpilot-pwa-erpnext-scheduler
    command:
      - bash
      - -c
      - |
        wait-for-it -t 120 erpnext-db:3306;
        wait-for-it -t 120 erpnext-redis-cache:6379;
        wait-for-it -t 120 erpnext-redis-queue:6379;
        bench schedule
    environment:
      - DB_HOST=erpnext-db
      - DB_PORT=3306
      - REDIS_CACHE=redis://erpnext-redis-cache:6379
      - REDIS_QUEUE=redis://erpnext-redis-queue:6379
      - SOCKETIO_PORT=9000
    volumes:
      - erpnext_sites:/home/frappe/frappe-bench/sites
      - erpnext_logs:/home/frappe/frappe-bench/logs
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    depends_on:
      erpnext-db:
        condition: service_healthy
      erpnext-create-site:
        condition: service_completed_successfully

  # ERPNext Worker (Long)
  erpnext-worker-long:
    image: frappe/erpnext:latest
    container_name: breakpilot-pwa-erpnext-worker-long
    command:
      - bash
      - -c
      - |
        wait-for-it -t 120 erpnext-db:3306;
        wait-for-it -t 120 erpnext-redis-cache:6379;
        wait-for-it -t 120 erpnext-redis-queue:6379;
        bench worker --queue long,default,short
    environment:
      - DB_HOST=erpnext-db
      - DB_PORT=3306
      - REDIS_CACHE=redis://erpnext-redis-cache:6379
      - REDIS_QUEUE=redis://erpnext-redis-queue:6379
      - SOCKETIO_PORT=9000
    volumes:
      - erpnext_sites:/home/frappe/frappe-bench/sites
      - erpnext_logs:/home/frappe/frappe-bench/logs
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    depends_on:
      erpnext-db:
        condition: service_healthy
      erpnext-create-site:
        condition: service_completed_successfully

  # ERPNext Worker (Short)
  erpnext-worker-short:
    image: frappe/erpnext:latest
    container_name: breakpilot-pwa-erpnext-worker-short
    command:
      - bash
      - -c
      - |
        wait-for-it -t 120 erpnext-db:3306;
        wait-for-it -t 120 erpnext-redis-cache:6379;
        wait-for-it -t 120 erpnext-redis-queue:6379;
        bench worker --queue short,default
    environment:
      - DB_HOST=erpnext-db
      - DB_PORT=3306
      - REDIS_CACHE=redis://erpnext-redis-cache:6379
      - REDIS_QUEUE=redis://erpnext-redis-queue:6379
      - SOCKETIO_PORT=9000
    volumes:
      - erpnext_sites:/home/frappe/frappe-bench/sites
      - erpnext_logs:/home/frappe/frappe-bench/logs
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    depends_on:
      erpnext-db:
        condition: service_healthy
      erpnext-create-site:
        condition: service_completed_successfully

  # ERPNext Frontend (Nginx)
  erpnext-frontend:
    image: frappe/erpnext:latest
    container_name: breakpilot-pwa-erpnext-frontend
    command:
      - nginx-entrypoint.sh
    ports:
      - "8090:8080"
    environment:
      - BACKEND=erpnext-backend:8000
      - SOCKETIO=erpnext-websocket:9000
      - UPSTREAM_REAL_IP_ADDRESS=127.0.0.1
      - UPSTREAM_REAL_IP_HEADER=X-Forwarded-For
      - UPSTREAM_REAL_IP_RECURSIVE=off
      - FRAPPE_SITE_NAME_HEADER=erpnext.local
    volumes:
      - erpnext_sites:/home/frappe/frappe-bench/sites
      - erpnext_logs:/home/frappe/frappe-bench/logs
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    depends_on:
      erpnext-backend:
        condition: service_started
      erpnext-websocket:
        condition: service_started

  # ============================================
  # Breakpilot Drive - Lernspiel (Unity WebGL)
  # Web UI: http://localhost:3001
  # ============================================
  breakpilot-drive:
    build:
      context: ./breakpilot-drive
      dockerfile: Dockerfile
    container_name: breakpilot-pwa-drive
    ports:
      - "3001:80"
    environment:
      # API Configuration (injected into Unity WebGL)
      - API_BASE_URL=${GAME_API_URL:-http://localhost:8000/api/game}
      # Feature Flags
      - GAME_REQUIRE_AUTH=${GAME_REQUIRE_AUTH:-false}
      - GAME_ENABLE_LEADERBOARDS=${GAME_ENABLE_LEADERBOARDS:-true}
    networks:
      - breakpilot-pwa-network
    depends_on:
      - backend
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:80/health.json"]
      interval: 30s
      timeout: 10s
      retries: 3
    restart: unless-stopped
    profiles:
      - game

  # ============================================
  # Camunda 7 - BPMN Workflow Engine
  # Web UI: http://localhost:8089/camunda
  # REST API: http://localhost:8089/engine-rest
  # License: Apache 2.0 (kommerziell nutzbar)
  # ============================================
  camunda:
    image: camunda/camunda-bpm-platform:7.21.0
    container_name: breakpilot-pwa-camunda
    ports:
      - "8089:8080"
    environment:
      - DB_DRIVER=org.postgresql.Driver
      - DB_URL=jdbc:postgresql://postgres:5432/breakpilot_db
      - DB_USERNAME=breakpilot
      - DB_PASSWORD=${POSTGRES_PASSWORD:-breakpilot123}
      - DB_VALIDATE_ON_BORROW=true
      - WAIT_FOR=postgres:5432
      - CAMUNDA_BPM_ADMIN_USER_ID=admin
      - CAMUNDA_BPM_ADMIN_USER_PASSWORD=${CAMUNDA_ADMIN_PASSWORD:-admin123}
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8080/camunda/api/engine || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s
    restart: unless-stopped
    profiles:
      - bpmn

  # ============================================
  # GeoEdu Service - Self-Hosted OSM + Terrain
  # DSGVO-konforme Erdkunde-Lernplattform
  # Web UI: http://localhost:8088
  # ============================================
  geo-service:
    build:
      context: ./geo-service
      dockerfile: Dockerfile
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-geo-service
    ports:
      - "8088:8088"
    environment:
      - PORT=8088
      - ENVIRONMENT=${ENVIRONMENT:-development}
      - JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key}
      # PostgreSQL (PostGIS fuer OSM-Daten)
      - DATABASE_URL=postgresql+asyncpg://breakpilot:breakpilot123@postgres:5432/breakpilot_db
      # MinIO (AOI Bundles, generierte Assets)
      - MINIO_ENDPOINT=minio:9000
      - MINIO_ACCESS_KEY=${MINIO_ROOT_USER:-breakpilot}
      - MINIO_SECRET_KEY=${MINIO_ROOT_PASSWORD:-breakpilot123}
      - MINIO_BUCKET=breakpilot-geo
      - MINIO_SECURE=false
      # Ollama (Lernstationen generieren)
      - OLLAMA_BASE_URL=${OLLAMA_BASE_URL:-http://host.docker.internal:11434}
      - OLLAMA_MODEL=${OLLAMA_DEFAULT_MODEL:-qwen2.5:14b}
      # Tile Server Config
      - TILE_CACHE_DIR=/app/cache/tiles
      - DEM_CACHE_DIR=/app/cache/dem
      - MAX_AOI_SIZE_KM2=4
    volumes:
      - geo_osm_data:/app/data/osm
      - geo_dem_data:/app/data/dem
      - geo_tile_cache:/app/cache/tiles
      - geo_aoi_bundles:/app/bundles
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      postgres:
        condition: service_healthy
      minio:
        condition: service_started
    networks:
      - breakpilot-pwa-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8088/health"]
      interval: 30s
      timeout: 10s
      start_period: 60s
      retries: 3
    restart: unless-stopped

  # ============================================
  # Voice Service - PersonaPlex + TaskOrchestrator
  # Voice-First Interface fuer Breakpilot
  # DSGVO-konform: Keine Audio-Persistenz
  # Web UI: http://localhost:8091
  # ============================================
  voice-service:
    build:
      context: ./voice-service
      dockerfile: Dockerfile
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-voice-service
    # Port 8091 wird von nginx HTTPS bereitgestellt (wss://macmini:8091)
    expose:
      - "8091"
    environment:
      - PORT=8091
      - DATABASE_URL=postgresql+asyncpg://breakpilot:breakpilot123@postgres:5432/breakpilot_db
      - VALKEY_URL=redis://valkey:6379/2
      - PERSONAPLEX_ENABLED=${PERSONAPLEX_ENABLED:-false}
      - PERSONAPLEX_WS_URL=${PERSONAPLEX_WS_URL:-ws://host.docker.internal:8998}
      - ORCHESTRATOR_ENABLED=true
      - FALLBACK_LLM_PROVIDER=${FALLBACK_LLM_PROVIDER:-ollama}
      - OLLAMA_BASE_URL=http://host.docker.internal:11434
      - OLLAMA_VOICE_MODEL=qwen2.5:32b
      - BQAS_JUDGE_MODEL=qwen2.5:14b
      - KLAUSUR_SERVICE_URL=http://klausur-service:8086
      - ENCRYPTION_ENABLED=true
      - AUDIO_PERSISTENCE=false
      - AUDIO_SAMPLE_RATE=24000
      - ENVIRONMENT=${ENVIRONMENT:-development}
      - JWT_SECRET=${JWT_SECRET:-your-super-secret-jwt-key-change-in-production}
    volumes:
      - voice_session_data:/app/data/sessions
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      postgres:
        condition: service_healthy
      valkey:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8091/health"]
      interval: 30s
      timeout: 10s
      start_period: 60s
      retries: 3
    restart: unless-stopped

  # ============================================
  # MkDocs Documentation
  # Web UI: http://localhost:8009
  # Material Theme with German language support
  # ============================================
  docs:
    build:
      context: .
      dockerfile: docs-src/Dockerfile
    platform: linux/arm64  # Mac Mini Apple Silicon
    container_name: breakpilot-pwa-docs
    ports:
      - "8009:80"
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:80/"]
      interval: 30s
      timeout: 10s
      retries: 3

  # ============================================
  # Gitea - Self-hosted Git Server
  # Web UI: http://localhost:3003
  # SSH: localhost:2222
  # Gitea Actions enabled for CI/CD
  # ============================================
  gitea:
    image: gitea/gitea:1.22-rootless
    container_name: breakpilot-pwa-gitea
    extra_hosts:
      - "macmini:192.168.178.100"
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - GITEA__database__DB_TYPE=postgres
      - GITEA__database__HOST=postgres:5432
      - GITEA__database__NAME=gitea
      - GITEA__database__USER=breakpilot
      - GITEA__database__PASSWD=breakpilot123
      - GITEA__server__DOMAIN=macmini
      - GITEA__server__SSH_DOMAIN=macmini
      - GITEA__server__ROOT_URL=http://macmini:3003/
      - GITEA__server__HTTP_PORT=3003
      - GITEA__server__SSH_PORT=2222
      - GITEA__server__SSH_LISTEN_PORT=2222
      - GITEA__actions__ENABLED=true
      - GITEA__actions__DEFAULT_ACTIONS_URL=https://gitea.com
      - GITEA__service__DISABLE_REGISTRATION=true
      - GITEA__service__REQUIRE_SIGNIN_VIEW=false
      - GITEA__repository__DEFAULT_BRANCH=main
      - GITEA__log__LEVEL=Info
      - GITEA__webhook__ALLOWED_HOST_LIST=macmini,192.168.178.100,woodpecker-server,localhost,external
    volumes:
      - gitea_data:/var/lib/gitea
      - gitea_config:/etc/gitea
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3003:3003"
      - "2222:2222"
    depends_on:
      postgres:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3003/api/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 30s
    restart: unless-stopped

  # ============================================
  # Gitea Actions Runner
  # Executes CI/CD workflows defined in .gitea/workflows/
  # Includes Syft, Grype, Trivy for SBOM generation
  # ============================================
  gitea-runner:
    image: gitea/act_runner:latest
    container_name: breakpilot-pwa-gitea-runner
    environment:
      - CONFIG_FILE=/config/config.yaml
      - GITEA_INSTANCE_URL=http://gitea:3003
      - GITEA_RUNNER_REGISTRATION_TOKEN=${GITEA_RUNNER_TOKEN:-}
      - GITEA_RUNNER_NAME=breakpilot-runner
      - GITEA_RUNNER_LABELS=ubuntu-latest:docker://node:20-bookworm,ubuntu-22.04:docker://ubuntu:22.04,self-hosted:host
    volumes:
      - gitea_runner_data:/data
      - ./gitea/runner-config.yaml:/config/config.yaml:ro
      - /var/run/docker.sock:/var/run/docker.sock
    depends_on:
      gitea:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # ============================================
  # Woodpecker CI - Server
  # Modern CI/CD with native container support
  # Web UI: http://localhost:8085
  # ============================================
  woodpecker-server:
    image: woodpeckerci/woodpecker-server:v3
    container_name: breakpilot-pwa-woodpecker-server
    ports:
      - "8090:8000"
    extra_hosts:
      - "macmini:192.168.178.100"
    environment:
      - WOODPECKER_OPEN=true
      - WOODPECKER_HOST=http://macmini:8090
      - WOODPECKER_ADMIN=pilotadmin,breakpilot_admin
      # Gitea OAuth Integration
      - WOODPECKER_GITEA=true
      - WOODPECKER_GITEA_URL=http://macmini:3003
      - WOODPECKER_GITEA_CLIENT=${WOODPECKER_GITEA_CLIENT:-}
      - WOODPECKER_GITEA_SECRET=${WOODPECKER_GITEA_SECRET:-}
      # Agent Secret (fuer Agent-Authentifizierung)
      - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET:-woodpecker-agent-secret-key}
      # Database (SQLite fuer einfache Einrichtung)
      - WOODPECKER_DATABASE_DRIVER=sqlite3
      - WOODPECKER_DATABASE_DATASOURCE=/var/lib/woodpecker/woodpecker.sqlite
      # Logging
      - WOODPECKER_LOG_LEVEL=info
      # Trust all repos (allows privileged containers)
      - WOODPECKER_PLUGINS_PRIVILEGED=true
      - WOODPECKER_PLUGINS_TRUSTED_CLONE=true
    volumes:
      - woodpecker_data:/var/lib/woodpecker
    depends_on:
      gitea:
        condition: service_healthy
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

  # ============================================
  # Night Scheduler - Nachtabschaltung
  # Stoppt Services nachts, startet sie morgens
  # API: http://localhost:8096
  # ============================================
  night-scheduler:
    build: ./night-scheduler
    container_name: breakpilot-pwa-night-scheduler
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./night-scheduler/config:/config
      - ./docker-compose.yml:/app/docker-compose.yml:ro
    environment:
      - COMPOSE_PROJECT_NAME=breakpilot-pwa
    ports:
      - "8096:8096"
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8096/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

  # ============================================
  # Woodpecker CI - Agent
  # Executes pipeline steps in containers
  # ============================================
  woodpecker-agent:
    image: woodpeckerci/woodpecker-agent:v3
    container_name: breakpilot-pwa-woodpecker-agent
    command: agent
    environment:
      - WOODPECKER_SERVER=woodpecker-server:9000
      - WOODPECKER_AGENT_SECRET=${WOODPECKER_AGENT_SECRET:-woodpecker-agent-secret-key}
      - WOODPECKER_MAX_WORKFLOWS=4
      - WOODPECKER_LOG_LEVEL=info
      # Backend für Container-Ausführung
      - WOODPECKER_BACKEND=docker
      - DOCKER_HOST=unix:///var/run/docker.sock
      # Extra hosts für Pipeline-Container (damit sie macmini erreichen)
      - WOODPECKER_BACKEND_DOCKER_EXTRA_HOSTS=macmini:192.168.178.100,gitea:192.168.178.100
      # Nutze das gleiche Netzwerk für Pipeline-Container
      - WOODPECKER_BACKEND_DOCKER_NETWORK=breakpilot-dev_breakpilot-pwa-network
      # Docker-Socket für Build-Steps (Host-Docker statt DinD)
      - WOODPECKER_BACKEND_DOCKER_VOLUMES=/var/run/docker.sock:/var/run/docker.sock
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    depends_on:
      - woodpecker-server
    networks:
      - breakpilot-pwa-network
    restart: unless-stopped

volumes:
  # Woodpecker CI Data
  woodpecker_data:
    driver: local
  # Vault Data
  vault_data:
    driver: local
  # Vault Agent Config (role-id, secret-id, token)
  vault_agent_config:
    driver: local
  # Vault-managed SSL Certificates
  vault_certs:
    driver: local
  breakpilot_pwa_data:
    driver: local
  # Embedding Service Model Cache
  embedding_models:
    driver: local
  # Valkey Session Cache
  valkey_data:
    driver: local
  dsms_data:
    driver: local
  klausur_uploads:
    driver: local
  eh_uploads:
    driver: local
  ocr_labeling:
    driver: local
  # PaddleOCR Model Cache (persist across container restarts)
  paddle_models:
    driver: local
  # PaddleOCR Service Model Cache (x86_64 emulation)
  paddleocr_models:
    driver: local
  qdrant_data:
    driver: local
  minio_data:
    driver: local
  synapse_data:
    driver: local
  synapse_db_data:
    driver: local
  # Jitsi Volumes
  jitsi_web_config:
    driver: local
  jitsi_web_crontabs:
    driver: local
  jitsi_transcripts:
    driver: local
  jitsi_prosody_config:
    driver: local
  jitsi_prosody_plugins:
    driver: local
  jitsi_jicofo_config:
    driver: local
  jitsi_jvb_config:
    driver: local
  # Jibri Recording Volumes
  jibri_recordings:
    driver: local
  # Transcription Worker Volumes
  transcription_models:
    driver: local
  transcription_temp:
    driver: local
  # ERPNext Volumes
  erpnext_db_data:
    driver: local
  erpnext_redis_queue_data:
    driver: local
  erpnext_redis_cache_data:
    driver: local
  erpnext_sites:
    driver: local
  erpnext_logs:
    driver: local
  # GeoEdu Service Volumes
  geo_osm_data:
    driver: local
  geo_dem_data:
    driver: local
  geo_tile_cache:
    driver: local
  geo_aoi_bundles:
    driver: local
  # Voice Service Volumes (transient sessions only)
  voice_session_data:
    driver: local
  # Gitea Volumes
  gitea_data:
    driver: local
  gitea_config:
    driver: local
  gitea_runner_data:
    driver: local

networks:
  breakpilot-pwa-network:
    driver: bridge
