import type { NextConfig } from "next";

const baseSecurityHeaders = [
  {
    key: "Strict-Transport-Security",
    value: "max-age=31536000; includeSubDomains; preload",
  },
  { key: "X-Content-Type-Options", value: "nosniff" },
  { key: "X-Frame-Options", value: "SAMEORIGIN" },
  { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
  {
    key: "Permissions-Policy",
    value: "camera=(), microphone=(), geolocation=(), interest-cohort=()",
  },
];

/**
 * Strict Content-Security-Policy for public pages.
 *
 * Notes on tradeoffs:
 * - `style-src 'unsafe-inline'` is required because Tailwind v4 emits inline
 *   style attributes for arbitrary values (`bg-[var(--color-blue)]`) and
 *   Next.js injects critical CSS as an inline style tag. Without this the
 *   brand tokens break.
 * - `img-src https:` is intentional. /news and /studies aggregate from
 *   third-party RSS/PubMed feeds and render publisher thumbnails inline
 *   from arbitrary HTTPS hosts (i0.wp.com, ryortho.com, doi.org CDNs,
 *   etc.). Maintaining a per-publisher allowlist would silently break
 *   thumbnails the moment a feed adds a new image host. Combined with
 *   `script-src 'self'` (no inline scripts) the residual risk from a
 *   data-exfil-via-image attack is minimal — an attacker would still
 *   need an XSS hole that the strict script policy already blocks.
 * - In dev mode Next 16 / Turbopack uses inline bootstrap scripts and
 *   eval() for HMR; without `'unsafe-eval' 'unsafe-inline'` on script-src
 *   hydration breaks and client-side navigation silently falls back to
 *   a server round trip that can land on the dev 404 overlay. We therefore
 *   emit those two keywords only when NODE_ENV !== 'production'. The
 *   production policy stays strict.
 * - `frame-ancestors 'none'` is stronger than XFO and both are set.
 */
function buildStrictCsp(): string {
  // Evaluated inside headers() so NODE_ENV reflects the actual runtime,
  // not whatever was in the env when next.config.ts was first imported.
  //
  // `'unsafe-inline'` on script-src is required even in production:
  // Next 16 + React 19 streams the RSC flight payload as inline
  // <script>__next_f.push(...)</script> tags, and there is no built-in
  // nonce plumbing for App Router static + dynamic mixed routes. The
  // pragmatic tradeoff is to allow inline scripts but keep `script-src`
  // restricted to 'self' otherwise — no <script src=external> loads.
  // `'unsafe-eval'` is dev-only (Turbopack HMR uses eval).
  const isDev = process.env.NODE_ENV !== "production";
  const scriptSrc = isDev
    ? "script-src 'self' 'unsafe-eval' 'unsafe-inline'"
    : "script-src 'self' 'unsafe-inline'";
  return [
    "default-src 'self'",
    "img-src 'self' data: https:",
    "media-src 'self' https:",
    "style-src 'self' 'unsafe-inline'",
    scriptSrc,
    "connect-src 'self'",
    "font-src 'self' data:",
    // Permit video embeds (YouTube, Vimeo) inline. Expand this list if new
    // trusted video providers are added.
    "frame-src 'self' https://www.youtube.com https://www.youtube-nocookie.com https://player.vimeo.com",
    "frame-ancestors 'none'",
    "base-uri 'self'",
    "form-action 'self'",
  ].join("; ");
}

/**
 * Relaxed CSP for the embedded Sanity Studio at /studio.
 *
 * Studio is a heavy SPA that needs eval (code editor), inline styles
 * (styled-components runtime), and websocket + CDN access to the Sanity
 * content lake. We accept the relaxation on /studio/* only because it
 * is auth-gated editorial tooling, not public content.
 */
const studioCsp = [
  "default-src 'self'",
  "img-src 'self' data: blob: https://cdn.sanity.io https://*.sanity.io",
  "style-src 'self' 'unsafe-inline'",
  "script-src 'self' 'unsafe-eval' 'unsafe-inline' blob:",
  "connect-src 'self' https://*.sanity.io wss://*.sanity.io https://*.apicdn.sanity.io",
  "font-src 'self' data:",
  "frame-ancestors 'none'",
  "worker-src 'self' blob:",
  "base-uri 'self'",
].join("; ");

const nextConfig: NextConfig = {
  output: "standalone",
  reactStrictMode: true,
  poweredByHeader: false,
  images: {
    formats: ["image/webp"],
    minimumCacheTTL: 60 * 60 * 24 * 30,
    remotePatterns: [{ protocol: "https", hostname: "cdn.sanity.io" }],
  },
  async headers() {
    // First-match wins in Next 16: /studio/:path* is matched before /:path*
    // so Studio receives the relaxed CSP and every other route gets strict.
    return [
      {
        source: "/studio/:path*",
        headers: [
          ...baseSecurityHeaders,
          { key: "Content-Security-Policy", value: studioCsp },
        ],
      },
      {
        source: "/:path*",
        headers: [
          ...baseSecurityHeaders,
          { key: "Content-Security-Policy", value: buildStrictCsp() },
        ],
      },
    ];
  },
};

export default nextConfig;
