# ============================================================
# WhatsApp Ticketing System — All-in-One Docker Compose
# Supabase self-hosted stack embedded
# Ref: https://supabase.com/docs/guides/self-hosting/docker
#
# Browser-facing URLs are derived from .env:
#   PUBLIC_SITE_HOST — hostname or IP the browser uses (default localhost)
#   *_HOST_PORT — published ports (unique per stack on one machine)
# Override full URLs only when you need https, custom domains, or non-default paths.
# Project name `wa-ticketing` isolates networks/volumes from other Compose projects.
# ============================================================

name: wa-ticketing

services:

  # ----------------------------------------------------------
  # APPLICATION SERVICES
  # ----------------------------------------------------------

  frontend:
    build:
      context: .
      dockerfile: apps/frontend/Dockerfile
      args:
        # Build-time fallback for local dev parity; Docker runtime uses runtime-config.js from env below
        VITE_API_URL: ${VITE_API_URL:-http://${PUBLIC_SITE_HOST:-localhost}:${API_HOST_PORT:-3001}}
        VITE_WS_URL: ${VITE_WS_URL:-ws://${PUBLIC_SITE_HOST:-localhost}:${API_HOST_PORT:-3001}}
    environment:
      PUBLIC_SITE_HOST: ${PUBLIC_SITE_HOST:-localhost}
      API_HOST_PORT: ${API_HOST_PORT:-3001}
      PUBLIC_HTTP_SCHEME: ${PUBLIC_HTTP_SCHEME:-http}
    ports:
      # Host port → container 3000. Set FRONTEND_PORT in .env (legacy: FRONTEND_HOST_PORT still works).
      - "${FRONTEND_PORT:-${FRONTEND_HOST_PORT:-3000}}:3000"
    depends_on:
      - api
    restart: unless-stopped

  api:
    build:
      context: .
      dockerfile: apps/api/Dockerfile
    ports:
      - "${API_HOST_PORT:-3001}:3001"
    environment:
      NODE_ENV: production
      PORT: 3001
      # Comma-separated browser origins; leave unset for allow-all (dev only). Production: https://app.example.com
      # Default: allow only this stack’s UI origin; unset in .env to fall back to allow-all (see api CORS)
      CORS_ORIGIN: ${CORS_ORIGIN:-http://${PUBLIC_SITE_HOST:-localhost}:${FRONTEND_PORT:-${FRONTEND_HOST_PORT:-3000}}}
      DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/postgres
      JWT_SECRET: ${JWT_SECRET}
      JWT_EXPIRY: ${JWT_EXPIRY:-8h}
      WA_WORKER_URL: http://whatsapp:3002
      ADMIN_EMAIL: ${ADMIN_EMAIL:-admin@example.com}
      ADMIN_PASSWORD: ${ADMIN_PASSWORD:-Admin123!}
      OPENAI_API_KEY: ${OPENAI_API_KEY:-}
      OPENAI_BASE_URL: ${OPENAI_BASE_URL:-}
      # Email channels + staff alerts (optional)
      SMTP_HOST: ${SMTP_HOST:-}
      SMTP_PORT: ${SMTP_PORT:-587}
      SMTP_SECURE: ${SMTP_SECURE:-false}
      SMTP_USER: ${SMTP_USER:-}
      SMTP_PASS: ${SMTP_PASS:-}
      SMTP_FROM: ${SMTP_FROM:-}
      # Signup verification links — same origin users use to open the app
      FRONTEND_URL: ${FRONTEND_URL:-http://${PUBLIC_SITE_HOST:-localhost}:${FRONTEND_PORT:-${FRONTEND_HOST_PORT:-3000}}}
      # Optional: require Cloudflare Turnstile on first signup chat message
      TURNSTILE_SECRET_KEY: ${TURNSTILE_SECRET_KEY:-}
      # SMS alerts via Twilio (optional)
      TWILIO_ACCOUNT_SID: ${TWILIO_ACCOUNT_SID:-}
      TWILIO_AUTH_TOKEN: ${TWILIO_AUTH_TOKEN:-}
      TWILIO_FROM_NUMBER: ${TWILIO_FROM_NUMBER:-}
    depends_on:
      db:
        condition: service_healthy
    restart: unless-stopped

  whatsapp:
    build:
      context: .
      dockerfile: services/whatsapp/Dockerfile
    # No host port: the API reaches this service at http://whatsapp:3002 on the compose network.
    # Publishing 3002:3002 breaks `docker compose up` when another app already uses host port 3002.
    # To debug from the host, add `ports: ["3002:3002"]` (or another host port) in docker-compose.override.yml.
    volumes:
      - wa_auth:/tmp/wa_ticketing_auth
    environment:
      NODE_ENV: production
      PORT: 3002
      API_INTERNAL_URL: http://api:3001
    depends_on:
      api:
        condition: service_started
    restart: unless-stopped

  # ----------------------------------------------------------
  # SUPABASE SELF-HOSTED STACK
  # ----------------------------------------------------------

  studio:
    image: supabase/studio:latest
    restart: unless-stopped
    ports:
      # Override STUDIO_HOST_PORT when another Supabase Studio already uses 8002 on the host
      - "${STUDIO_HOST_PORT:-8002}:3000"
    environment:
      STUDIO_PG_META_URL: http://meta:8080
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      DEFAULT_ORGANIZATION_NAME: ${STUDIO_DEFAULT_ORGANIZATION}
      DEFAULT_PROJECT_NAME: ${STUDIO_DEFAULT_PROJECT}
      SUPABASE_URL: http://kong:8000
      # Kong’s public URL on the host (must match KONG_HTTP_HOST_PORT)
      SUPABASE_PUBLIC_URL: ${SUPABASE_PUBLIC_URL:-http://${PUBLIC_SITE_HOST:-localhost}:${KONG_HTTP_HOST_PORT:-8001}}
      SUPABASE_ANON_KEY: ${ANON_KEY}
      SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
      AUTH_JWT_SECRET: ${JWT_SECRET}
      LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN}
      NEXT_PUBLIC_ENABLE_LOGS: "true"
      NEXT_ANALYTICS_BACKEND_PROVIDER: postgres
    depends_on:
      analytics:
        condition: service_healthy
    # Default image healthcheck hits 127.0.0.1:3000; Next.js 16 binds to the container hostname only.
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "node -e \"fetch('http://'+require('os').hostname()+':3000').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\"",
        ]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 40s

  kong:
    image: kong:2.8.1
    restart: unless-stopped
    ports:
      - "${KONG_HTTP_HOST_PORT:-8001}:8000/tcp"
      # Host 8443 is often taken (IIS, other stacks); override KONG_HTTPS_HOST_PORT if needed
      - "${KONG_HTTPS_HOST_PORT:-8444}:8443/tcp"
    volumes:
      - ./volumes/api/kong.yml:/home/kong/kong.yml:ro
    environment:
      KONG_DATABASE: "off"
      KONG_DECLARATIVE_CONFIG: /home/kong/kong.yml
      KONG_DNS_ORDER: LAST,A,CNAME
      KONG_PLUGINS: request-transformer,cors,key-auth,acl,basic-auth
      KONG_NGINX_PROXY_PROXY_BUFFER_SIZE: 160k
      KONG_NGINX_PROXY_PROXY_BUFFERS: 64 160k
      SUPABASE_ANON_KEY: ${ANON_KEY}
      SUPABASE_SERVICE_KEY: ${SERVICE_ROLE_KEY}
      DASHBOARD_USERNAME: ${DASHBOARD_USERNAME}
      DASHBOARD_PASSWORD: ${DASHBOARD_PASSWORD}

  auth:
    image: supabase/gotrue:v2.164.0
    restart: unless-stopped
    environment:
      GOTRUE_API_HOST: 0.0.0.0
      GOTRUE_API_PORT: 9999
      API_EXTERNAL_URL: ${API_EXTERNAL_URL:-http://${PUBLIC_SITE_HOST:-localhost}:${KONG_HTTP_HOST_PORT:-8001}}
      GOTRUE_DB_DRIVER: postgres
      GOTRUE_DB_DATABASE_URL: postgres://supabase_auth_admin:${POSTGRES_PASSWORD}@db:5432/postgres
      GOTRUE_SITE_URL: ${SITE_URL:-http://${PUBLIC_SITE_HOST:-localhost}:${FRONTEND_PORT:-${FRONTEND_HOST_PORT:-3000}}}
      # Extra redirect origins beyond GOTRUE_SITE_URL (comma-separated); often empty for single-origin stacks
      GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS:-}
      GOTRUE_DISABLE_SIGNUP: "false"
      GOTRUE_JWT_ADMIN_ROLES: service_role
      GOTRUE_JWT_AUD: authenticated
      GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
      GOTRUE_JWT_EXP: ${JWT_EXPIRY:-28800}
      GOTRUE_JWT_SECRET: ${JWT_SECRET}
      GOTRUE_MAILER_AUTOCONFIRM: "true"
    depends_on:
      db:
        condition: service_healthy

  rest:
    image: postgrest/postgrest:v12.2.3
    restart: unless-stopped
    environment:
      PGRST_DB_URI: postgres://authenticator:${POSTGRES_PASSWORD}@db:5432/postgres
      PGRST_DB_SCHEMAS: ${PGRST_DB_SCHEMAS:-public}
      PGRST_DB_ANON_ROLE: anon
      PGRST_JWT_SECRET: ${JWT_SECRET}
      PGRST_DB_USE_LEGACY_GUCS: "false"
      PGRST_APP_SETTINGS_JWT_SECRET: ${JWT_SECRET}
      PGRST_APP_SETTINGS_JWT_EXP: ${JWT_EXPIRY:-3600}
    depends_on:
      db:
        condition: service_healthy

  realtime:
    image: supabase/realtime:v2.34.47
    restart: unless-stopped
    environment:
      PORT: 4000
      DB_HOST: db
      DB_PORT: 5432
      DB_USER: supabase_admin
      DB_PASSWORD: ${POSTGRES_PASSWORD}
      DB_NAME: postgres
      DB_AFTER_CONNECT_QUERY: SET search_path TO _realtime
      DB_ENC_KEY: supabaserealtime
      API_JWT_SECRET: ${JWT_SECRET}
      FLY_ALLOC_ID: fly123
      FLY_APP_NAME: realtime
      # Realtime 2.34+ expects APP_NAME (see upstream Supabase docker-compose)
      APP_NAME: realtime
      SECRET_KEY_BASE: ${SECRET_KEY_BASE}
      ERL_AFLAGS: -proto_dist inet_tcp
      ENABLE_TAILSCALE: "false"
      DNS_NODES: "''"
      # Avoid bash `RLIMIT_NOFILE: unbound variable` in older realtime images on Docker Desktop
      RLIMIT_NOFILE: "10000"
    depends_on:
      db:
        condition: service_healthy

  storage:
    image: supabase/storage-api:v1.11.13
    restart: unless-stopped
    volumes:
      - ./volumes/storage:/var/lib/storage:z
    environment:
      ANON_KEY: ${ANON_KEY}
      SERVICE_KEY: ${SERVICE_ROLE_KEY}
      POSTGREST_URL: http://rest:3000
      PGRST_JWT_SECRET: ${JWT_SECRET}
      DATABASE_URL: postgres://supabase_storage_admin:${POSTGRES_PASSWORD}@db:5432/postgres
      FILE_SIZE_LIMIT: 52428800
      STORAGE_BACKEND: file
      FILE_STORAGE_BACKEND_PATH: /var/lib/storage
      TENANT_ID: stub
      REGION: stub
      GLOBAL_S3_BUCKET: stub
      ENABLE_IMAGE_TRANSFORMATION: "true"
      IMGPROXY_URL: http://imgproxy:5001
    depends_on:
      db:
        condition: service_healthy
      rest:
        condition: service_started

  imgproxy:
    image: darthsim/imgproxy:v3.8.0
    restart: unless-stopped
    environment:
      IMGPROXY_BIND: ":5001"
      IMGPROXY_LOCAL_FILESYSTEM_ROOT: /
      IMGPROXY_USE_ETAG: "true"
      IMGPROXY_ENABLE_WEBP_DETECTION: "true"

  meta:
    image: supabase/postgres-meta:v0.84.2
    restart: unless-stopped
    environment:
      PG_META_PORT: 8080
      PG_META_DB_HOST: db
      PG_META_DB_PORT: 5432
      PG_META_DB_NAME: postgres
      PG_META_DB_USER: supabase_admin
      PG_META_DB_PASSWORD: ${POSTGRES_PASSWORD}
      PG_META_CRYPTO_KEY: ${PG_META_CRYPTO_KEY}
    depends_on:
      db:
        condition: service_healthy

  functions:
    image: supabase/edge-runtime:v1.67.4
    restart: unless-stopped
    volumes:
      - ./volumes/functions:/home/deno/functions:Z
    environment:
      JWT_SECRET: ${JWT_SECRET}
      SUPABASE_URL: http://kong:8000
      SUPABASE_ANON_KEY: ${ANON_KEY}
      SUPABASE_SERVICE_ROLE_KEY: ${SERVICE_ROLE_KEY}
      SUPABASE_DB_URL: postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/postgres
      VERIFY_JWT: "false"
    # Without this, edge-runtime prints --help and exits (see upstream Supabase docker-compose)
    command: ["start", "--main-service", "/home/deno/functions/main"]
    depends_on:
      analytics:
        condition: service_healthy
      kong:
        condition: service_started

  analytics:
    image: supabase/logflare:1.4.0
    restart: unless-stopped
    ports:
      # Logflare / analytics UI — raise ANALYTICS_HOST_PORT if 14000 is taken
      - "${ANALYTICS_HOST_PORT:-14000}:4000"
    environment:
      LOGFLARE_NODE_HOST: 127.0.0.1
      DB_USERNAME: supabase_admin
      # Logflare uses DB _supabase + schema _analytics (not a DB named _analytics)
      DB_DATABASE: _supabase
      DB_HOSTNAME: db
      DB_PORT: 5432
      DB_PASSWORD: ${POSTGRES_PASSWORD}
      DB_SCHEMA: _analytics
      LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN}
      LOGFLARE_PRIVATE_ACCESS_TOKEN: ${LOGFLARE_PRIVATE_ACCESS_TOKEN}
      # Must be quoted strings — YAML bools trigger BigQuery/gcloud.json path in Logflare
      LOGFLARE_SINGLE_TENANT: "true"
      LOGFLARE_SUPABASE_MODE: "true"
      LOGFLARE_MIN_CLUSTER_SIZE: "1"
      POSTGRES_BACKEND_URL: postgresql://supabase_admin:${POSTGRES_PASSWORD}@db:5432/_supabase
      POSTGRES_BACKEND_SCHEMA: _analytics
      LOGFLARE_FEATURE_FLAG_OVERRIDE: multibackend=true
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "http://localhost:4000/health"]
      timeout: 5s
      interval: 5s
      retries: 10

  db:
    image: supabase/postgres:15.8.1.060
    restart: unless-stopped
    ports:
      # Direct Postgres on host — use POSTGRES_HOST_PORT when 5432 is already in use
      - "${POSTGRES_HOST_PORT:-5432}:5432"
    volumes:
      # _supabase DB must exist before logs.sql creates _analytics schema (matches upstream Supabase docker)
      - ./volumes/db/_supabase.sql:/docker-entrypoint-initdb.d/migrations/97-_supabase.sql:Z
      - ./volumes/db/realtime.sql:/docker-entrypoint-initdb.d/migrations/99-realtime.sql:Z
      - ./volumes/db/webhooks.sql:/docker-entrypoint-initdb.d/init-scripts/98-webhooks.sql:Z
      - ./volumes/db/roles.sql:/docker-entrypoint-initdb.d/init-scripts/99-roles.sql:Z
      - ./volumes/db/jwt.sql:/docker-entrypoint-initdb.d/init-scripts/99-jwt.sql:Z
      - ./volumes/db/logs.sql:/docker-entrypoint-initdb.d/migrations/99-logs.sql:Z
      - ./volumes/db/pooler.sql:/docker-entrypoint-initdb.d/migrations/99-pooler.sql:Z
      # App migrations — must live directly in migrations/ so migrate.sh picks them up
      - ./supabase/migrations/001_users.sql:/docker-entrypoint-initdb.d/migrations/001_users.sql:Z
      - ./supabase/migrations/002_contacts.sql:/docker-entrypoint-initdb.d/migrations/002_contacts.sql:Z
      - ./supabase/migrations/003_channels.sql:/docker-entrypoint-initdb.d/migrations/003_channels.sql:Z
      - ./supabase/migrations/004_tickets.sql:/docker-entrypoint-initdb.d/migrations/004_tickets.sql:Z
      - ./supabase/migrations/005_messages.sql:/docker-entrypoint-initdb.d/migrations/005_messages.sql:Z
      - ./supabase/migrations/006_audit_logs.sql:/docker-entrypoint-initdb.d/migrations/006_audit_logs.sql:Z
      - ./supabase/migrations/007_channel_settings_ticket_intake.sql:/docker-entrypoint-initdb.d/migrations/007_channel_settings_ticket_intake.sql:Z
      - ./supabase/migrations/008_platform_settings.sql:/docker-entrypoint-initdb.d/migrations/008_platform_settings.sql:Z
      - ./supabase/migrations/009_companies_and_portal.sql:/docker-entrypoint-initdb.d/migrations/009_companies_and_portal.sql:Z
      - ./supabase/migrations/010_contact_phone_portal_length.sql:/docker-entrypoint-initdb.d/migrations/010_contact_phone_portal_length.sql:Z
      - ./supabase/migrations/011_users_display_name.sql:/docker-entrypoint-initdb.d/migrations/011_users_display_name.sql:Z
      - ./supabase/migrations/012_web_chat_channel_always_on.sql:/docker-entrypoint-initdb.d/migrations/012_web_chat_channel_always_on.sql:Z
      - ./supabase/migrations/013_email_channels_alerts.sql:/docker-entrypoint-initdb.d/migrations/013_email_channels_alerts.sql:Z
      - pgdata:/var/lib/postgresql/data:Z
    environment:
      POSTGRES_HOST: /var/run/postgresql
      PGPORT: 5432
      POSTGRES_PORT: 5432
      PGPASSWORD: ${POSTGRES_PASSWORD}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      PGDATABASE: postgres
      POSTGRES_DB: postgres
      JWT_SECRET: ${JWT_SECRET}
      JWT_EXP: 3600
    healthcheck:
      test: pg_isready -U postgres -h localhost
      interval: 5s
      timeout: 5s
      retries: 10

  vector:
    image: timberio/vector:0.28.1-alpine
    restart: unless-stopped
    volumes:
      - ./volumes/logs/vector.yml:/etc/vector/vector.yml:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      LOGFLARE_PUBLIC_ACCESS_TOKEN: ${LOGFLARE_PUBLIC_ACCESS_TOKEN}
    depends_on:
      analytics:
        condition: service_healthy

  supavisor:
    image: supabase/supavisor:2.4.5
    restart: unless-stopped
    ports:
      # Supavisor pooler — override POOLER_HOST_PORT if another stack uses 6543
      - "${POOLER_HOST_PORT:-6543}:6543"
    environment:
      PORT: 4000
      POSTGRES_PORT: 5432
      POSTGRES_DB: postgres
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      DATABASE_URL: ecto://supabase_admin:${POSTGRES_PASSWORD}@db:5432/postgres
      CLUSTER_POSTGRES: "true"
      SECRET_KEY_BASE: ${SECRET_KEY_BASE}
      VAULT_ENC_KEY: ${VAULT_ENC_KEY}
      API_JWT_SECRET: ${JWT_SECRET}
      METRICS_JWT_SECRET: ${JWT_SECRET}
      REGION: local
      ERL_AFLAGS: -proto_dist inet_tcp
    depends_on:
      db:
        condition: service_healthy

# ----------------------------------------------------------
# VOLUMES
# ----------------------------------------------------------

volumes:
  pgdata:
    driver: local
  wa_auth:
    driver: local
