import { createRequire } from 'node:module'
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import eslintPlugin from 'vite-plugin-eslint'
import svgr from 'vite-plugin-svgr'
import path from 'path'
import { fileURLToPath } from 'url'
import { visualizer } from 'rollup-plugin-visualizer'

const require = createRequire(import.meta.url)
const svgo = require('vite-plugin-svgo')

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

const isCloudflare = process.env.VITE_DEPLOY_TARGET === 'cloudflare'

/** Resolve a package's ESM entry to an absolute path (pnpm-safe single instance). */
function esmEntry(id) {
  const cjs = require.resolve(id)
  const root = path.dirname(path.dirname(cjs)) // .../pkg/dist → .../pkg
  const pkg = require(path.join(root, 'package.json'))
  const rel =
    (typeof pkg.exports?.['.'] === 'object' && pkg.exports['.'].import) ||
    (typeof pkg.exports === 'object' && pkg.exports.import) ||
    pkg.module ||
    pkg.exports?.['.'] ||
    'dist/index.js'
  return path.join(root, rel)
}

const codemirrorSingletons = {
  '@codemirror/state': esmEntry('@codemirror/state'),
  '@codemirror/view': esmEntry('@codemirror/view'),
  '@codemirror/language': esmEntry('@codemirror/language'),
  '@codemirror/commands': esmEntry('@codemirror/commands'),
  '@codemirror/autocomplete': esmEntry('@codemirror/autocomplete'),
  // Must match the @lezer/common used by @codemirror/lang-* parsers (1.5.0).
  // 1.5.2 breaks parseMixed tree walking across nested language mounts.
  '@lezer/common': path.resolve(__dirname, 'node_modules/@lezer/common/dist/index.js'),
  '@lezer/lr': esmEntry('@lezer/lr'),
  '@lezer/highlight': esmEntry('@lezer/highlight')
}

export default defineConfig({
  base: isCloudflare ? '/' : './', // Cloudflare Pages needs absolute paths; Electron needs relative for file://
  resolve: {
    alias: {
      three: path.resolve(__dirname, 'node_modules/three'),
      ...codemirrorSingletons
    },
    dedupe: Object.keys(codemirrorSingletons)
  },
  optimizeDeps: {
    // Do not prebundle CodeMirror — pnpm + esbuild duplicates @codemirror/state
    // and breaks FacetProvider instanceof checks.
    include: ['@ant-design/icons'],
    exclude: [
      '@uiw/react-codemirror',
      'codemirror',
      '@codemirror/state',
      '@codemirror/view',
      '@codemirror/language',
      '@codemirror/commands',
      '@codemirror/autocomplete',
      '@codemirror/theme-one-dark',
      '@codemirror/lang-javascript',
      '@codemirror/lang-xml',
      '@codemirror/lang-json',
      '@codemirror/lang-html',
      '@codemirror/lang-css',
      '@codemirror/lang-python',
      '@codemirror/lang-markdown',
      '@codemirror/lang-sql',
      '@codemirror/lang-java',
      '@codemirror/lang-cpp',
      '@codemirror/lang-rust',
      '@codemirror/lang-go',
      '@codemirror/lang-php',
      '@codemirror/lang-yaml',
      '@lezer/common',
      '@lezer/lr',
      '@lezer/highlight',
      '@lezer/javascript',
      '@lezer/xml'
    ]
  },
  plugins: [
    react(),
    svgo(),
    svgr(),
    eslintPlugin(),
    visualizer(),
    // Transform index.html paths to absolute when building for Cloudflare Pages
    isCloudflare && {
      name: 'cloudflare-html-paths',
      transformIndexHtml(html) {
        return html
          .replace(/href="\.\//g, 'href="/')
          .replace(/src="\.\//g, 'src="/')
      }
    }
  ].filter(Boolean),
  build: {
    outDir: 'build',
    chunkSizeWarningLimit: 2000,
    rollupOptions: {
      output: {
        manualChunks(id) {
          // --- CodeMirror
          if (id.includes('node_modules/@codemirror')) {
            return 'codemirror'
          }

          // --- Lezer
          if (id.includes('node_modules/@lezer')) {
            return 'lezer'
          }

          // --- Core Vendor (Ant Design, React, etc.)
          if (
            id.includes('node_modules/antd') ||
            id.includes('node_modules/@ant-design') ||
            id.includes('node_modules/rc-') ||
            id.includes('node_modules/antd-style') ||
            id.includes('node_modules/@rc-component') ||
            id.includes('node_modules/dayjs') ||
            id.includes('node_modules/react') ||
            id.includes('node_modules/react-dom') ||
            id.includes('node_modules/react-router-dom')
          ) {
            return 'antd'
          }

          // --- Lodash
          if (id.includes('node_modules/lodash')) {
            return 'lodash'
          }

          // --- Three.js
          if (id.includes('node_modules/three')) {
            return 'three'
          }

          // --- GCode Preview
          if (id.includes('node_modules/gcode-preview')) {
            return 'gcode-preview'
          }

          // --- Online 3D Viewer
          if (id.includes('node_modules/online-3d-viewer')) {
            return 'online-3d-viewer'
          }

          // --- tsparticles
          if (
            id.includes('node_modules/@tsparticles') ||
            id.includes('node_modules/tsparticles')
          ) {
            return 'tsparticles'
          }

          // --- Tiptap
          if (id.includes('node_modules/@tiptap')) {
            return 'tiptap'
          }
        }
      }
    }
  },
  server: {
    allowedHosts: ['dev.tombutcher.work'],
    host: '0.0.0.0',
    port: 5173,
    open: false
  }
})
