# ==============================================
# Mosaic Stack - Docker Swarm Deployment
# ==============================================
#
# IMPORTANT: Docker Swarm does NOT support docker-compose profiles
# To disable services (e.g., for external alternatives), manually comment them out
#
# Current Configuration:
#   - PostgreSQL: ENABLED (internal)
#   - Valkey: ENABLED (internal)
#   - Coordinator: ENABLED (internal)
#   - OpenBao: DISABLED (must use standalone - see docker-compose.openbao.yml)
#   - Authentik: DISABLED (commented out - using external OIDC)
#   - Ollama: DISABLED (commented out - using external Ollama)
#
# For detailed deployment instructions, see:
#   docs/SWARM-DEPLOYMENT.md
#
# Quick Start:
#   1. cp .env.swarm.example .env
#   2. nano .env  # Configure environment
#   3. ./scripts/deploy-swarm.sh mosaic
#   4. Initialize OpenBao manually (see docs/SWARM-DEPLOYMENT.md)
#
# ==============================================

services:
  # ======================
  # PostgreSQL Database
  # ======================
  postgres:
    image: git.mosaicstack.dev/mosaic/stack-postgres:${IMAGE_TAG:-latest}
    environment:
      POSTGRES_USER: ${POSTGRES_USER:-mosaic}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-mosaic_dev_password}
      POSTGRES_DB: ${POSTGRES_DB:-mosaic}
      POSTGRES_SHARED_BUFFERS: ${POSTGRES_SHARED_BUFFERS:-256MB}
      POSTGRES_EFFECTIVE_CACHE_SIZE: ${POSTGRES_EFFECTIVE_CACHE_SIZE:-1GB}
      POSTGRES_MAX_CONNECTIONS: ${POSTGRES_MAX_CONNECTIONS:-100}
    volumes:
      - postgres_data:/var/lib/postgresql/data
      # Note: init-scripts bind mount removed for Portainer compatibility
      # Init scripts are baked into the postgres image at build time
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-mosaic} -d ${POSTGRES_DB:-mosaic}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s
    networks:
      - internal
    deploy:
      restart_policy:
        condition: on-failure

  # ======================
  # Valkey Cache
  # ======================
  valkey:
    image: valkey/valkey:8-alpine
    command:
      - valkey-server
      - --maxmemory ${VALKEY_MAXMEMORY:-256mb}
      - --maxmemory-policy noeviction
      - --appendonly yes
    volumes:
      - valkey_data:/data
    healthcheck:
      test: ["CMD", "valkey-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 10s
    networks:
      - internal
    deploy:
      restart_policy:
        condition: on-failure

  # ======================
  # OpenBao Secrets Vault - COMMENTED OUT
  # ======================
  # IMPORTANT: OpenBao CANNOT run in swarm mode due to port binding conflicts.
  # Deploy OpenBao as a standalone container instead:
  #   docker compose -f docker-compose.openbao.yml up -d
  #
  # Alternative: Use external HashiCorp Vault or managed secrets service
  #
  # openbao:
  #   image: git.mosaicstack.dev/mosaic/stack-openbao:${IMAGE_TAG:-latest}
  #   environment:
  #     OPENBAO_ADDR: ${OPENBAO_ADDR:-http://0.0.0.0:8200}
  #     OPENBAO_DEV_ROOT_TOKEN_ID: ${OPENBAO_DEV_ROOT_TOKEN_ID:-root}
  #   volumes:
  #     - openbao_data:/openbao/data
  #     - openbao_logs:/openbao/logs
  #     - openbao_init:/openbao/init
  #   cap_add:
  #     - IPC_LOCK
  #   healthcheck:
  #     test:
  #       ["CMD", "wget", "--spider", "--quiet", "http://localhost:8200/v1/sys/health?standbyok=true"]
  #     interval: 10s
  #     timeout: 5s
  #     retries: 5
  #     start_period: 30s
  #   networks:
  #     - internal
  #   deploy:
  #     restart_policy:
  #       condition: on-failure

  # ======================
  # Authentik - COMMENTED OUT (Using External Authentik)
  # ======================
  # Uncomment these services if you want to run Authentik internally
  # For external Authentik, configure OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET in .env
  #
  # authentik-postgres:
  #   image: postgres:17.7-alpine3.22
  #   environment:
  #     POSTGRES_USER: ${AUTHENTIK_POSTGRES_USER:-authentik}
  #     POSTGRES_PASSWORD: ${AUTHENTIK_POSTGRES_PASSWORD:-authentik_password}
  #     POSTGRES_DB: ${AUTHENTIK_POSTGRES_DB:-authentik}
  #   volumes:
  #     - authentik_postgres_data:/var/lib/postgresql/data
  #   healthcheck:
  #     test: ["CMD-SHELL", "pg_isready -U ${AUTHENTIK_POSTGRES_USER:-authentik}"]
  #     interval: 10s
  #     timeout: 5s
  #     retries: 5
  #     start_period: 20s
  #   networks:
  #     - internal
  #   deploy:
  #     restart_policy:
  #       condition: on-failure
  #
  # authentik-redis:
  #   image: valkey/valkey:8-alpine
  #   command: valkey-server --save 60 1 --loglevel warning
  #   volumes:
  #     - authentik_redis_data:/data
  #   healthcheck:
  #     test: ["CMD", "valkey-cli", "ping"]
  #     interval: 10s
  #     timeout: 5s
  #     retries: 5
  #     start_period: 10s
  #   networks:
  #     - internal
  #   deploy:
  #     restart_policy:
  #       condition: on-failure
  #
  # authentik-server:
  #   image: ghcr.io/goauthentik/server:2024.12.1
  #   command: server
  #   environment:
  #     AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:-change-this-to-a-random-secret}
  #     AUTHENTIK_ERROR_REPORTING__ENABLED: ${AUTHENTIK_ERROR_REPORTING:-false}
  #     AUTHENTIK_POSTGRESQL__HOST: authentik-postgres
  #     AUTHENTIK_POSTGRESQL__PORT: 5432
  #     AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRES_DB:-authentik}
  #     AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRES_USER:-authentik}
  #     AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRES_PASSWORD:-authentik_password}
  #     AUTHENTIK_REDIS__HOST: authentik-redis
  #     AUTHENTIK_REDIS__PORT: 6379
  #     AUTHENTIK_BOOTSTRAP_PASSWORD: ${AUTHENTIK_BOOTSTRAP_PASSWORD:-admin}
  #     AUTHENTIK_BOOTSTRAP_EMAIL: ${AUTHENTIK_BOOTSTRAP_EMAIL:-admin@localhost}
  #     AUTHENTIK_COOKIE_DOMAIN: ${AUTHENTIK_COOKIE_DOMAIN:-.mosaicstack.dev}
  #   volumes:
  #     - authentik_media:/media
  #     - authentik_templates:/templates
  #   healthcheck:
  #     test:
  #       [
  #         "CMD",
  #         "wget",
  #         "--no-verbose",
  #         "--tries=1",
  #         "--spider",
  #         "http://localhost:9000/-/health/live/",
  #       ]
  #     interval: 30s
  #     timeout: 10s
  #     retries: 3
  #     start_period: 90s
  #   networks:
  #     - internal
  #     - traefik-public
  #   deploy:
  #     restart_policy:
  #       condition: on-failure
  #     labels:
  #       - "traefik.enable=true"
  #       - "traefik.http.routers.mosaic-auth.rule=Host(`${MOSAIC_AUTH_DOMAIN:-auth.mosaicstack.dev}`)"
  #       - "traefik.http.routers.mosaic-auth.entrypoints=web"
  #       - "traefik.http.services.mosaic-auth.loadbalancer.server.port=9000"
  #
  # authentik-worker:
  #   image: ghcr.io/goauthentik/server:2024.12.1
  #   command: worker
  #   environment:
  #     AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY:-change-this-to-a-random-secret}
  #     AUTHENTIK_ERROR_REPORTING__ENABLED: ${AUTHENTIK_ERROR_REPORTING:-false}
  #     AUTHENTIK_POSTGRESQL__HOST: authentik-postgres
  #     AUTHENTIK_POSTGRESQL__PORT: 5432
  #     AUTHENTIK_POSTGRESQL__NAME: ${AUTHENTIK_POSTGRES_DB:-authentik}
  #     AUTHENTIK_POSTGRESQL__USER: ${AUTHENTIK_POSTGRES_USER:-authentik}
  #     AUTHENTIK_POSTGRESQL__PASSWORD: ${AUTHENTIK_POSTGRES_PASSWORD:-authentik_password}
  #     AUTHENTIK_REDIS__HOST: authentik-redis
  #     AUTHENTIK_REDIS__PORT: 6379
  #   volumes:
  #     - authentik_media:/media
  #     - authentik_certs:/certs
  #     - authentik_templates:/templates
  #   networks:
  #     - internal
  #   deploy:
  #     restart_policy:
  #       condition: on-failure

  # ======================
  # Ollama (Optional AI Service)
  # ======================
  # ollama:
  #   image: ollama/ollama:latest
  #   volumes:
  #     - ollama_data:/root/.ollama
  #   healthcheck:
  #     test: ["CMD", "curl", "-f", "http://localhost:11434/api/tags"]
  #     interval: 30s
  #     timeout: 10s
  #     retries: 3
  #     start_period: 60s
  #   networks:
  #     - internal
  #   deploy:
  #     restart_policy:
  #       condition: on-failure

  # ======================
  # Mosaic Coordinator
  # ======================
  coordinator:
    image: git.mosaicstack.dev/mosaic/stack-coordinator:${IMAGE_TAG:-latest}
    environment:
      GITEA_WEBHOOK_SECRET: ${GITEA_WEBHOOK_SECRET}
      GITEA_URL: ${GITEA_URL:-https://git.mosaicstack.dev}
      ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
      LOG_LEVEL: ${LOG_LEVEL:-info}
      HOST: 0.0.0.0
      PORT: 8000
      COORDINATOR_POLL_INTERVAL: ${COORDINATOR_POLL_INTERVAL:-5.0}
      COORDINATOR_MAX_CONCURRENT_AGENTS: ${COORDINATOR_MAX_CONCURRENT_AGENTS:-10}
      COORDINATOR_ENABLED: ${COORDINATOR_ENABLED:-true}
      # Telemetry (task completion tracking & predictions)
      MOSAIC_TELEMETRY_ENABLED: ${MOSAIC_TELEMETRY_ENABLED:-false}
      MOSAIC_TELEMETRY_SERVER_URL: ${MOSAIC_TELEMETRY_SERVER_URL:-https://tel-api.mosaicstack.dev}
      MOSAIC_TELEMETRY_API_KEY: ${MOSAIC_TELEMETRY_API_KEY:-}
      MOSAIC_TELEMETRY_INSTANCE_ID: ${MOSAIC_TELEMETRY_INSTANCE_ID:-}
      MOSAIC_TELEMETRY_DRY_RUN: ${MOSAIC_TELEMETRY_DRY_RUN:-false}
    healthcheck:
      test:
        [
          "CMD",
          "python",
          "-c",
          "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')",
        ]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 5s
    networks:
      - internal
    deploy:
      restart_policy:
        condition: on-failure

  # ======================
  # Mosaic API
  # ======================
  api:
    image: git.mosaicstack.dev/mosaic/stack-api:${IMAGE_TAG:-latest}
    environment:
      NODE_ENV: production
      PORT: ${API_PORT:-3001}
      API_HOST: ${API_HOST:-0.0.0.0}
      DATABASE_URL: postgresql://${POSTGRES_USER:-mosaic}:${POSTGRES_PASSWORD:-mosaic_dev_password}@postgres:5432/${POSTGRES_DB:-mosaic}
      VALKEY_URL: redis://valkey:6379
      OIDC_ISSUER: ${OIDC_ISSUER}
      OIDC_CLIENT_ID: ${OIDC_CLIENT_ID}
      OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET}
      OIDC_REDIRECT_URI: ${OIDC_REDIRECT_URI:-http://localhost:3001/auth/callback}
      JWT_SECRET: ${JWT_SECRET:-change-this-to-a-random-secret}
      JWT_EXPIRATION: ${JWT_EXPIRATION:-24h}
      BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET}
      OLLAMA_ENDPOINT: ${OLLAMA_ENDPOINT:-http://ollama:11434}
      OPENBAO_ADDR: ${OPENBAO_ADDR:-http://openbao:8200}
      ENCRYPTION_KEY: ${ENCRYPTION_KEY}
      # Telemetry (task completion tracking & predictions)
      MOSAIC_TELEMETRY_ENABLED: ${MOSAIC_TELEMETRY_ENABLED:-false}
      MOSAIC_TELEMETRY_SERVER_URL: ${MOSAIC_TELEMETRY_SERVER_URL:-https://tel-api.mosaicstack.dev}
      MOSAIC_TELEMETRY_API_KEY: ${MOSAIC_TELEMETRY_API_KEY:-}
      MOSAIC_TELEMETRY_INSTANCE_ID: ${MOSAIC_TELEMETRY_INSTANCE_ID:-}
      MOSAIC_TELEMETRY_DRY_RUN: ${MOSAIC_TELEMETRY_DRY_RUN:-false}
    healthcheck:
      test:
        [
          "CMD-SHELL",
          'node -e "require(''http'').get(''http://localhost:${API_PORT:-3001}/health'', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"',
        ]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    networks:
      - internal
      - traefik-public
    deploy:
      restart_policy:
        condition: on-failure
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.mosaic-api.rule=Host(`${MOSAIC_API_DOMAIN:-api.mosaicstack.dev}`)"
        - "traefik.http.routers.mosaic-api.entrypoints=web"
        - "traefik.http.services.mosaic-api.loadbalancer.server.port=${API_PORT:-3001}"

  # ======================
  # Mosaic Orchestrator
  # ======================
  orchestrator:
    image: git.mosaicstack.dev/mosaic/stack-orchestrator:${IMAGE_TAG:-latest}
    user: "1000:1000"
    environment:
      NODE_ENV: production
      ORCHESTRATOR_PORT: 3001
      VALKEY_URL: redis://valkey:6379
      CLAUDE_API_KEY: ${CLAUDE_API_KEY}
      DOCKER_SOCKET: /var/run/docker.sock
      GIT_USER_NAME: "Mosaic Orchestrator"
      GIT_USER_EMAIL: "orchestrator@mosaicstack.dev"
      KILLSWITCH_ENABLED: "true"
      SANDBOX_ENABLED: "true"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - orchestrator_workspace:/workspace
    healthcheck:
      test:
        ["CMD-SHELL", "wget --no-verbose --tries=1 --spider http://localhost:3001/health || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    networks:
      - internal
    # Note: security_opt not supported in swarm mode
    # Security hardening done via cap_drop/cap_add
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    tmpfs:
      - /tmp:noexec,nosuid,size=100m
    deploy:
      restart_policy:
        condition: on-failure

  # ======================
  # Mosaic Web
  # ======================
  web:
    image: git.mosaicstack.dev/mosaic/stack-web:${IMAGE_TAG:-latest}
    environment:
      NODE_ENV: production
      PORT: ${WEB_PORT:-3000}
      NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://localhost:3001}
    healthcheck:
      test:
        [
          "CMD-SHELL",
          'node -e "require(''http'').get(''http://localhost:${WEB_PORT:-3000}'', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"',
        ]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    networks:
      - traefik-public
    deploy:
      restart_policy:
        condition: on-failure
      labels:
        - "traefik.enable=true"
        - "traefik.http.routers.mosaic-web.rule=Host(`${MOSAIC_WEB_DOMAIN:-mosaic.mosaicstack.dev}`)"
        - "traefik.http.routers.mosaic-web.entrypoints=web"
        - "traefik.http.services.mosaic-web.loadbalancer.server.port=${WEB_PORT:-3000}"

# ======================
# Volumes
# ======================
volumes:
  postgres_data:
  valkey_data:
  # OpenBao volumes - commented out (using standalone deployment)
  # openbao_data:
  # openbao_logs:
  # openbao_init:
  # Authentik volumes - commented out (using external Authentik)
  # authentik_postgres_data:
  # authentik_redis_data:
  # authentik_media:
  # authentik_certs:
  # authentik_templates:
  # Ollama volume - commented out (using external Ollama)
  # ollama_data:
  orchestrator_workspace:

# ======================
# Networks
# ======================
networks:
  internal:
    driver: overlay
  traefik-public:
    external: true
