version: '3.9'

services:
  # ============================================================
  # Infrastructure Services
  # ============================================================

  postgres:
    image: postgres:15-alpine
    container_name: genex-postgres
    environment:
      POSTGRES_USER: genex
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: genex
    ports:
      - "127.0.0.1:45432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./migrations:/docker-entrypoint-initdb.d
    command:
      - "postgres"
      - "-c"
      - "wal_level=logical"           # Required for Debezium CDC
      - "-c"
      - "max_replication_slots=10"    # CDC connector slots
      - "-c"
      - "max_wal_senders=10"          # WAL sender processes
      - "-c"
      # WAL 安全阀: 限制单个 replication slot 最多保留 10GB WAL
      # 超过此值 PostgreSQL 会使 slot 失效, 防止磁盘被吃满
      # (rwadurian 事故: 无此限制, 单个 stuck slot 累积 305GB WAL)
      - "max_slot_wal_keep_size=10GB"
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U genex"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - genex-network

  redis:
    image: redis:7-alpine
    container_name: genex-redis
    ports:
      - "127.0.0.1:46379:6379"
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - genex-network

  kafka:
    image: confluentinc/cp-kafka:7.7.0
    container_name: genex-kafka
    environment:
      # KRaft mode (no Zookeeper needed since Kafka 3.5+)
      KAFKA_NODE_ID: 1
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093,PLAINTEXT_HOST://0.0.0.0:29092
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:49092
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,CONTROLLER:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
      KAFKA_LOG_DIRS: /var/lib/kafka/data
      CLUSTER_ID: "genex-kafka-cluster-001"
    ports:
      - "127.0.0.1:49092:9092"
      - "127.0.0.1:49093:29092"
    volumes:
      - kafka_data:/var/lib/kafka/data
    healthcheck:
      test: ["CMD", "kafka-broker-api-versions", "--bootstrap-server", "localhost:9092"]
      interval: 10s
      timeout: 10s
      retries: 5
    restart: unless-stopped
    networks:
      - genex-network

  # MinIO Object Storage (S3-compatible, multi-region replication support)
  minio:
    image: minio/minio:latest
    container_name: genex-minio
    environment:
      MINIO_ROOT_USER: genex-admin
      MINIO_ROOT_PASSWORD: genex-minio-secret
    ports:
      - "127.0.0.1:49000:9000"     # S3 API
      - "127.0.0.1:49001:9001"     # Console UI
    volumes:
      - minio_data:/data
    command: server /data --console-address ":9001"
    healthcheck:
      test: ["CMD", "mc", "ready", "local"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
    networks:
      - genex-network

  # MinIO bucket initialization
  minio-init:
    image: minio/mc:latest
    container_name: genex-minio-init
    depends_on:
      minio:
        condition: service_healthy
    entrypoint: >
      /bin/sh -c "
      mc alias set genex http://minio:9000 genex-admin genex-minio-secret;
      mc mb --ignore-existing genex/kyc-documents;
      mc mb --ignore-existing genex/coupon-images;
      mc mb --ignore-existing genex/issuer-documents;
      mc mb --ignore-existing genex/sar-reports;
      mc mb --ignore-existing genex/avatars;
      mc mb --ignore-existing genex/exports;
      mc mb --ignore-existing genex/app-releases;
      mc anonymous set download genex/coupon-images;
      mc anonymous set download genex/avatars;
      echo 'MinIO buckets initialized';
      "
    networks:
      - genex-network

  # Debezium Kafka Connect (CDC - Change Data Capture)
  # 版本说明: 必须使用 2.5.1+ (修复 DBZ-7316: searchWalPosition 不推进 confirmed_flush_lsn, 导致 WAL 无限积压)
  kafka-connect:
    image: debezium/connect:2.5.4.Final
    container_name: genex-kafka-connect
    environment:
      BOOTSTRAP_SERVERS: kafka:9092
      GROUP_ID: genex-connect
      CONFIG_STORAGE_TOPIC: genex_connect_configs
      OFFSET_STORAGE_TOPIC: genex_connect_offsets
      STATUS_STORAGE_TOPIC: genex_connect_statuses
      CONFIG_STORAGE_REPLICATION_FACTOR: 1
      OFFSET_STORAGE_REPLICATION_FACTOR: 1
      STATUS_STORAGE_REPLICATION_FACTOR: 1
      # Offset 提交频率: 默认 60s, 缩短至 10s 以减少重启后重复处理窗口
      OFFSET_FLUSH_INTERVAL_MS: 10000
      OFFSET_FLUSH_TIMEOUT_MS: 5000
    ports:
      - "127.0.0.1:48083:8083"
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:8083/ || exit 1"]
      interval: 30s
      timeout: 10s
      start_period: 30s
      retries: 3
    depends_on:
      kafka:
        condition: service_healthy
      postgres:
        condition: service_healthy
    restart: unless-stopped
    networks:
      - genex-network

  # Kong API Gateway (DB-less / Declarative mode)
  kong:
    image: kong:3.5
    container_name: genex-kong
    environment:
      KONG_DATABASE: "off"
      KONG_DECLARATIVE_CONFIG: /etc/kong/kong.yml
      KONG_PROXY_ACCESS_LOG: /dev/stdout
      KONG_ADMIN_ACCESS_LOG: /dev/stdout
      KONG_PROXY_ERROR_LOG: /dev/stderr
      KONG_ADMIN_ERROR_LOG: /dev/stderr
      KONG_ADMIN_LISTEN: 0.0.0.0:8001
      KONG_PROXY_LISTEN: 0.0.0.0:8080
    ports:
      - "48080:8080"    # Proxy (frontend connects here)
      - "127.0.0.1:48001:8001"    # Admin API
    volumes:
      - ./kong/kong.yml:/etc/kong/kong.yml:ro
    healthcheck:
      test: ["CMD", "kong", "health"]
      interval: 10s
      timeout: 10s
      retries: 5
    restart: unless-stopped
    networks:
      - genex-network

  # ============================================================
  # NestJS Services (9)
  # ============================================================

  user-service:
    build:
      context: .
      dockerfile: services/user-service/Dockerfile
    container_name: genex-user-service
    ports:
      - "4001:3001"
    environment:
      - NODE_ENV=development
      - PORT=3001
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - KAFKA_BROKERS=kafka:9092
      - JWT_ACCESS_SECRET=dev-access-secret-change-in-production
      - JWT_ACCESS_EXPIRY=15m
      - JWT_REFRESH_SECRET=dev-refresh-secret-change-in-production
      - JWT_REFRESH_EXPIRY=7d
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      kafka:
        condition: service_healthy
    networks:
      - genex-network

  issuer-service:
    build:
      context: .
      dockerfile: services/issuer-service/Dockerfile
    container_name: genex-issuer-service
    ports:
      - "4002:3002"
    environment:
      - NODE_ENV=development
      - PORT=3002
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - KAFKA_BROKERS=kafka:9092
      - JWT_ACCESS_SECRET=dev-access-secret-change-in-production
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      kafka:
        condition: service_healthy
    networks:
      - genex-network

  clearing-service:
    build:
      context: .
      dockerfile: services/clearing-service/Dockerfile
    container_name: genex-clearing-service
    ports:
      - "4004:3004"
    environment:
      - NODE_ENV=development
      - PORT=3004
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - KAFKA_BROKERS=kafka:9092
      - JWT_ACCESS_SECRET=dev-access-secret-change-in-production
    depends_on:
      postgres:
        condition: service_healthy
      kafka:
        condition: service_healthy
    networks:
      - genex-network

  compliance-service:
    build:
      context: .
      dockerfile: services/compliance-service/Dockerfile
    container_name: genex-compliance-service
    ports:
      - "4005:3005"
    environment:
      - NODE_ENV=development
      - PORT=3005
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - KAFKA_BROKERS=kafka:9092
      - JWT_ACCESS_SECRET=dev-access-secret-change-in-production
    depends_on:
      postgres:
        condition: service_healthy
      kafka:
        condition: service_healthy
    networks:
      - genex-network

  notification-service:
    build:
      context: .
      dockerfile: services/notification-service/Dockerfile
    container_name: genex-notification-service
    ports:
      - "4008:3008"
    environment:
      - NODE_ENV=development
      - PORT=3008
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - KAFKA_BROKERS=kafka:9092
      - REDIS_HOST=redis
      - REDIS_PORT=6379
    depends_on:
      kafka:
        condition: service_healthy
    networks:
      - genex-network

  # ============================================================
  # Telemetry Service (NestJS :3011) - User presence, events, DAU, Prometheus metrics
  # ============================================================

  telemetry-service:
    build:
      context: .
      dockerfile: services/telemetry-service/Dockerfile
    container_name: genex-telemetry-service
    ports:
      - "4011:3011"
    environment:
      - NODE_ENV=development
      - PORT=3011
      - SERVICE_NAME=telemetry-service
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - KAFKA_BROKERS=kafka:9092
      - JWT_ACCESS_SECRET=dev-access-secret-change-in-production
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      kafka:
        condition: service_healthy
    networks:
      - genex-network

  # ============================================================
  # Admin Service (NestJS :3012) - App version management, OTA updates
  # ============================================================

  admin-service:
    build:
      context: .
      dockerfile: services/admin-service/Dockerfile
    container_name: genex-admin-service
    ports:
      - "4012:3012"
    environment:
      - NODE_ENV=development
      - PORT=3012
      - SERVICE_NAME=admin-service
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - MINIO_ENDPOINT=minio
      - MINIO_PORT=9000
      - MINIO_ACCESS_KEY=genex-admin
      - MINIO_SECRET_KEY=genex-minio-secret
      - MINIO_BUCKET=app-releases
      - JWT_ACCESS_SECRET=dev-access-secret-change-in-production
    depends_on:
      postgres:
        condition: service_healthy
      minio:
        condition: service_healthy
    networks:
      - genex-network

  # ============================================================
  # Go Services (3)
  # ============================================================

  trading-service:
    build:
      context: ./services/trading-service
      dockerfile: Dockerfile
    container_name: genex-trading-service
    ports:
      - "4003:3003"
    environment:
      - PORT=3003
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - KAFKA_BROKERS=kafka:9092
      - JWT_ACCESS_SECRET=dev-access-secret-change-in-production
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
      kafka:
        condition: service_healthy
    networks:
      - genex-network

  translate-service:
    build:
      context: ./services/translate-service
      dockerfile: Dockerfile
    container_name: genex-translate-service
    ports:
      - "4007:3007"
    environment:
      - PORT=3007
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - JWT_ACCESS_SECRET=dev-access-secret-change-in-production
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - genex-network

  chain-indexer:
    build:
      context: ./services/chain-indexer
      dockerfile: Dockerfile
    container_name: genex-chain-indexer
    ports:
      - "4009:3009"
    environment:
      - PORT=3009
      - KAFKA_BROKERS=kafka:9092
      - RPC_URL=http://172.17.0.1:8545
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
    depends_on:
      kafka:
        condition: service_healthy
    networks:
      - genex-network

  # ============================================================
  # Auth Service (NestJS) - JWT dual-token, registration, login, RBAC
  # ============================================================

  auth-service:
    build:
      context: .
      dockerfile: services/auth-service/Dockerfile
    container_name: genex-auth-service
    ports:
      - "4010:3010"
    environment:
      - NODE_ENV=development
      - PORT=3010
      - SERVICE_NAME=auth-service
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - KAFKA_BROKERS=kafka:9092
      - JWT_ACCESS_SECRET=dev-access-secret-change-in-production
      - JWT_ACCESS_EXPIRY=15m
      - JWT_REFRESH_SECRET=dev-refresh-secret-change-in-production
      - JWT_REFRESH_EXPIRY=7d
      - SMS_ENABLED=${SMS_ENABLED:-false}
      - SMS_CODE_EXPIRE_SECONDS=300
      - SMS_DAILY_LIMIT=10
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    networks:
      - genex-network

  # ============================================================
  # AI Service (NestJS) - Anti-corruption layer to external AI agent cluster
  # ============================================================

  ai-service:
    build:
      context: .
      dockerfile: services/ai-service/Dockerfile
    container_name: genex-ai-service
    ports:
      - "4006:3006"
    environment:
      - NODE_ENV=development
      - PORT=3006
      - SERVICE_NAME=ai-service
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_USERNAME=genex
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_NAME=genex
      - KAFKA_BROKERS=kafka:9092
      - REDIS_HOST=redis
      - REDIS_PORT=6379
      - AI_AGENT_CLUSTER_URL=http://external-ai-agents:8000
      - AI_AGENT_API_KEY=your-ai-agent-api-key
      - AI_AGENT_TIMEOUT=30000
    depends_on:
      postgres:
        condition: service_healthy
      kafka:
        condition: service_healthy
    networks:
      - genex-network

volumes:
  postgres_data:
  redis_data:
  kafka_data:
  minio_data:

networks:
  genex-network:
    driver: bridge
