version: "3.9"

# Docker Swarm Stack Configuration - PRODUCTION
# Deploy with: docker stack deploy -c infra/stacks/vello/stack.prod.yml vello
# Features: Multiple replicas, no hot reload, production optimized

secrets:
  # Database
  database_url:
    external: true
  supabase_url:
    external: true
  supabase_key:
    external: true

  # Security
  jwt_secret:
    external: true
  encryption_key:
    external: true
  service_token:
    external: true

  # Redis
  redis_password:
    external: true

configs:
  redis_config:
    file: ../../../../platform/services/redis/config/redis.conf

services:
  # Redis Service - Production with high availability
  redis:
    image: redis:7-alpine
    networks:
      - vello-overlay
      - agenticca-core
    secrets:
      - redis_password
    configs:
      - source: redis_config
        target: /usr/local/etc/redis/redis.conf
    volumes:
      - redis_data:/data
    environment:
      - REDIS_PASSWORD_FILE=/run/secrets/redis_password
    command: sh -c 'redis-server /usr/local/etc/redis/redis.conf --requirepass "$$(cat $$REDIS_PASSWORD_FILE)"'
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
          - node.labels.redis == true
      resources:
        limits:
          cpus: "2"
          memory: 2G
        reservations:
          cpus: "1"
          memory: 1G
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
        monitor: 60s
        max_failure_ratio: 0.3
    healthcheck:
      test:
        [
          "CMD",
          "sh",
          "-c",
          "redis-cli -a $$(cat /run/secrets/redis_password) ping",
        ]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 30s

  # API Service - Production with multiple replicas
  api:
    image: vello-api:v0.7
    networks:
      - vello-overlay
      - agenticca-core
    ports:
      - target: ${API_INTERNAL_PORT}
        published: ${API_PUBLIC_PORT}
        protocol: tcp
        mode: host
    secrets:
      - database_url
      - supabase_url
      - supabase_key
      - jwt_secret
      - encryption_key
      - service_token
      - redis_password
    environment:
      # Use secret files
      - DATABASE_URL_FILE=/run/secrets/database_url
      - SUPABASE_URL_FILE=/run/secrets/supabase_url
      - SUPABASE_KEY_FILE=/run/secrets/supabase_key
      - SECRET_KEY_FILE=/run/secrets/jwt_secret
      - ENCRYPTION_KEY_FILE=/run/secrets/encryption_key
      - SERVICE_TOKEN_FILE=/run/secrets/service_token

      # Production settings - NO HOT RELOAD
      - ALLOWED_ORIGINS=${ALLOWED_ORIGINS}
      - API_HOST=${API_HOST}
      - API_PORT=${API_INTERNAL_PORT}
      - MODELSGATEWAY_URL=${MODELSGATEWAY_INTERNAL_BASE_URL}
      - EVENT_COLLECTOR_INTERNAL_BASE_URL=${EVENT_COLLECTOR_INTERNAL_BASE_URL}
      - EVENT_COLLECTOR_URL=${EVENT_COLLECTOR_INTERNAL_BASE_URL}
      - INTERNAL_ADMIN_TOKEN=prod-internal-admin
      - API_RELOAD=false
      - ACCESS_TOKEN_EXPIRE_MINUTES=15
      - REFRESH_TOKEN_EXPIRE_DAYS=7
      - WS_HEARTBEAT_INTERVAL=30
      - ENVIRONMENT=production

      # Redis with secret
      - REDIS_HOST=${REDIS_INTERNAL_HOST}
      - REDIS_PORT=${REDIS_INTERNAL_PORT}
      - REDIS_INTERNAL_BASE_URL=${REDIS_INTERNAL_BASE_URL}
      - REDIS_URL=${REDIS_INTERNAL_BASE_URL}
      - REDIS_PASSWORD_FILE=/run/secrets/redis_password
      - JOBS_ADD_COMPAT_ENABLED=false
    volumes:
      # Only data volumes - NO SOURCE CODE
      - api_logs:/app/logs
      - api_temp:/app/temp
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.api == true
        preferences:
          - spread: node.id
      resources:
        limits:
          cpus: "1"
          memory: 1G
        reservations:
          cpus: "0.25"
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
        monitor: 60s
        max_failure_ratio: 0.3
    healthcheck:
      test: ["CMD", "curl", "-f", "${API_HEALTHCHECK_URL}"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    depends_on:
      - redis

  # Vello Ingester Service - Production
  vello-ingester:
    image: vello-ingester:v0.7
    networks:
      - vello-overlay
    secrets:
      - database_url
      - redis_password
    environment:
      - DATABASE_URL_FILE=/run/secrets/database_url
      - REDIS_PASSWORD_FILE=/run/secrets/redis_password
      - REDIS_INTERNAL_BASE_URL=${REDIS_INTERNAL_BASE_URL}
      - CANONICAL_EVENTS_STREAM=agenticca:events:modelsgateway:v1
      - INGESTER_WRITE_ENABLED=true
      - INGESTER_DRY_RUN=false
      - INGESTER_CONSUMER_GROUP=vello-ingester
      - LOG_LEVEL=INFO
      - ENVIRONMENT=production
    volumes:
      - vello_ingester_logs:/app/logs
    deploy:
      replicas: 1
      placement:
        preferences:
          - spread: node.id
      resources:
        limits:
          cpus: "0.75"
          memory: 768M
        reservations:
          cpus: "0.25"
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
        monitor: 60s
        max_failure_ratio: 0.3
    depends_on:
      - redis
      - api

  # Web Admin (Admin Panel) - Production
  web-admin:
    image: vello-web-admin:v0.7
    networks:
      - vello-overlay
    ports:
      - target: ${WEB_ADMIN_INTERNAL_PORT}
        published: ${WEB_ADMIN_PUBLIC_PORT}
        protocol: tcp
        mode: host
    environment:
      - NODE_ENV=production
      - PORT=${WEB_ADMIN_INTERNAL_PORT}
      # IMPORTANT: Set the publicly reachable API base URL (e.g., https://api.agenticca.ai/vello)
      - VITE_API_PUBLIC_BASE_URL=${VITE_API_PUBLIC_BASE_URL:-https://api.example.com}
      - VITE_API_PUBLIC_WS_BASE_URL=${API_PUBLIC_WS_BASE_URL:-wss://api.example.com}
      # Disable background auto-refresh of Jobs in production unless intentionally enabled at build time
      - VITE_ENABLE_JOBS_AUTOREFRESH=false
    deploy:
      replicas: 1
      placement:
        preferences:
          - spread: node.id
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
        monitor: 60s
        max_failure_ratio: 0.3
    healthcheck:
      test: ["CMD", "curl", "-f", "http://127.0.0.1:${WEB_ADMIN_INTERNAL_PORT}/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s
    depends_on:
      - api

  # Web User (User Portal) - Production
  web-user:
    image: vello-web-user:v0.7
    networks:
      - vello-overlay
    ports:
      - target: 80
        published: ${WEB_USER_PUBLIC_PORT}
        protocol: tcp
        mode: host
    deploy:
      replicas: 1
      placement:
        preferences:
          - spread: node.id
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
        monitor: 60s
        max_failure_ratio: 0.3
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1/"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

volumes:
  redis_data:
    driver: local
  api_logs:
    driver: local
  api_temp:
    driver: local
  vello_ingester_logs:
    driver: local

networks:
  vello-overlay:
    driver: overlay
    attachable: true
    ipam:
      config:
        - subnet: 10.0.1.0/24
  agenticca-core:
    external: true
    name: agenticca-core
