import type { NextConfig } from 'next'

const isProd = process.env.NODE_ENV === 'production'
// Cloudflare Turnstile (intake bot check) loads only when a site key is configured.
const turnstile = process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY ? ' https://challenges.cloudflare.com' : ''
// Google reCAPTCHA v3 loads its API, a versioned script, and a verification frame.
const recaptchaScript = process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY ? ' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/' : ''
const recaptchaFrame = process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY ? ' https://www.google.com/recaptcha/ https://recaptcha.google.com/recaptcha/' : ''
const recaptchaConnect = process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY ? ' https://www.google.com/recaptcha/' : ''

/**
 * Security headers (2026-09-22, SOC 2 / ISO 27001 hardening). Applied to every
 * response. HSTS only in production so a local http://localhost never gets
 * pinned. The CSP allows what this app actually uses: same-origin scripts
 * and styles (Next inlines both), signed file URLs from Supabase storage and
 * S3 (img/media/connect), data: images for embedded icons, and the phone
 * camera / microphone for unit photos and voice chat. Nothing may frame us.
 */
const csp = [
  "default-src 'self'",
  `script-src 'self' 'unsafe-inline'${isProd ? '' : " 'unsafe-eval'"}${turnstile}${recaptchaScript}`,
  "style-src 'self' 'unsafe-inline'",
  "img-src 'self' data: blob: https:",
  "media-src 'self' blob: https:",
  "font-src 'self' data:",
  // LiveKit uses HTTPS for region discovery / validation and WSS for signaling.
  // Include regional failover hosts as well as the project hostname.
  `connect-src 'self' https://*.supabase.co https://*.amazonaws.com https://*.livekit.cloud wss://*.livekit.cloud${recaptchaConnect}${isProd ? '' : ' ws: wss:'}`,
  `frame-src 'self'${turnstile}${recaptchaFrame}`,
  "frame-ancestors 'none'",
  "base-uri 'self'",
  "form-action 'self'",
  "object-src 'none'",
  ...(isProd ? ['upgrade-insecure-requests'] : []),
].join('; ')

const securityHeaders = [
  { key: 'Content-Security-Policy', value: csp },
  { key: 'X-Frame-Options', value: 'DENY' },
  { key: 'X-Content-Type-Options', value: 'nosniff' },
  { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
  { key: 'Permissions-Policy', value: 'camera=(self), microphone=(self), geolocation=(), payment=(), usb=()' },
  { key: 'X-DNS-Prefetch-Control', value: 'off' },
  ...(isProd ? [{ key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' }] : []),
]

const nextConfig: NextConfig = {
  // Standalone output (STANDALONE=1, used by scripts/package-cpanel.sh) bundles
  // the server + node_modules into .next/standalone for the upload-a-zip flow.
  // Default builds omit it so `next start` (PM2 / deploy.sh on the server) works.
  output: process.env.STANDALONE === '1' ? 'standalone' : undefined,
  // The cPanel host's glibc is too old for sharp's native binary, and the app
  // renders no next/image content — skip the optimizer so the Mac-built
  // standalone bundle runs on Linux without platform-specific binaries.
  images: { unoptimized: true },
  poweredByHeader: false,
  // The Carrier Dispatch "Create Trip" wizard submits the rate confirmation
  // and every permit in ONE Server Action request. Next caps that body at
  // 1 MB by default, which failed the whole submit ("Body exceeded 1 MB
  // limit", 2026-10-01). Per-file limit stays 1 MB (src/lib/domain/upload-limits.ts);
  // 25 MB fits one rate con plus ~20 permits with multipart overhead.
  experimental: { serverActions: { bodySizeLimit: '25mb' } },
  async headers() {
    return [{ source: '/:path*', headers: securityHeaders }]
  },
}

export default nextConfig
