import fs from 'node:fs'
import { createRequire } from 'node:module'
import path from 'node:path'
import react from '@vitejs/plugin-react'
import type { InlineConfig } from 'vite'
import { createMobileStaticMiddleware, createRfwtxtMiddleware } from '../lib/middleware.ts'
import { resolveWorkflowsDeps } from '../plugins/resolve-workflows.ts'
import { generatePandaConfig } from './panda.ts'

const require = createRequire(import.meta.url)

export interface PreviewConfigOptions {
	/** Absolute path to the preview app directory (app/) */
	appDir: string
	/** Absolute path to the customer's workflows directory */
	workflowsDir: string
	/** Absolute path to rfw-preview static files directory */
	staticDir: string
	/** Absolute path to the customer's project root */
	projectRoot: string
	/** Absolute path to this package's root (where node_modules lives) */
	packageRoot: string
	/** Dev server port */
	port?: number
}

/**
 * Creates a Vite config for the preview app (dev server or static build).
 */
export function createPreviewConfig(opts: PreviewConfigOptions): {
	config: InlineConfig
	cleanup: () => void
} {
	const { appDir, workflowsDir, staticDir, projectRoot, packageRoot, port = 4173 } = opts

	const resolveAnchor = path.join(projectRoot, '__resolve_anchor__.ts')
	const panda = generatePandaConfig({ appDir, workflowsDir, packageRoot })

	// Force a single React instance. Without this, the preview app (inside
	// the package) resolves its own React from the package's node_modules,
	// while workflow components resolve React from the customer's project.
	// Two React instances = hooks crash ("Cannot read properties of null").
	const reactPath = path.dirname(require.resolve('react/package.json'))
	const reactDomPath = path.dirname(require.resolve('react-dom/package.json'))

	// Install root is where node_modules actually lives (above the package).
	// Needed for PandaCSS preset resolution (bun hoists deps).
	const installRoot = packageRoot.replace(/\/node_modules\/.*$/, '')

	const config: InlineConfig = {
		root: appDir,
		base: './',

		resolve: {
			alias: {
				'@workflows': workflowsDir,
				react: reactPath,
				'react-dom': reactDomPath,
			},
		},

		plugins: [
			react(),
			resolveWorkflowsDeps(resolveAnchor),
			{
				name: 'pyck-panda-css',
				async buildStart() {
					// Generate PandaCSS CSS using @pandacss/node directly.
					// The PostCSS plugin's getCss() returns empty because it
					// doesn't call parseFiles(). Using the node API we drive
					// the full pipeline: load config → parse files → generate CSS.
					const { loadConfigAndCreateContext } = require('@pandacss/node')
					const ctx = await loadConfigAndCreateContext({
						configPath: panda.configPath,
						cwd: installRoot,
					})
					const sheet = ctx.createSheet()
					ctx.appendBaselineCss(sheet)
					ctx.parseFiles()
					ctx.appendParserCss(sheet)
					const css = sheet.toCss()

					// Write generated CSS to a file that index.css imports.
					const outPath = path.join(appDir, '__panda.css')
					fs.writeFileSync(outPath, css, 'utf-8')
				},
			},
			{
				name: 'pyck-preview-middleware',
				configureServer(server) {
					server.middlewares.use(createRfwtxtMiddleware(workflowsDir))
					if (fs.existsSync(staticDir)) {
						server.middlewares.use(createMobileStaticMiddleware(staticDir))
					}
				},
			},
		],

		server: {
			port,
			fs: {
				allow: [projectRoot, appDir, path.dirname(appDir)],
			},
		},

		build: {
			emptyOutDir: true,
		},
	}

	return { config, cleanup: panda.cleanup }
}
