import { createPwaPlugin } from '@tale/ui/pwa/vite-plugin';
import { tanstackRouter } from '@tanstack/router-plugin/vite';
import viteReact from '@vitejs/plugin-react';
import { defineConfig } from 'vite';

import { injectAcceptLanguage } from './vite-plugins/inject-accept-language';
import { injectEnv } from './vite-plugins/inject-env';
import { serveBrandingImages } from './vite-plugins/serve-branding-images';
import { serveCanvasPreview } from './vite-plugins/serve-canvas-preview';
import { serveStatus } from './vite-plugins/serve-status';
import { serveWebdav } from './vite-plugins/serve-webdav';
import { stubSSRImports } from './vite-plugins/stub-ssr';
import { watchExamples } from './vite-plugins/watch-examples';

// Convex service endpoints for dev proxy. Defaults to localhost so local
// developers running `bunx convex-local-backend` standalone just work; for
// compose-based dev (`docker compose up convex` + `bun run dev`) set
// CONVEX_URL=http://localhost:3210 in .env.local or similar.
const CONVEX_BASE = process.env.CONVEX_URL || 'http://127.0.0.1:3210';
// Site-proxy lives on a separate port (default 3211) on the same host.
const CONVEX_SITE_PROXY =
  process.env.CONVEX_SITE_PROXY_URL || CONVEX_BASE.replace(/:\d+$/, ':3211');

export default defineConfig({
  base: './',
  resolve: {
    dedupe: ['convex', 'convex/react', 'react', 'react-dom'],
    tsconfigPaths: true,
  },
  server: {
    port: 3000,
    // Fail loudly if 3000 is taken instead of silently moving to the next free
    // port: SITE_URL, the Convex proxy, and the dev orchestrator all assume the
    // app is on 3000, so a silent port shift just looks like "localhost:3000 is
    // broken". The dev orchestrator passes --strictPort too; this keeps direct
    // `vite`/preview invocations consistent.
    strictPort: true,
    proxy: {
      // Proxy Convex API requests to the (possibly remote) convex service.
      '/ws_api': {
        target: CONVEX_BASE,
        changeOrigin: true,
        ws: true,
        rewrite: (p) => p.replace(/^\/ws_api/, ''),
      },
      '/http_api': {
        target: CONVEX_SITE_PROXY,
        changeOrigin: true,
        rewrite: (p) => p.replace(/^\/http_api/, ''),
      },
      // Storage and internal action callbacks go to the Convex backend (3210)
      '/api/storage': {
        target: CONVEX_BASE,
        changeOrigin: true,
      },
      '/api/actions': {
        target: CONVEX_BASE,
        changeOrigin: true,
      },
      // All other /api/* requests to Convex HTTP endpoint (auth, SSO, documents, workflows, etc.)
      '/api': {
        target: CONVEX_SITE_PROXY,
        changeOrigin: true,
      },
    },
  },
  optimizeDeps: {
    include: [
      'react',
      'react-dom',
      'react/jsx-runtime',
      'convex/react',
      '@tanstack/react-router',
      '@tanstack/react-query',
      '@convex-dev/react-query',
      'lucide-react',
      '@radix-ui/react-dialog',
      '@radix-ui/react-dropdown-menu',
      '@radix-ui/react-popover',
      '@radix-ui/react-tooltip',
      '@radix-ui/react-slot',
      'framer-motion',
      'zod',
      'lodash',
      'date-fns',
      // Markdown rendering stack. Every consumer (chat, skills, automations,
      // changelog, workspace viewers, docs bodies) lives behind a code-split
      // route or a lazily-loaded dialog, so Vite's cold-start scanner never
      // reaches them. The first navigation that mounts a renderer then
      // discovers this whole cluster at once, forcing a "optimized
      // dependencies changed. reloading" re-bundle — which rewrites the hashed
      // chunk names and 404s any in-flight request for the old ones (zwitch,
      // unist-util-visit, etc.). Pre-bundling the four entry points pulls their
      // transitive deps (micromark-*, hast/mdast-util-*) into the first pass so
      // no mid-session re-optimization is ever triggered.
      'react-markdown',
      'remark-gfm',
      'rehype-raw',
      'rehype-sanitize',
      // Custom markdown plugins (@tale/ui) reach *past* react-markdown and
      // import these micromark/unist internals directly, so each becomes its
      // own optimized entry that the react-markdown pre-bundle above does not
      // cover. Discovered only when a markdown route mounts a plugin, they
      // re-bundle the shared micromark chunk mid-session and 404 the in-flight
      // `dev-*.js` request. Listing them keeps that chunk stable from cold start.
      'micromark-core-commonmark',
      'micromark-util-classify-character',
      'unist-util-visit',
      // Charting stack (analytics + automation metrics) is behind lazy routes,
      // so the cold-start scanner never sees `recharts`. First chart mount
      // discovers it plus its transitive prop-types -> react-is, forcing a
      // re-optimization that 404s the in-flight `react-is-*.js` chunk.
      'recharts',
    ],
    exclude: [
      '@tanstack/react-start/server',
      '@tanstack/react-start-server',
      '@tanstack/start-server-core',
      '@tanstack/start-plugin-core',
    ],
  },
  build: {
    outDir: 'dist',
    sourcemap: true,
    chunkSizeWarningLimit: 2000,
    rollupOptions: {
      output: {
        manualChunks(id) {
          if (id.includes('node_modules')) {
            // Group React core + tightly coupled dependencies together to avoid circular deps
            if (
              id.includes('/react/') ||
              id.includes('react-dom') ||
              id.includes('react-is') ||
              id.includes('scheduler') ||
              id.includes('@tanstack') ||
              id.includes('convex')
            ) {
              return 'vendor-core';
            }
            if (id.includes('@radix-ui')) {
              return 'vendor-radix';
            }
            if (id.includes('xlsx')) {
              return 'vendor-xlsx';
            }
            if (id.includes('pdfjs-dist')) {
              return 'vendor-pdf';
            }
            if (id.includes('katex')) {
              return 'vendor-katex';
            }
            if (
              id.includes('codemirror') ||
              id.includes('@codemirror') ||
              id.includes('@lezer')
            ) {
              return 'vendor-codemirror';
            }
            if (id.includes('lucide-react')) {
              return 'vendor-icons';
            }
            if (
              id.includes('libphonenumber-js') ||
              id.includes('validator/lib') ||
              id.includes('validator/es')
            ) {
              return 'vendor-pii';
            }
          }
          return undefined;
        },
      },
    },
  },
  plugins: [
    tanstackRouter(),
    injectEnv(),
    injectAcceptLanguage(),
    stubSSRImports(),
    viteReact(),
    watchExamples(),
    serveBrandingImages(),
    serveCanvasPreview(),
    serveStatus(),
    serveWebdav(),
    createPwaPlugin({
      name: 'Tale',
      shortName: 'Tale',
      description: 'AI-powered customer support platform',
      themeColor: '#09090b',
      backgroundColor: '#fcfcfc',
      projectDir: import.meta.dirname,
      icons: [
        { src: 'assets/pwa-64x64.png', sizes: '64x64', type: 'image/png' },
        { src: 'assets/pwa-192x192.png', sizes: '192x192', type: 'image/png' },
        {
          src: 'assets/pwa-512x512.png',
          sizes: '512x512',
          type: 'image/png',
          purpose: 'any',
        },
        {
          src: 'assets/maskable-icon-512x512.png',
          sizes: '512x512',
          type: 'image/png',
          purpose: 'maskable',
        },
      ],
    }),
  ],
});
