import type { NextConfig } from 'next';
import bundleAnalyzer from '@next/bundle-analyzer';
import { withSentryConfig } from '@sentry/nextjs';

const isProd = process.env.NODE_ENV === 'production';

// Wrap the config with the bundle analyzer. Run `ANALYZE=true pnpm build`
// to get treemaps of the client + server bundles after the build
// completes. Pairs with the recharts dynamic-import work the audit
// flagged — gives us the tool to verify chart bundles only ship on the
// dashboard surface and not on routes that don't render them.
const withBundleAnalyzer = bundleAnalyzer({
  enabled: process.env.ANALYZE === 'true',
});

/**
 * Security headers applied to every response. Per audit-pass-#3 finding:
 * the previous config emitted no CSP, X-Frame-Options, HSTS, or
 * X-Content-Type-Options — the app was open to clickjacking + MIME
 * sniffing.
 *
 * CSP notes:
 *   - 'unsafe-inline' on style-src is required by Tailwind's runtime
 *     style injection and Radix; revisit when Tailwind v4 ships a
 *     nonce story.
 *   - 'unsafe-eval' on script-src is dev-only — Next dev uses eval for
 *     HMR. Production drops it.
 *   - connect-src allows ws/wss for Socket.IO and https: for outgoing
 *     fetches; tighten in prod via per-port branding URLs once we move
 *     the s3 image references into a known allowlist.
 *   - img-src https: is wide because port branding pulls from
 *     s3.portnimara.com plus per-port image URLs configured at runtime.
 */
// Dev-only allow-list: react-grab (the in-page click-to-source devtool)
// is fetched from unpkg, so script/style/connect must allow it. Strip
// these entries in prod via the conditional below.
const devScriptHosts = isProd ? '' : ' http://unpkg.com https://unpkg.com';
const devConnectHosts = isProd ? '' : ' http://unpkg.com https://unpkg.com';

// Fallback CSP for paths the proxy doesn't run on (static assets,
// API JSON responses where script-src is moot). Production HTML
// responses get a stricter per-request nonce-based CSP set in
// `src/proxy.ts:applyCsp`; this header just provides a sane default
// so a misconfigured static-only route still has a CSP.
//
// Dev keeps 'unsafe-inline' + 'unsafe-eval' on script-src because
// Next's HMR runtime evaluates code dynamically and the nonce
// machinery doesn't reach it.
const csp = [
  "default-src 'self'",
  `script-src 'self' 'unsafe-inline'${isProd ? '' : " 'unsafe-eval'"}${devScriptHosts}`,
  "style-src 'self' 'unsafe-inline'",
  "img-src 'self' data: blob: https:",
  // https: so react-pdf/pdf.js can load its standard-font pack + branding fonts.
  "font-src 'self' data: https:",
  `connect-src 'self' ws: wss: https:${devConnectHosts}`,
  // PDF previews iframe a presigned storage URL; embedded-signing iframes the
  // Documenso host. Both are per-port/per-env, so allow https: (matching
  // img-src). frame-ancestors 'none' still blocks others from embedding us.
  "frame-src 'self' blob: https:",
  "frame-ancestors 'none'",
  "base-uri 'self'",
  "form-action 'self'",
  "object-src 'none'",
].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=(), geolocation=()' },
  ...(isProd
    ? [{ key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' }]
    : []),
];

const nextConfig: NextConfig = {
  output: 'standalone',
  // Hide the floating dev indicator (the little circle/N badge in the
  // corner). Compile errors still surface via the full overlay; this
  // only removes the idle "everything is fine" indicator that's been
  // visible in every screenshot from the iPhone testing pass.
  devIndicators: false,
  // LAN access from a real iPhone hits the dev server via the Mac's
  // local IP (e.g. 192.168.x.x), not localhost. Next surfaces a warning
  // and blocks cross-origin /_next/* fetches (incl. HMR) unless we
  // allow-list the origins explicitly. When HMR is blocked the page
  // never fully hydrates and form click handlers fall back to native
  // submits — the symptom that bit us with a hard-coded IP. Wildcards
  // cover any LAN device without a per-network config edit.
  ...(isProd ? {} : { allowedDevOrigins: ['192.168.*.*', '10.*.*.*', '172.16.*.*', '172.20.*.*'] }),
  // Native/CJS-leaning server-only packages — list here so Next doesn't
  // bundle them into the route trace (slower cold start + risk that
  // native bindings fail at runtime). Build-auditor C3+M3: socket.io
  // is only imported by the custom server entry point, so the Next
  // tracer has no reason to include it; listing here makes the
  // dependency visible to the build system.
  serverExternalPackages: [
    'pino',
    'pino-pretty',
    'bullmq',
    'ioredis',
    'minio',
    'postgres',
    'better-auth',
    'nodemailer',
    'socket.io',
    '@socket.io/redis-adapter',
    'imapflow',
    'mailparser',
    'pdf-lib',
    'sharp',
    'tesseract.js',
    '@react-pdf/renderer',
    'unpdf',
  ],
  images: {
    remotePatterns: [{ protocol: 'https', hostname: '*.portnimara.com' }],
  },
  typedRoutes: true,
  // ECharts ships ES modules that older Next/webpack versions can't parse
  // without a transpile-pass. Listing here is the official recommendation
  // from echarts-for-react when used inside Next.
  transpilePackages: ['echarts', 'zrender', 'echarts-for-react'],
  outputFileTracingIncludes: {
    // Bundle the EOI source PDF so the in-app EOI pathway can read it at
    // runtime in the standalone build. Reading via fs.readFile from
    // process.cwd() requires the file to be traced explicitly.
    '/api/v1/document-templates/**': ['./assets/eoi-template.pdf'],
  },
  async redirects() {
    return [
      {
        source: '/:portSlug/documents/files',
        destination: '/:portSlug/documents',
        permanent: true,
      },
      {
        source: '/:portSlug/documents/files/:path*',
        destination: '/:portSlug/documents',
        permanent: true,
      },
    ];
  },
  async headers() {
    return [
      {
        source: '/:path*',
        headers: securityHeaders,
      },
    ];
  },
};

// Sentry wrapper is conditional: if NEXT_PUBLIC_SENTRY_DSN isn't set we
// skip its build-time source-map upload + middleware injection so dev
// builds stay fast and CI doesn't need credentials. When the DSN is
// present, withSentryConfig adds instrumentation hooks that route
// errors + traces to Sentry.
const withSentry = process.env.NEXT_PUBLIC_SENTRY_DSN
  ? (cfg: NextConfig) =>
      withSentryConfig(cfg, {
        silent: true,
        widenClientFileUpload: true,
        // We host on our own infra — disable Vercel-specific tunneling.
        tunnelRoute: undefined,
        // Strip Sentry debug logger from prod bundle.
        disableLogger: true,
      })
  : (cfg: NextConfig) => cfg;

export default withSentry(withBundleAnalyzer(nextConfig));
