import {defineConfig, type Plugin} from 'vite'
import {readFileSync} from 'fs'
import {createHash} from 'crypto'
import {resolve, join} from 'path'
import {tmpdir} from 'os'
import presets from './web/servers.json'

const PORT_FILE = join(tmpdir(), 'xftp-test-server.port')
const FIXTURES = resolve(import.meta.dirname, '../tests/fixtures')

const __dirname = import.meta.dirname

function parseHost(addr: string): string {
  const m = addr.match(/@(.+)$/)
  if (!m) throw new Error('bad server address: ' + addr)
  const host = m[1].split(',')[0]
  return host.includes(':') ? host : host + ':443'
}

function cspPlugin(servers: string[], isDev: boolean): Plugin {
  const origins = servers.map(s => 'https://' + parseHost(s)).join(' ')
  return {
    name: 'csp-connect-src',
    transformIndexHtml: {
      order: 'pre',
      handler(html) {
        if (isDev) {
          return html.replace(/<meta\s[^>]*?Content-Security-Policy[\s\S]*?>/i, '')
        }
        // Auto-compute SHA-256 hashes for inline scripts (CSP script-src)
        const scriptHashes: string[] = []
        html.replace(/<script>(.+?)<\/script>/gs, (_m: string, content: string) => {
          scriptHashes.push("'sha256-" + createHash('sha256').update(content, 'utf-8').digest('base64') + "'")
          return _m
        })
        return html
          .replace('__CSP_CONNECT_SRC__', origins)
          .replace('__CSP_SCRIPT_HASHES__', scriptHashes.join(' '))
      }
    }
  }
}

// Compute fingerprint from ca.crt (SHA-256 of DER)
function getFingerprint(): string {
  const pem = readFileSync(join(FIXTURES, 'ca.crt'), 'utf-8')
  const der = Buffer.from(pem.replace(/-----[^-]+-----/g, '').replace(/\s/g, ''), 'base64')
  return createHash('sha256').update(der).digest('base64')
    .replace(/\+/g, '-').replace(/\//g, '_')
}

// Plugin to inject __XFTP_SERVERS__ via Vite define (reads PORT_FILE written by test/runSetup.ts)
function xftpServersPlugin(): Plugin {
  const fp = getFingerprint()
  return {
    name: 'xftp-servers-define',
    config() {
      const port = readFileSync(PORT_FILE, 'utf-8').trim()
      const serverAddr = `xftp://${fp}@localhost:${port}`
      return {define: {__XFTP_SERVERS__: JSON.stringify([serverAddr])}}
    }
  }
}

export default defineConfig(({mode}) => {
  const define: Record<string, string> = {}
  let servers: string[]
  const plugins: Plugin[] = []

  if (mode === 'development') {
    // In development mode, use the test server (port from globalSetup)
    plugins.push(xftpServersPlugin())
    define['__XFTP_PROXY_PORT__'] = JSON.stringify(null)
    // For CSP plugin, use localhost placeholder (CSP stripped in dev server anyway)
    servers = ['xftp://fp@localhost:443']
  } else {
    // In production mode, use the preset servers
    servers = [...presets.simplex]
    define['__XFTP_SERVERS__'] = JSON.stringify(servers)
    define['__XFTP_PROXY_PORT__'] = JSON.stringify(null)
  }

  plugins.push(cspPlugin(servers, mode === 'development'))

  const httpsConfig = mode === 'development' ? {
    key: readFileSync(join(FIXTURES, 'web.key')),
    cert: readFileSync(join(FIXTURES, 'web.crt')),
  } : undefined

  return {
    base: './',
    root: 'web',
    build: {
      outDir: resolve(__dirname, 'dist-web'),
      emptyOutDir: true,
      target: 'esnext',
      minify: false,
      chunkSizeWarningLimit: 1200,
      rollupOptions: {
        external: ['node:http2', 'url'],
        output: {
          entryFileNames: 'assets/[name].js',
          chunkFileNames: 'assets/[name].js',
          assetFileNames: 'assets/[name][extname]',
        },
      },
    },
    server: httpsConfig ? {https: httpsConfig} : {},
    preview: {host: true, https: false},
    define,
    worker: {format: 'es' as const, rollupOptions: {external: ['node:http2', 'url'], output: {entryFileNames: 'assets/[name].js'}}},
    plugins,
  }
})
