# ==============================================================================
# Multideal - Cloudflare Workers configuration
# ==============================================================================
#
# SECRETS SETUP - run these commands before first deploy (each prompts for value):
#
#   wrangler secret put DATABASE_URL              # Neon Postgres connection string
#   wrangler secret put SESSION_SECRET            # openssl rand -hex 32
#   wrangler secret put JWT_SECRET               # openssl rand -hex 32  (HS256 access-token signing)
#   wrangler secret put JWT_SECRET_PREV          # previous JWT_SECRET during key rotation (optional)
#   wrangler secret put QR_SECRET                 # openssl rand -hex 32
#   wrangler secret put GUEST_ACCESS_TOKEN_SECRET # openssl rand -hex 32
#   wrangler secret put PII_KEY                   # openssl rand -hex 32
#   wrangler secret put RESEND_API_KEY
#   wrangler secret put VAPID_PUBLIC_KEY
#   wrangler secret put VAPID_PRIVATE_KEY
#   wrangler secret put VAPID_SUBJECT
#   wrangler secret put IMAGES_ACCOUNT_HASH
#   wrangler secret put IMAGES_API_TOKEN
#   wrangler secret put SENTRY_DSN
#   wrangler secret put GOOGLE_API_KEY       # Google Gemini API key for AI agents
#   wrangler secret put STRIPE_SECRET_KEY        # Stripe platform secret key (sk_test_… / sk_live_…)
#   wrangler secret put STRIPE_WEBHOOK_SECRET    # Stripe webhook signing secret (whsec_…)
#
# For environment-specific secrets, append --env preview or --env production:
#   wrangler secret put DATABASE_URL --env production
#
# ==============================================================================

name = "multideal-preview"
# Custom entry: v13 adapter pattern — set main here, adapter uses this as the worker entry.
# src/worker.ts exports default { fetch } + RenderDO class.
main = "src/worker.ts"
compatibility_date = "2026-04-01"
compatibility_flags = ["nodejs_compat"]
workers_dev = true

[cache]
enabled = true

# Smart Placement — co-locates worker invocations near Neon (DATABASE_URL).
# Home renders 20+ Neon HTTP subrequests in parallel; from a far edge (e.g.
# BKK), each request burns 200-300ms of wall + CPU on TCP/TLS overhead alone,
# blowing the per-request budget (Error 1102). Smart mode lets CF re-route
# the invocation to a colo near the origin once it observes the latency
# pattern, dropping each subrequest's wall to ~10-20ms.
# Docs: https://developers.cloudflare.com/workers/configuration/smart-placement/
[placement]
mode = "smart"

# Custom domain (dev environment) - Cloudflare auto-provisions cert + DNS
[[routes]]
pattern = "dev.multi.deal"
custom_domain = true

# @astrojs/cloudflare 13 outputs client assets to dist/client/ (not dist/)
[assets]
directory = "./dist/client"
binding = "ASSETS"
run_worker_first = [
  "/*",
  "!/brand/*",
  "!/icons/*",
]

# --- Bindings (values come from `wrangler secret put` or env-specific overrides) ---
# R2 bucket for uploads, originals, QR PNGs.
[[r2_buckets]]
binding = "R2_BUCKET"
bucket_name = "multideal-uploads-preview"

# Cron triggers — dispatched via the custom entry's `scheduled` export.
# ACCOUNT LIMIT: 5 crons/account (Free). This 1 + vibeflare's 1 = 2/5.
# The v13 adapter carries [triggers] into the generated wrangler.json verbatim;
# deploy.sh asserts they survived (fail-closed gate), no injection needed.
[triggers]
crons = [
  "*/30 * * * *",
]

# Top-level is the PREVIEW env (deployed via `pnpm deploy:preview` or
# `wrangler deploy` with no --env flag). Wrangler 4's env block resolution
# was inconsistent when both [vars] and [env.preview] existed, so we
# collapsed preview to the default and kept [env.production] for prod.

# --- Durable Object namespace bindings (all classes hosted LOCALLY in this worker) ---
# Classes exported from src/worker.ts. No script_name = local binding. The v13
# adapter carries local DO bindings + [[migrations]] into the generated
# wrangler.json verbatim; deploy.sh asserts all 14 current bindings survived
# and that migration history still accounts for the dropped JobRunnerDO.
[[durable_objects.bindings]]
name = "DEAL_DO"
class_name = "DealDO"
[[durable_objects.bindings]]
name = "GROUP_DEAL_DO"
class_name = "GroupDealDO"
[[durable_objects.bindings]]
name = "PERSONAL_OFFER_DO"
class_name = "PersonalOfferDO"
[[durable_objects.bindings]]
name = "SCHEDULED_PUBLISH_DO"
class_name = "ScheduledPublishDO"
[[durable_objects.bindings]]
name = "GOLD_WINDOW_DO"
class_name = "GoldWindowDO"
[[durable_objects.bindings]]
name = "STRIPE_RECONCILE_DO"
class_name = "StripeReconcileDO"
[[durable_objects.bindings]]
name = "SUPPORT_TICKET_DO"
class_name = "SupportTicketDO"
[[durable_objects.bindings]]
name = "SUPPORT_CASE_DO"
class_name = "SupportCaseDO"
[[durable_objects.bindings]]
name = "USER_SESSION_DO"
class_name = "UserSessionDO"
[[durable_objects.bindings]]
name = "TOPIC_DO"
class_name = "TopicDO"
[[durable_objects.bindings]]
name = "CACHE_EPOCH_DO"
class_name = "CacheEpochDO"
# PasswordHashDO — argon2id hashing under the 30s DO CPU budget.
[[durable_objects.bindings]]
name = "PASSWORD_HASH_DO"
class_name = "PasswordHashDO"
# RenderDO — local DO class for SSR offload under 30s CPU budget.
# new_sqlite_classes is REQUIRED for Free plan DO registration.
[[durable_objects.bindings]]
name = "RENDER_DO"
class_name = "RenderDO"
[[durable_objects.bindings]]
name = "SCHEDULER_DO"
class_name = "SchedulerDO"
# No script_name — class is in this worker (multideal-preview), not web-do.

[[migrations]]
tag = "v1-render-do"
new_sqlite_classes = ["RenderDO"]

# All 13 DO classes originally merged in from web-do. Freeze this historical
# creation list for deploy-gate history checks; later deletions must use a new
# migration instead of mutating history.
[[migrations]]
tag = "v2-merge-do-host"
new_sqlite_classes = ["DealDO", "GroupDealDO", "PersonalOfferDO", "ScheduledPublishDO", "GoldWindowDO", "StripeReconcileDO", "SupportTicketDO", "SupportCaseDO", "UserSessionDO", "TopicDO", "JobRunnerDO", "CacheEpochDO", "PasswordHashDO"]

[[migrations]]
tag = "v3-drop-job-runner-do"
deleted_classes = ["JobRunnerDO"]

[[migrations]]
tag = "v4-scheduler-do"
new_sqlite_classes = ["SchedulerDO"]

# --- Cloudflare Queues ---
# Producers + consumers both on this worker now (merged from web-do). The custom
# entry's `queue` export (src/worker.ts) handles all consumers.
[[queues.producers]]
binding = "OUTBOX_QUEUE"
queue = "multideal-outbox-preview"

[[queues.producers]]
binding = "LLM_JOBS_QUEUE"
queue = "multideal-llm-jobs-preview"

# Translation jobs queue producer — consumer is local (this worker's `queue` export).
[[queues.producers]]
binding = "TRANSLATION_QUEUE"
queue = "multideal-translation-jobs-preview"

# Settlements queue producer — consumer is local (this worker's `queue` export).
[[queues.producers]]
binding = "SETTLEMENTS"
queue = "multideal-settlements-preview"

# --- Queue consumers (merged from web-do; custom entry hosts the `queue` export) ---
# The v13 adapter carries [[queues.consumers]] into the generated wrangler.json
# verbatim; deploy.sh asserts they survived (fail-closed gate), no injection needed.
[[queues.consumers]]
queue = "multideal-outbox-preview"
dead_letter_queue = "multideal-outbox-dlq-preview"
max_batch_size = 10
max_batch_timeout = 5
max_retries = 3

[[queues.consumers]]
queue = "multideal-llm-jobs-preview"
dead_letter_queue = "multideal-llm-jobs-dlq-preview"
max_batch_size = 10
max_batch_timeout = 5
max_retries = 3

# Translation jobs consumer — max_retries=0 because the consumer manages its own
# exponential-backoff retries via self-requeue (env.TRANSLATION_QUEUE.send + delaySeconds).
[[queues.consumers]]
queue = "multideal-translation-jobs-preview"
dead_letter_queue = "multideal-translation-jobs-dlq-preview"
max_batch_size = 10
max_batch_timeout = 5
max_retries = 0

# Settlements consumer — processes vendor payout settlement jobs.
[[queues.consumers]]
queue = "multideal-settlements-preview"
dead_letter_queue = "multideal-settlements-dlq-preview"
max_batch_size = 10
max_batch_timeout = 5
max_retries = 5
retry_delay = 60

# DLQ consumers (settlements + work-class) — log + ack for ops inspection; never
# throw (a throwing DLQ handler re-reads and re-fails every message).
[[queues.consumers]]
queue = "multideal-settlements-dlq-preview"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 1

[[queues.consumers]]
queue = "multideal-outbox-dlq-preview"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 1

[[queues.consumers]]
queue = "multideal-llm-jobs-dlq-preview"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 1

[[queues.consumers]]
queue = "multideal-translation-jobs-dlq-preview"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 1

[[analytics_engine_datasets]]
binding = "MULTIDEAL_DEV"
dataset = "multideal-dev"

[[d1_databases]]
binding = "EMAIL_MOCK_DB"
database_name = "multideal-email-mock"
database_id = "11435636-4db3-487b-9039-f07fbc5df022"

[vars]
ENVIRONMENT = "preview"
PUBLIC_SITE_URL = "https://dev.multi.deal"
PUBLIC_LAUNCH_STATE = "prelaunch"
LOG_LEVEL = "debug"
# Payment provider selector: "stripe" (live Stripe Connect) | "mock" (in-DB simulator, dev).
PAYMENT_PROVIDER = "stripe"
ALLOW_MOCK_INVOICING = "true"
PLATFORM_FEE_PCT = "10"
TRANSLATION_ENABLED = "true"
# Cloudflare account ID for Analytics Engine REST queries (set to your CF account ID).
CF_ACCOUNT_ID = "4c417b8bd59ae45e1fa25ca2d3c59806"
ADMIN_EMAIL = "admin@multi.deal"
# CF_AE_API_TOKEN — secret; set via: pnpm exec wrangler secret put CF_AE_API_TOKEN
STRIPE_PAYOUT_ENABLED = "true"
SHARE_BASE_URL = "https://multi.deal"
RENDER_DO_ENABLED = "true"

[observability]
enabled = true
head_sampling_rate = 1

# --- Production environment (app.multideal.co.il) ---
# Wrangler env blocks do NOT inherit top-level bindings — mirror preview exactly.
[env.production]
name = "multideal"
compatibility_date = "2026-04-01"
compatibility_flags = ["nodejs_compat"]
workers_dev = false

[env.production.observability]
enabled = true
head_sampling_rate = 1

[env.production.cache]
enabled = true

[env.production.placement]
mode = "smart"

[[env.production.routes]]
pattern = "app.multideal.co.il/*"
custom_domain = true

[env.production.assets]
directory = "./dist/client"
binding = "ASSETS"
run_worker_first = [
  "/*",
  "!/brand/*",
  "!/icons/*",
]

[[env.production.r2_buckets]]
binding = "R2_BUCKET"
bucket_name = "multideal-uploads-production"

[env.production.triggers]
crons = [
  "*/30 * * * *",
]

[[env.production.durable_objects.bindings]]
name = "DEAL_DO"
class_name = "DealDO"
[[env.production.durable_objects.bindings]]
name = "GROUP_DEAL_DO"
class_name = "GroupDealDO"
[[env.production.durable_objects.bindings]]
name = "PERSONAL_OFFER_DO"
class_name = "PersonalOfferDO"
[[env.production.durable_objects.bindings]]
name = "SCHEDULED_PUBLISH_DO"
class_name = "ScheduledPublishDO"
[[env.production.durable_objects.bindings]]
name = "GOLD_WINDOW_DO"
class_name = "GoldWindowDO"
[[env.production.durable_objects.bindings]]
name = "STRIPE_RECONCILE_DO"
class_name = "StripeReconcileDO"
[[env.production.durable_objects.bindings]]
name = "SUPPORT_TICKET_DO"
class_name = "SupportTicketDO"
[[env.production.durable_objects.bindings]]
name = "SUPPORT_CASE_DO"
class_name = "SupportCaseDO"
[[env.production.durable_objects.bindings]]
name = "USER_SESSION_DO"
class_name = "UserSessionDO"
[[env.production.durable_objects.bindings]]
name = "TOPIC_DO"
class_name = "TopicDO"
[[env.production.durable_objects.bindings]]
name = "CACHE_EPOCH_DO"
class_name = "CacheEpochDO"
[[env.production.durable_objects.bindings]]
name = "PASSWORD_HASH_DO"
class_name = "PasswordHashDO"
[[env.production.durable_objects.bindings]]
name = "RENDER_DO"
class_name = "RenderDO"
[[env.production.durable_objects.bindings]]
name = "SCHEDULER_DO"
class_name = "SchedulerDO"

[[env.production.migrations]]
tag = "v1-render-do"
new_sqlite_classes = ["RenderDO"]

[[env.production.migrations]]
tag = "v2-merge-do-host"
new_sqlite_classes = ["DealDO", "GroupDealDO", "PersonalOfferDO", "ScheduledPublishDO", "GoldWindowDO", "StripeReconcileDO", "SupportTicketDO", "SupportCaseDO", "UserSessionDO", "TopicDO", "JobRunnerDO", "CacheEpochDO", "PasswordHashDO"]

[[env.production.migrations]]
tag = "v3-drop-job-runner-do"
deleted_classes = ["JobRunnerDO"]

[[env.production.migrations]]
tag = "v4-scheduler-do"
new_sqlite_classes = ["SchedulerDO"]

[[env.production.queues.producers]]
binding = "OUTBOX_QUEUE"
queue = "multideal-outbox-production"

[[env.production.queues.producers]]
binding = "LLM_JOBS_QUEUE"
queue = "multideal-llm-jobs-production"

[[env.production.queues.producers]]
binding = "TRANSLATION_QUEUE"
queue = "multideal-translation-jobs-production"

[[env.production.queues.producers]]
binding = "SETTLEMENTS"
queue = "multideal-settlements-production"

[[env.production.queues.consumers]]
queue = "multideal-outbox-production"
dead_letter_queue = "multideal-outbox-dlq-production"
max_batch_size = 10
max_batch_timeout = 5
max_retries = 3

[[env.production.queues.consumers]]
queue = "multideal-llm-jobs-production"
dead_letter_queue = "multideal-llm-jobs-dlq-production"
max_batch_size = 10
max_batch_timeout = 5
max_retries = 3

[[env.production.queues.consumers]]
queue = "multideal-translation-jobs-production"
dead_letter_queue = "multideal-translation-jobs-dlq-production"
max_batch_size = 10
max_batch_timeout = 5
max_retries = 0

[[env.production.queues.consumers]]
queue = "multideal-settlements-production"
dead_letter_queue = "multideal-settlements-dlq-production"
max_batch_size = 10
max_batch_timeout = 5
max_retries = 5
retry_delay = 60

[[env.production.queues.consumers]]
queue = "multideal-settlements-dlq-production"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 1

[[env.production.queues.consumers]]
queue = "multideal-outbox-dlq-production"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 1

[[env.production.queues.consumers]]
queue = "multideal-llm-jobs-dlq-production"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 1

[[env.production.queues.consumers]]
queue = "multideal-translation-jobs-dlq-production"
max_batch_size = 10
max_batch_timeout = 30
max_retries = 1

[env.production.vars]
ENVIRONMENT = "production"
# Flip to https://www.multideal.co.il when marketplace traffic launches.
PUBLIC_SITE_URL = "https://app.multideal.co.il"
PUBLIC_LAUNCH_STATE = "prelaunch"
ALLOW_MOCK_INVOICING = "false"
LOG_LEVEL = "warn"
PAYMENT_PROVIDER = "stripe"
PLATFORM_FEE_PCT = "10"
TRANSLATION_ENABLED = "true"
CF_ACCOUNT_ID = "4c417b8bd59ae45e1fa25ca2d3c59806"
ADMIN_EMAIL = "admin@multi.deal"
STRIPE_PAYOUT_ENABLED = "false"
SHARE_BASE_URL = "https://multi.deal"
RENDER_DO_ENABLED = "true"
