// vite build config for control UI
// widget has its own config, see src/pretix/static/pretixpresale/widget/vite.config.ts
import { defineConfig, type Plugin } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import { execSync } from 'child_process'
import { readFileSync } from 'fs'
import { parse as parseToml } from 'smol-toml'

// Shared dependencies exposed to plugins via import map.
// Adding a dep here auto-generates a _vendor/{name} entry and
// makes it available in the import map.
const SHARED_DEPS = ['vue']

const { entries: pretixPluginEntries } = discoverPretixPlugins()
const pluginDirs = [...new Set(Object.values(pretixPluginEntries).map(p => path.dirname(p)))]

export default defineConfig({
	plugins: [
		vue(),
		sharedDepsPlugin(),
		pretixPluginDevEntries(),
	],
	resolve: {
		// Pin shared deps to pretix's node_modules to prevent duplicate instances
		// across plugins whose node_modules live in sibling directories
		dedupe: [...SHARED_DEPS, '@vue/runtime-core', '@vue/reactivity', '@vue/shared'],
	},
	server: {
		fs: {
			// Allow serving source files from sibling plugin directories
			allow: ['src', ...pluginDirs],
		},
	},
	build: {
		manifest: true,
		outDir: path.resolve(__dirname, 'src/pretix/static.dist/vite/control'),
		rollupOptions: {
			preserveEntrySignatures: 'exports-only',
			input: {
				'webcheckin/main': path.resolve(__dirname, 'src/pretix/plugins/webcheckin/static/pretixplugins/webcheckin/main.ts'),
				'checkinrules/main': path.resolve(__dirname, 'src/pretix/static/pretixcontrol/js/ui/checkinrules/index.ts'),
				...Object.fromEntries(SHARED_DEPS.map(dep => [`_vendor/${dep}`, `virtual:vendor/${dep}`])),
				...pretixPluginEntries,
			},
		}
	},
	optimizeDeps: {
		exclude: ['moment', 'jquery']
	}
})

// Virtual module plugin: generates re-export entries for each shared dep
// In dev mode, serves /__pretix_importmap so the python template tag can build the import map without hardcoding dep names.
function sharedDepsPlugin (): Plugin {
	const PREFIX = 'virtual:vendor/'
	const RESOLVED = '\0virtual:vendor/'
	return {
		name: 'pretix-shared-deps',
		resolveId (id) {
			if (id.startsWith(PREFIX))
				return RESOLVED + id.slice(PREFIX.length)
		},
		load (id) {
			if (id.startsWith(RESOLVED)) {
				const pkg = id.slice(RESOLVED.length)
				return `export * from '${pkg}'`
			}
		},
		// Serve the import map data so the Python template tag can fetch it in dev mode
		configureServer (server) {
			server.middlewares.use((req, res, next) => {
				if (req.url === '/__pretix_importmap') {
					const imports = Object.fromEntries(
						SHARED_DEPS.map(dep => [
							dep,
							`/node_modules/.vite/deps/${dep.replace('/', '_')}.js`,
						])
					)
					res.setHeader('Content-Type', 'application/json')
					res.setHeader('Access-Control-Allow-Origin', '*')
					res.end(JSON.stringify(imports))
					return
				}
				next()
			})
		},
	}
}

// TODO move to separate file?
function discoverPretixPlugins (): { entries: Record<string, string> } {
	let manifestFiles: string[] = []
	try {
		const raw = execSync(`python -c "
import importlib_metadata as metadata, json, pathlib, tomllib
result = []
for ep in metadata.entry_points(group='pretix.plugin'):
    dist = ep.dist
    if not dist: continue
    try:
        url_info = json.loads(dist.read_text('direct_url.json') or '{}')
        if not url_info.get('dir_info', {}).get('editable'):
            continue  # non-editable plugins build their own assets
        p = pathlib.Path(url_info['url'].replace('file://', '')) / 'pretixplugin.toml'
        if not p.exists():
            continue
        with p.open('rb') as f:
            if 'vite' in tomllib.load(f):
                result.append(str(p))
    except Exception:
        pass
print(json.dumps(result))
"`, { stdio: ['pipe', 'pipe', 'inherit'] }).toString().trim()
		manifestFiles = JSON.parse(raw)
	} catch (error) {
		console.error('Failed to discover pretix plugins, skipping plugin entries:', error)
	}
	const entries: Record<string, string> = {}
	for (const manifestFile of manifestFiles) {
		const packageRoot = manifestFile.replace(/[/\\]pretixplugin\.toml$/, '')
		const parsed = parseToml(readFileSync(manifestFile, 'utf8')) as {
			vite: { entries: Record<string, string> }
		}
		for (const [name, rel] of Object.entries(parsed.vite.entries)) {
			entries[name] = path.join(packageRoot, rel)
		}
	}
	return { entries }
}

// In dev mode, the browser requests /{entryName} from the Vite dev server.
// Vite can't find these files since they live outside the project root.
// This plugin rewrites those URLs to /@fs{absPath} so Vite serves them directly.
function pretixPluginDevEntries (): Plugin {
	return {
		name: 'pretix-plugin-dev-entries',
		configureServer (server) {
			server.middlewares.use((req, _res, next) => {
				const urlPath = req.url?.split('?')[0]
				if (urlPath) {
					const name = urlPath.slice(1) // strip leading /
					if (name in pretixPluginEntries)
						req.url = `/@fs${pretixPluginEntries[name]}`
				}
				next()
			})
		},
	}
}
