##############################################################################
# Beconnect — docker-compose.yml
# Branch: robustez-do-beconnected
#
# Capacidade: 100k+ utilizadores simultâneos em todos os domínios
#
# ─── Topologia ───────────────────────────────────────────────────────────────
#  ENTRADA ÚNICA          → gateway (nginx, porta 8000)
#  LARAVEL (PHP-FPM ×4)  → app (replicas:4) + nginx
#  CART SERVICE (Node×4)  → cart-service (replicas:4)
#  PAYMENT SERVICE (C#)  → payment-service
#  STOCK SERVICE (Java)  → stock-service
#  POS PWA (React)       → pos-pwa
#  DB WRITE              → mysql-master
#  DB READ               → mysql-replica-1 + mysql-replica-2
#  QUERY ROUTER          → proxysql (porta 6033)
#  CACHE/FILAS           → redis-1..6 (Redis Cluster)
#  SEARCH                → meilisearch
#  WEBSOCKETS            → soketi
##############################################################################

x-app-base: &app-base
  build:
    context: .
    dockerfile: docker/php/Dockerfile
  restart: unless-stopped
  working_dir: /var/www
  volumes:
    - .:/var/www
    - ./docker/php/local.ini:/usr/local/etc/php/conf.d/local.ini
  networks:
    - beconnect
  depends_on:
    proxysql:
      condition: service_healthy
    redis-1:
      condition: service_healthy

x-redis-node: &redis-node
  image: redis:7-alpine
  restart: unless-stopped
  networks:
    - beconnect
  healthcheck:
    test: ["CMD", "redis-cli", "-p", "6379", "ping"]
    interval: 10s
    timeout: 3s
    retries: 5

services:

  # ═══════════════════════════════════════════════════════════════════════════
  # API GATEWAY — entrada única pública (porta 8000)
  # ═══════════════════════════════════════════════════════════════════════════
  gateway:
    image: nginx:1.25-alpine
    container_name: beconnect_gateway
    restart: unless-stopped
    ports:
      - "8000:80"
    volumes:
      - ./services/api-gateway/nginx.gateway.conf:/etc/nginx/conf.d/default.conf:ro
      - ./services/api-gateway/error.html:/etc/nginx/html/error.html:ro
    networks:
      - beconnect
    depends_on:
      - nginx
      - cart-service
      - payment-service
      - stock-service
      - pos-pwa
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1/gateway/health"]
      interval: 15s
      timeout: 5s
      retries: 3

  # ═══════════════════════════════════════════════════════════════════════════
  # LARAVEL OCTANE (RoadRunner) — 4 réplicas × auto workers (14 CPUs cada)
  # Workers PHP persistentes: sem bootstrap por pedido (~10ms vs ~100ms)
  # nginx usa Docker DNS least_conn para distribuir requisições → port 8080
  # ═══════════════════════════════════════════════════════════════════════════
  app:
    <<: *app-base
    environment:
      - PHP_OPCACHE_ENABLE=1
      - DB_HOST=proxysql
      - DB_PORT=6033
      - REDIS_CLUSTER_ENABLED=true
      - REDIS_NODE_1=redis-1
      - REDIS_NODE_2=redis-2
      - REDIS_NODE_3=redis-3
      - REDIS_NODE_4=redis-4
      - REDIS_NODE_5=redis-5
      - REDIS_NODE_6=redis-6
    deploy:
      replicas: 4
      resources:
        limits:
          cpus: "2.0"
          memory: 1G
        reservations:
          cpus: "0.5"
          memory: 512M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3

  # nginx para Laravel (load balance entre réplicas PHP-FPM)
  nginx:
    image: nginx:1.25-alpine
    container_name: beconnect_nginx
    restart: unless-stopped
    volumes:
      - .:/var/www
      - ./docker/nginx/beconnect.conf:/etc/nginx/conf.d/default.conf
    networks:
      - beconnect
    depends_on:
      - app

  # ═══════════════════════════════════════════════════════════════════════════
  # CART SERVICE — Node.js 20 + Redis Cluster | 4 réplicas
  # ═══════════════════════════════════════════════════════════════════════════
  cart-service:
    build:
      context: ./services/cart-service
      dockerfile: Dockerfile
    restart: unless-stopped
    environment:
      PORT:                3001
      REDIS_CLUSTER_NODES: "redis-1:6379,redis-2:6379,redis-3:6379,redis-4:6379,redis-5:6379,redis-6:6379"
      STOCK_SERVICE_URL:   http://stock-service:8081
      PAYMENT_SERVICE_URL: http://payment-service:8082
      ALLOWED_ORIGINS:     http://localhost:8000,http://gateway:80
      INTERNAL_API_KEY:    "${INTERNAL_API_KEY:-beconnect_internal}"
      LARAVEL_JWT_SECRET:  "${APP_KEY:-beconnect_dev_secret}"
      LOG_LEVEL:           info
      NODE_ENV:            production
    networks:
      - beconnect
    depends_on:
      redis-1:
        condition: service_healthy
      redis-cluster-init:
        condition: service_completed_successfully
    deploy:
      replicas: 4
      resources:
        limits:
          cpus: "0.75"
          memory: 384M
      restart_policy:
        condition: on-failure

  # ═══════════════════════════════════════════════════════════════════════════
  # PAYMENT SERVICE — C# .NET 8
  # ═══════════════════════════════════════════════════════════════════════════
  payment-service:
    build:
      context: ./services/payment-service
      dockerfile: Dockerfile
    container_name: beconnect_payment
    restart: unless-stopped
    environment:
      ASPNETCORE_URLS:        http://+:8082
      ASPNETCORE_ENVIRONMENT: Production
      DB_HOST:                proxysql
      DB_PORT:                6033
      DB_DATABASE:            "${DB_DATABASE:-beconnect}"
      DB_USERNAME:            "${DB_USERNAME:-beconnect}"
      DB_PASSWORD:            "${DB_PASSWORD:-beconnect_secret}"
      REDIS_HOST:             redis-1
      REDIS_PORT:             6379
      STOCK_SERVICE_URL:      http://stock-service:8081
      LARAVEL_APP_URL:        http://nginx:80
      INTERNAL_API_KEY:       "${INTERNAL_API_KEY:-beconnect_internal}"
      EMola__ApiUrl:          "${EMOLA_API_URL:-https://api.sandbox.emola.co.mz}"
      EMola__ApiKey:          "${EMOLA_API_KEY:-}"
      EMola__MerchantNumber:  "${EMOLA_MERCHANT_NUMBER:-}"
    networks:
      - beconnect
    depends_on:
      proxysql:
        condition: service_healthy
      redis-1:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    healthcheck:
      test: ["CMD", "bash", "-c", "echo > /dev/tcp/localhost/8082"]
      interval: 20s
      timeout: 10s
      retries: 3

  # ═══════════════════════════════════════════════════════════════════════════
  # STOCK SERVICE — Java Spring Boot 3 + JPA (pessimistic locking)
  # ═══════════════════════════════════════════════════════════════════════════
  stock-service:
    build:
      context: ./services/stock-service
      dockerfile: Dockerfile
    container_name: beconnect_stock
    restart: unless-stopped
    environment:
      DB_HOST:          proxysql
      DB_PORT:          6033
      DB_DATABASE:      "${DB_DATABASE:-beconnect}"
      DB_USERNAME:      "${DB_USERNAME:-beconnect}"
      DB_PASSWORD:      "${DB_PASSWORD:-beconnect_secret}"
      REDIS_HOST:       redis-1
      REDIS_PORT:       6379
      INTERNAL_API_KEY: "${INTERNAL_API_KEY:-beconnect_internal}"
      JAVA_TOOL_OPTIONS: "-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -XX:+UseG1GC"
    networks:
      - beconnect
    depends_on:
      proxysql:
        condition: service_healthy
      redis-1:
        condition: service_healthy
    deploy:
      resources:
        limits:
          cpus: "1.5"
          memory: 768M
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://localhost:8081/api/stock/health"]
      interval: 20s
      timeout: 10s
      retries: 5

  # ═══════════════════════════════════════════════════════════════════════════
  # POS PWA — React + IndexedDB (nginx estático)
  # ═══════════════════════════════════════════════════════════════════════════
  pos-pwa:
    build:
      context: ./services/pos-pwa
      dockerfile: Dockerfile
    container_name: beconnect_pos_pwa
    restart: unless-stopped
    networks:
      - beconnect
    deploy:
      resources:
        limits:
          cpus: "0.25"
          memory: 128M

  # ═══════════════════════════════════════════════════════════════════════════
  # MySQL MASTER — escritas (INSERT / UPDATE / DELETE)
  # ═══════════════════════════════════════════════════════════════════════════
  mysql-master:
    image: mysql:8.0
    container_name: beconnect_mysql_master
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: "${DB_PASSWORD:-beconnect_secret}"
      MYSQL_DATABASE:      "${DB_DATABASE:-beconnect}"
      MYSQL_USER:          "${DB_USERNAME:-beconnect}"
      MYSQL_PASSWORD:      "${DB_PASSWORD:-beconnect_secret}"
      MYSQL_SERVER_ID:     "1"
    volumes:
      - beconnect_mysql_master:/var/lib/mysql
      - ./docker/mysql/master.cnf:/etc/mysql/conf.d/custom.cnf:ro
    command: >
      --server-id=1
      --gtid-mode=ON
      --enforce-gtid-consistency=ON
      --log-bin=mysql-bin
      --binlog-format=ROW
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_PASSWORD:-beconnect_secret}"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - beconnect
    ports:
      - "3308:3306"

  # ═══════════════════════════════════════════════════════════════════════════
  # MySQL REPLICA 1 — leituras
  # ═══════════════════════════════════════════════════════════════════════════
  mysql-replica-1:
    image: mysql:8.0
    container_name: beconnect_mysql_replica1
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: "${DB_PASSWORD:-beconnect_secret}"
      MYSQL_DATABASE:      "${DB_DATABASE:-beconnect}"
      MYSQL_USER:          "${DB_USERNAME:-beconnect}"
      MYSQL_PASSWORD:      "${DB_PASSWORD:-beconnect_secret}"
    volumes:
      - beconnect_mysql_replica1:/var/lib/mysql
      - ./docker/mysql/replica.cnf:/etc/mysql/conf.d/custom.cnf:ro
    command: >
      --server-id=2
      --gtid-mode=ON
      --enforce-gtid-consistency=ON
      --log-bin=mysql-bin
      --log-slave-updates=ON
      --read-only=ON
      --super-read-only=ON
      --relay-log=relay-bin
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_PASSWORD:-beconnect_secret}"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - beconnect
    depends_on:
      mysql-master:
        condition: service_healthy

  # ═══════════════════════════════════════════════════════════════════════════
  # MySQL REPLICA 2 — leituras
  # ═══════════════════════════════════════════════════════════════════════════
  mysql-replica-2:
    image: mysql:8.0
    container_name: beconnect_mysql_replica2
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: "${DB_PASSWORD:-beconnect_secret}"
      MYSQL_DATABASE:      "${DB_DATABASE:-beconnect}"
      MYSQL_USER:          "${DB_USERNAME:-beconnect}"
      MYSQL_PASSWORD:      "${DB_PASSWORD:-beconnect_secret}"
    volumes:
      - beconnect_mysql_replica2:/var/lib/mysql
      - ./docker/mysql/replica.cnf:/etc/mysql/conf.d/custom.cnf:ro
    command: >
      --server-id=3
      --gtid-mode=ON
      --enforce-gtid-consistency=ON
      --log-bin=mysql-bin
      --log-slave-updates=ON
      --read-only=ON
      --super-read-only=ON
      --relay-log=relay-bin
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_PASSWORD:-beconnect_secret}"]
      interval: 10s
      timeout: 5s
      retries: 10
    networks:
      - beconnect
    depends_on:
      mysql-master:
        condition: service_healthy

  # ═══════════════════════════════════════════════════════════════════════════
  # MYSQL INIT — configura replicação GTID uma única vez
  # ═══════════════════════════════════════════════════════════════════════════
  mysql-replication-init:
    image: mysql:8.0
    container_name: beconnect_mysql_init
    restart: "no"
    volumes:
      - ./docker/mysql/init-replication.sh:/init-replication.sh:ro
    command: bash /init-replication.sh
    environment:
      MYSQL_ROOT_PASSWORD:  "${DB_PASSWORD:-beconnect_secret}"
      MYSQL_REPL_PASSWORD:  "${MYSQL_REPL_PASSWORD:-repl_secret_2025}"
    networks:
      - beconnect
    depends_on:
      mysql-master:
        condition: service_healthy
      mysql-replica-1:
        condition: service_healthy
      mysql-replica-2:
        condition: service_healthy

  # ═══════════════════════════════════════════════════════════════════════════
  # PROXYSQL — connection pooling + read/write splitting
  # porta 6033 → MySQL (para app, workers, serviços)
  # porta 6032 → admin ProxySQL
  # ═══════════════════════════════════════════════════════════════════════════
  proxysql:
    image: proxysql/proxysql:2.6.3
    container_name: beconnect_proxysql
    restart: unless-stopped
    volumes:
      - ./docker/proxysql/proxysql.cnf:/etc/proxysql.cnf:ro
      - beconnect_proxysql_data:/var/lib/proxysql
    ports:
      - "6032:6032"   # admin (monitorização)
    networks:
      - beconnect
    depends_on:
      mysql-master:
        condition: service_healthy
      mysql-replica-1:
        condition: service_healthy
      mysql-replica-2:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "mysqladmin", "-h", "127.0.0.1", "-P", "6032", "-u", "admin", "-padmin_proxysql_2025", "ping"]
      interval: 10s
      timeout: 5s
      retries: 10

  # ═══════════════════════════════════════════════════════════════════════════
  # REDIS CLUSTER — 6 nós (3 masters + 3 réplicas)
  # Capacidade: ~300k ops/s | 1.8GB RAM total
  # ═══════════════════════════════════════════════════════════════════════════
  redis-1:
    <<: *redis-node
    container_name: beconnect_redis_1
    volumes:
      - beconnect_redis_1:/data
      - ./docker/redis-cluster/redis.conf:/etc/redis/redis.conf:ro
    command: redis-server /etc/redis/redis.conf --cluster-announce-hostname redis-1
    ports:
      - "6379:6379"

  redis-2:
    <<: *redis-node
    container_name: beconnect_redis_2
    volumes:
      - beconnect_redis_2:/data
      - ./docker/redis-cluster/redis.conf:/etc/redis/redis.conf:ro
    command: redis-server /etc/redis/redis.conf --cluster-announce-hostname redis-2

  redis-3:
    <<: *redis-node
    container_name: beconnect_redis_3
    volumes:
      - beconnect_redis_3:/data
      - ./docker/redis-cluster/redis.conf:/etc/redis/redis.conf:ro
    command: redis-server /etc/redis/redis.conf --cluster-announce-hostname redis-3

  redis-4:
    <<: *redis-node
    container_name: beconnect_redis_4
    volumes:
      - beconnect_redis_4:/data
      - ./docker/redis-cluster/redis.conf:/etc/redis/redis.conf:ro
    command: redis-server /etc/redis/redis.conf --cluster-announce-hostname redis-4

  redis-5:
    <<: *redis-node
    container_name: beconnect_redis_5
    volumes:
      - beconnect_redis_5:/data
      - ./docker/redis-cluster/redis.conf:/etc/redis/redis.conf:ro
    command: redis-server /etc/redis/redis.conf --cluster-announce-hostname redis-5

  redis-6:
    <<: *redis-node
    container_name: beconnect_redis_6
    volumes:
      - beconnect_redis_6:/data
      - ./docker/redis-cluster/redis.conf:/etc/redis/redis.conf:ro
    command: redis-server /etc/redis/redis.conf --cluster-announce-hostname redis-6

  # ─── Redis Standalone — sessões, filas e rate-limiting ───────────────────────
  # Redis Cluster não suporta EVAL multi-slot nem SELECT (database != 0).
  # Laravel usa EVAL para filas (migrateExpiredJobs) e rate-limiting.
  # Nó standalone separado do cluster garante operações atómicas sem restrições.
  redis-standalone:
    image: redis:7-alpine
    container_name: beconnect_redis_standalone
    restart: unless-stopped
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru --appendonly yes
    volumes:
      - beconnect_redis_standalone:/data
    networks:
      - beconnect
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 5

  # Bootstrap do cluster — corre uma vez
  redis-cluster-init:
    image: redis:7-alpine
    container_name: beconnect_redis_cluster_init
    restart: "no"
    volumes:
      - ./docker/redis-cluster/create-cluster.sh:/create-cluster.sh:ro
    command: sh /create-cluster.sh
    networks:
      - beconnect
    depends_on:
      redis-1: { condition: service_healthy }
      redis-2: { condition: service_healthy }
      redis-3: { condition: service_healthy }
      redis-4: { condition: service_healthy }
      redis-5: { condition: service_healthy }
      redis-6: { condition: service_healthy }

  # ═══════════════════════════════════════════════════════════════════════════
  # MEILISEARCH — pesquisa de produtos
  # ═══════════════════════════════════════════════════════════════════════════
  meilisearch:
    image: getmeili/meilisearch:v1.7
    container_name: beconnect_meilisearch
    restart: unless-stopped
    ports:
      - "7700:7700"
    environment:
      MEILI_MASTER_KEY: "${MEILISEARCH_KEY:-beconnect_meili_secret}"
      MEILI_ENV:        production
    volumes:
      - beconnect_meili_data:/meili_data
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:7700/health"]
      interval: 15s
      timeout: 5s
      retries: 5
    networks:
      - beconnect

  # ═══════════════════════════════════════════════════════════════════════════
  # SOKETI — WebSockets tempo real (entregas, notificações)
  # ═══════════════════════════════════════════════════════════════════════════
  soketi:
    image: quay.io/soketi/soketi:latest-16-alpine
    container_name: beconnect_soketi
    restart: unless-stopped
    ports:
      - "6001:6001"
      - "9601:9601"
    environment:
      SOKETI_DEFAULT_APP_ID:        "${PUSHER_APP_ID:-beconnect}"
      SOKETI_DEFAULT_APP_KEY:       "${PUSHER_APP_KEY:-beconnect_key}"
      SOKETI_DEFAULT_APP_SECRET:    "${PUSHER_APP_SECRET:-beconnect_secret}"
      SOKETI_DEFAULT_APP_MAX_CONNS: "10000"
      SOKETI_DEBUG:                 "0"
    networks:
      - beconnect

  # ═══════════════════════════════════════════════════════════════════════════
  # MAILPIT — captura de emails em desenvolvimento (UI em :8025, SMTP em :1025)
  # ═══════════════════════════════════════════════════════════════════════════
  mailpit:
    image: axllent/mailpit:latest
    container_name: beconnect_mailpit
    restart: unless-stopped
    ports:
      - "8025:8025"   # UI web
      - "1025:1025"   # SMTP
    networks:
      - beconnect

  # ═══════════════════════════════════════════════════════════════════════════
  # phpMyAdmin — administração visual (acesso local)
  # ═══════════════════════════════════════════════════════════════════════════
  phpmyadmin:
    image: phpmyadmin:latest
    container_name: beconnect_phpmyadmin
    restart: unless-stopped
    ports:
      - "8084:80"
    environment:
      PMA_HOST:     proxysql
      PMA_PORT:     6033
      PMA_USER:     beconnect
      PMA_PASSWORD: "${DB_PASSWORD:-beconnect_secret}"
      UPLOAD_LIMIT: 50M
    networks:
      - beconnect
    depends_on:
      proxysql:
        condition: service_healthy

  # ═══════════════════════════════════════════════════════════════════════════
  # QUEUE WORKERS — Laravel (cada domínio tem o seu worker dedicado)
  # ═══════════════════════════════════════════════════════════════════════════
  queue_payments:
    <<: *app-base
    container_name: beconnect_queue_payments
    environment:
      DB_HOST: proxysql
      DB_PORT: "6033"
      REDIS_CLUSTER_ENABLED: "true"
      REDIS_NODE_1: redis-1
      REDIS_NODE_2: redis-2
      REDIS_NODE_3: redis-3
    command: php artisan queue:work --queue=payments --sleep=2 --tries=5 --backoff=30 --max-time=3600 --memory=256

  queue_delivery:
    <<: *app-base
    container_name: beconnect_queue_delivery
    environment:
      DB_HOST: proxysql
      DB_PORT: "6033"
      REDIS_CLUSTER_ENABLED: "true"
      REDIS_NODE_1: redis-1
      REDIS_NODE_2: redis-2
      REDIS_NODE_3: redis-3
    command: php artisan queue:work --queue=delivery,orders --sleep=2 --tries=3 --backoff=10 --max-time=3600 --memory=128

  queue_notifications:
    <<: *app-base
    container_name: beconnect_queue_notifications
    environment:
      DB_HOST: proxysql
      DB_PORT: "6033"
      REDIS_CLUSTER_ENABLED: "true"
      REDIS_NODE_1: redis-1
    command: php artisan queue:work --queue=notifications,search_index --sleep=5 --tries=3 --backoff=60 --max-time=3600 --memory=128

  queue_checkout:
    <<: *app-base
    container_name: beconnect_queue_checkout
    environment:
      DB_HOST: proxysql
      DB_PORT: "6033"
      REDIS_CLUSTER_ENABLED: "true"
      REDIS_NODE_1: redis-1
      REDIS_NODE_2: redis-2
      REDIS_NODE_3: redis-3
    command: php artisan queue:work --queue=checkout --sleep=1 --tries=3 --backoff=5 --max-time=3600 --memory=256

  queue:
    <<: *app-base
    container_name: beconnect_queue
    environment:
      DB_HOST: proxysql
      DB_PORT: "6033"
      REDIS_CLUSTER_ENABLED: "true"
      REDIS_NODE_1: redis-1
    command: php artisan queue:work --queue=default --sleep=3 --tries=3 --max-time=3600 --memory=128

  scheduler:
    <<: *app-base
    container_name: beconnect_scheduler
    environment:
      DB_HOST: proxysql
      DB_PORT: "6033"
    command: >
      sh -c "while true; do php artisan schedule:run --verbose --no-interaction; sleep 60; done"

  # build node (perfil separado)
  node:
    image: node:20-alpine
    container_name: beconnect_node
    working_dir: /var/www
    volumes:
      - .:/var/www
    command: sh -c "npm install --legacy-peer-deps && npm run build"
    profiles:
      - build

##############################################################################
# Networks
##############################################################################
networks:
  beconnect:
    driver: bridge

##############################################################################
# Volumes
##############################################################################
volumes:
  # MySQL (3 volumes separados — master não partilha dados com réplicas)
  beconnect_mysql_master:
  beconnect_mysql_replica1:
  beconnect_mysql_replica2:
  beconnect_proxysql_data:

  # Redis Cluster (6 volumes — cada nó tem o seu)
  beconnect_redis_1:
  beconnect_redis_2:
  beconnect_redis_3:
  beconnect_redis_4:
  beconnect_redis_5:
  beconnect_redis_6:
  beconnect_redis_standalone:

  # Serviços
  beconnect_meili_data:
