#!/usr/bin/env node
import { writeFileSync } from 'node:fs'

const defaults = {
  output: 'deploy/gateway-appliance/.env',
  mode: 'remote',
  telegramMode: 'polling',
  provider: 'telegram',
}

function usage() {
  return `Open Cowork Cloud Channel Gateway appliance setup

Usage:
  node scripts/gateway-appliance-setup.mjs --mode remote --cloud-url https://cowork.example.com --service-token TOKEN --telegram-bot-token TOKEN --output .env.gateway
  node scripts/gateway-appliance-setup.mjs --mode local --telegram-bot-token TOKEN --output .env.gateway

Options:
  --mode remote|local
  --cloud-url URL
  --service-token TOKEN
  --provider telegram|fake
  --telegram-mode polling|webhook
  --telegram-bot-token TOKEN
  --telegram-webhook-secret SECRET
  --public-url https://gateway.example.com
  --admin-token TOKEN
  --output PATH
  --print
  --allow-secret-print
`
}

function parseArgs(argv) {
  const parsed = { ...defaults, print: false, allowSecretPrint: false }
  for (let index = 0; index < argv.length; index += 1) {
    const arg = argv[index]
    if (arg === '--help' || arg === '-h') {
      parsed.help = true
      continue
    }
    if (arg === '--print') {
      parsed.print = true
      continue
    }
    if (arg === '--allow-secret-print') {
      parsed.allowSecretPrint = true
      continue
    }
    if (!arg.startsWith('--')) throw new Error(`Unexpected argument: ${arg}`)
    const key = arg.slice(2).replace(/-([a-z])/g, (_match, letter) => letter.toUpperCase())
    const value = argv[index + 1]
    if (!value || value.startsWith('--')) throw new Error(`${arg} requires a value.`)
    parsed[key] = value
    index += 1
  }
  return parsed
}

function assertChoice(value, choices, label) {
  if (!choices.includes(value)) throw new Error(`${label} must be one of: ${choices.join(', ')}`)
}

function validate(config) {
  assertChoice(config.mode, ['remote', 'local'], '--mode')
  assertChoice(config.provider, ['telegram', 'fake'], '--provider')
  assertChoice(config.telegramMode, ['polling', 'webhook'], '--telegram-mode')
  if (config.mode === 'remote' && !config.cloudUrl) throw new Error('--cloud-url is required for remote mode.')
  if (config.mode === 'remote' && !config.serviceToken) throw new Error('--service-token is required for remote mode.')
  if (config.provider === 'telegram' && !config.telegramBotToken) throw new Error('--telegram-bot-token is required for Telegram.')
  if (config.provider === 'telegram' && config.telegramMode === 'webhook') {
    if (!config.publicUrl) throw new Error('--public-url is required for Telegram webhook mode.')
    if (!config.telegramWebhookSecret) throw new Error('--telegram-webhook-secret is required for Telegram webhook mode.')
    let publicUrl
    try {
      publicUrl = new URL(config.publicUrl)
    } catch {
      throw new Error('--public-url must be a valid HTTPS URL for Telegram webhook mode.')
    }
    if (publicUrl.protocol !== 'https:') throw new Error('--public-url must be HTTPS for Telegram webhook mode.')
    if (!config.adminToken) throw new Error('--admin-token is required when exposing webhook mode publicly.')
  }
}

function quote(value) {
  return String(value ?? '').replaceAll('\n', '')
}

function renderEnv(config) {
  const localCloudUrl = 'http://open-cowork-cloud:8787'
  const cloudUrl = config.mode === 'local' ? localCloudUrl : config.cloudUrl
  const serviceToken = config.mode === 'local'
    ? (config.serviceToken || 'replace-with-local-cloud-gateway-token')
    : config.serviceToken
  const publicWebhookMode = config.provider === 'telegram' && config.telegramMode === 'webhook'
  const lines = [
    '# Generated by scripts/gateway-appliance-setup.mjs.',
    '# Store this file outside git and rotate every token if it is exposed.',
    `OPEN_COWORK_CLOUD_BASE_URL=${quote(cloudUrl)}`,
    `OPEN_COWORK_GATEWAY_SERVICE_TOKEN=${quote(serviceToken)}`,
    `OPEN_COWORK_GATEWAY_ADMIN_TOKEN=${quote(config.adminToken || '')}`,
    `OPEN_COWORK_GATEWAY_HOST=${publicWebhookMode ? '0.0.0.0' : '127.0.0.1'}`,
    'OPEN_COWORK_GATEWAY_PORT=8790',
    `OPEN_COWORK_GATEWAY_PUBLIC_URL=${quote(config.publicUrl || '')}`,
    'OPEN_COWORK_GATEWAY_PRODUCT_MODE=cloud_channel',
    'OPEN_COWORK_GATEWAY_MODE=self-host',
    'OPEN_COWORK_GATEWAY_METRICS_ENABLED=false',
    'OPEN_COWORK_GATEWAY_DIAGNOSTICS_ENABLED=false',
    'OPEN_COWORK_GATEWAY_TRUST_PROXY_HEADERS=false',
    'OPEN_COWORK_GATEWAY_TRUSTED_PROXY_CIDRS=',
    'OPEN_COWORK_GATEWAY_ENABLE_FAKE_PROVIDER=false',
  ]
  if (config.provider === 'fake') {
    lines.push(
      'OPEN_COWORK_GATEWAY_ENABLE_FAKE_PROVIDER=true',
      'OPEN_COWORK_GATEWAY_FAKE_CHANNEL_BINDING_ID=fake-binding',
    )
  } else {
    lines.push(
      `OPEN_COWORK_GATEWAY_TELEGRAM_BOT_TOKEN=${quote(config.telegramBotToken)}`,
      'OPEN_COWORK_GATEWAY_TELEGRAM_CHANNEL_BINDING_ID=telegram',
      `OPEN_COWORK_GATEWAY_TELEGRAM_MODE=${config.telegramMode}`,
      `OPEN_COWORK_GATEWAY_TELEGRAM_PUBLIC_URL=${quote(config.publicUrl || '')}`,
      `OPEN_COWORK_GATEWAY_TELEGRAM_WEBHOOK_SECRET=${quote(config.telegramWebhookSecret || '')}`,
    )
  }
  return `${lines.join('\n')}\n`
}

function hasSensitivePrintArgs(config) {
  return Boolean(
    config.serviceToken
    || config.adminToken
    || config.telegramBotToken
    || config.telegramWebhookSecret
  )
}

function renderNextSteps(config) {
  const composeFile = config.mode === 'local'
    ? 'docker-compose.cloud-gateway.yml'
    : 'docker-compose.gateway-remote.yml'
  const modeLabel = config.mode === 'local' ? 'local all-in-one' : 'remote Cloud'
  const providerStep = config.provider === 'telegram'
    ? config.telegramMode === 'webhook'
      ? 'Set the Telegram webhook to https://gateway.example.com/webhooks/telegram through your reverse proxy.'
      : 'Start the service, then message the Telegram bot directly to create or continue a cloud thread.'
    : 'Use the fake provider only on loopback for local smoke tests.'
  return [
    `Mode: ${modeLabel}`,
    `Compose file: ${composeFile}`,
    '1. Create or copy the gateway service token from the Open Cowork Cloud admin UI.',
    '2. Save the generated env file with 0600 permissions and keep it out of git.',
    '3. Start the gateway with Docker Compose, systemd, launchd, or your process supervisor.',
    `4. ${providerStep}`,
    '5. Check /health and /ready locally; expose /metrics or /diagnostics only with the admin token.',
  ].join('\n')
}

function main() {
  const config = parseArgs(process.argv.slice(2))
  if (config.help) {
    process.stdout.write(usage())
    return
  }
  validate(config)
  const envText = renderEnv(config)
  const nextSteps = renderNextSteps(config)
  if (config.print) {
    if (hasSensitivePrintArgs(config) && !config.allowSecretPrint) {
      throw new Error('--print would write live gateway secrets to stdout. Write an env file instead, or add --allow-secret-print only for an intentionally private terminal.')
    }
    process.stdout.write(envText)
    process.stdout.write('\n')
    process.stdout.write(nextSteps)
    process.stdout.write('\n')
    return
  }
  writeFileSync(config.output, envText, { mode: 0o600 })
  process.stdout.write(`Wrote ${config.output}\n\n${nextSteps}\n`)
}

try {
  main()
} catch (error) {
  process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n\n${usage()}`)
  process.exit(1)
}
