/**
 * Vite-native entry file generator (v2 - clean architecture)
 * Single source for both dev and prod with minimal differences
 */
import { join } from 'node:path';
import { discoverRoutes } from './vite/route-discovery';
import { generateWebAnalyticsFile } from './webanalytics-generator';
import { computeApiMountPath } from './vite/api-mount-path';
/**
 * Generate entry file with clean Vite-native architecture
 */
export async function generateEntryFile(options) {
    const { rootDir, projectId, deploymentId, logger, mode, workbench, analytics, vitePort, noBundle, preDiscoveredRoutes, } = options;
    const srcDir = join(rootDir, 'src');
    const generatedDir = join(srcDir, 'generated');
    const entryPath = join(generatedDir, 'app.ts');
    logger.trace(`Generating unified entry file (supports both dev and prod modes)...`);
    // Check if analytics is enabled
    const analyticsEnabled = analytics !== false;
    // Generate web analytics files only if enabled
    if (analyticsEnabled) {
        await generateWebAnalyticsFile({ rootDir, logger, analytics });
    }
    // Use pre-discovered routes if available, otherwise discover them
    const routeInfoList = preDiscoveredRoutes ??
        (await discoverRoutes(srcDir, projectId, deploymentId, logger)).routeInfoList;
    // Check for web and workbench
    const hasWebFrontend = (await Bun.file(join(srcDir, 'web', 'index.html')).exists()) ||
        (await Bun.file(join(srcDir, 'web', 'frontend.tsx')).exists());
    // Workbench is configured at build time, but only enabled at runtime in dev mode
    const hasWorkbenchConfig = !!workbench;
    // Get unique route files that need to be imported (relative to src/)
    const routeFiles = new Set();
    for (const route of routeInfoList) {
        if (route.filename) {
            routeFiles.add(route.filename);
        }
    }
    // Generate imports
    const runtimeImports = [
        `  createRouter,`,
        `  createBaseMiddleware,`,
        `  createCorsMiddleware,`,
        `  createOtelMiddleware,`,
        `  createAgentMiddleware,`,
        `  createCompressionMiddleware,`,
        `  getAppState,`,
        `  getAppConfig,`,
        `  getUserRouter,`,
        `  register,`,
        `  getSpanProcessors,`,
        `  createServices,`,
        `  runAgentSetups,`,
        `  getThreadProvider,`,
        `  getSessionProvider,`,
        `  setGlobalLogger,`,
        `  setGlobalTracer,`,
        `  setGlobalRouter,`,
        `  enableProcessExitProtection,`,
        `  hasWaitUntilPending,`,
        `  loadBuildMetadata,`,
        `  createWorkbenchRouter,`,
        `  bootstrapRuntimeEnv,`,
        `  patchBunS3ForStorageDev,`,
        `  runShutdown,`,
    ];
    if (noBundle) {
        runtimeImports.push(`  applyDevPatches,`);
    }
    if (hasWebFrontend) {
        runtimeImports.push(`  mimeTypes,`);
    }
    const imports = [
        `import { `,
        ...runtimeImports,
        `} from '@agentuity/runtime';`,
        `import type { Context } from 'hono';`,
        `import { websocket${hasWebFrontend ? ', serveStatic' : ''} } from 'hono/bun';`,
        hasWebFrontend ? `import { readFileSync, existsSync } from 'node:fs';` : '',
    ].filter(Boolean);
    imports.push(`import { type LogLevel } from '@agentuity/core';`);
    if (analyticsEnabled) {
        imports.push(`import { injectAnalytics, registerAnalyticsRoutes } from './webanalytics.js';`);
        imports.push(`import { analyticsConfig } from './analytics-config.js';`);
    }
    // Generate route mounting code for all discovered routes
    // Sort route files for deterministic output
    const sortedRouteFiles = [...routeFiles].sort();
    const routeImportsAndMounts = [];
    let routeIndex = 0;
    for (const routeFile of sortedRouteFiles) {
        // Normalize path separators for cross-platform compatibility (Windows uses backslashes)
        const normalizedRouteFile = routeFile.replace(/\\/g, '/');
        // Convert src/api/auth/route.ts -> auth/route
        const relativePath = normalizedRouteFile.replace(/^src\/api\//, '').replace(/\.tsx?$/, '');
        // Determine the mount path using the shared helper
        // This ensures consistency with route type generation in ast.ts
        const mountPath = computeApiMountPath(relativePath);
        const importName = `router_${routeIndex++}`;
        routeImportsAndMounts.push(`const { default: ${importName} } = await import('../api/${relativePath}.js');`);
        routeImportsAndMounts.push(`app.route('${mountPath}', ${importName});`);
    }
    const apiMount = routeImportsAndMounts.length > 0
        ? `
// Apply middleware and mount API routes
// If user passed router(s) via createApp({ router }), mount those instead of discovered files
const __userMounts = getUserRouter();
if (__userMounts) {
	for (const mount of __userMounts) {
		// Apply Agentuity middleware (CORS, OTel, agent context) to each user-provided prefix
		const prefix = mount.path.endsWith('/') ? mount.path + '*' : mount.path + '/*';
		app.use(prefix, createCorsMiddleware());
		app.use(prefix, createOtelMiddleware());
		app.use(prefix, createAgentMiddleware(''));
		app.route(mount.path, mount.router);
	}
} else {
	// File-based routing: apply middleware to /api/* and mount discovered route files
	app.use('/api/*', createCorsMiddleware());
	app.use('/api/*', createOtelMiddleware());
	app.use('/api/*', createAgentMiddleware(''));
${routeImportsAndMounts.map((line) => `\t${line}`).join('\n')}
}
`
        : '';
    // Workbench API routes mounting
    // Always mounted - these routes are needed for the cloud workbench to communicate with deployed agents
    // Auth is handled by middleware inside the router (signature verification in production, no auth in development)
    // The hasWorkbenchConfig flag only controls whether the local workbench UI is served
    const workbenchApiMount = `
// Mount workbench API routes (/_agentuity/workbench/*)
// Always available for cloud workbench communication
// Auth is handled inside the router (signature verification in production)
const workbenchRouter = createWorkbenchRouter();
app.route('/', workbenchRouter);

// hasWorkbenchConfig controls whether the local workbench UI is served (dev mode only)
const hasWorkbenchConfig = ${hasWorkbenchConfig};
`;
    // Asset proxy routes - Always generated, but only active at runtime when:
    //   - NODE_ENV !== 'production' (isDevelopment())
    //   - and process.env.VITE_PORT is set
    const assetProxyRoutes = `
// Asset proxy routes - Development mode only (proxies to Vite asset server)
if (isDevelopment() && process.env.VITE_PORT) {
	const VITE_ASSET_PORT = parseInt(process.env.VITE_PORT, 10);

	const proxyToVite = async (c: Context, pathOverride?: string) => {
		const targetPath = pathOverride ?? c.req.path;
		const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}\${targetPath}\`;
		try {
			otel.logger.debug(\`[Proxy] \${c.req.method} \${c.req.path} -> Vite:\${VITE_ASSET_PORT}\${targetPath}\`);
			const res = await fetch(viteUrl, { signal: AbortSignal.timeout(10000) });
			otel.logger.debug(\`[Proxy] \${c.req.path} -> \${res.status} (\${res.headers.get('content-type')})\`);
			return new Response(res.body, {
				status: res.status,
				headers: res.headers,
			});
		} catch (err) {
			if (err instanceof Error && err.name === 'TimeoutError') {
				otel.logger.error(\`Vite proxy timeout: \${c.req.path}\`);
				return c.text('Vite asset server timeout', 504);
			}
			otel.logger.error(\`Failed to proxy to Vite: \${c.req.path} - \${err instanceof Error ? err.message : String(err)}\`);
			return c.text('Vite asset server error', 500);
		}
	};

	// HMR WebSocket proxy - enables hot reload through tunnels (*.agentuity.live)
	// This proxies the Vite HMR WebSocket connection from the Bun server to Vite
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	const viteHmrWebsocket = (globalThis as any).__AGENTUITY_VITE_HMR_WEBSOCKET__ = {
		// Map of client WebSocket -> Vite WebSocket
		connections: new Map<WebSocket, WebSocket>(),

		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		open(clientWs: any) {
			// Get the query string from ws.data (set during upgrade)
			const queryString = clientWs.data?.queryString || '';
			const viteWsUrl = \`ws://127.0.0.1:\${VITE_ASSET_PORT}/__vite_hmr\${queryString}\`;
			otel.logger.debug('[HMR Proxy] Client connected, opening connection to Vite at %s', viteWsUrl);

			// Connect to Vite with the 'vite-hmr' subprotocol (required by Vite)
			const viteWs = new WebSocket(viteWsUrl, ['vite-hmr']);

			viteWs.onopen = () => {
				otel.logger.debug('[HMR Proxy] Connected to Vite HMR server');
			};

			viteWs.onmessage = (event) => {
				// Forward messages from Vite to client
				if (clientWs.readyState === WebSocket.OPEN) {
					clientWs.send(event.data);
				}
			};

			viteWs.onerror = (error) => {
				otel.logger.error('[HMR Proxy] Vite WebSocket error: %s', error);
			};

			viteWs.onclose = () => {
				otel.logger.debug('[HMR Proxy] Vite WebSocket closed');
				viteHmrWebsocket.connections.delete(clientWs);
				if (clientWs.readyState === WebSocket.OPEN) {
					clientWs.close();
				}
			};

			viteHmrWebsocket.connections.set(clientWs, viteWs);
		},

		message(clientWs: WebSocket, message: string | Buffer) {
			// Forward messages from client to Vite
			const viteWs = viteHmrWebsocket.connections.get(clientWs);
			if (viteWs && viteWs.readyState === WebSocket.OPEN) {
				viteWs.send(message);
			}
		},

		close(clientWs: WebSocket) {
			otel.logger.debug('[HMR Proxy] Client WebSocket closed');
			const viteWs = viteHmrWebsocket.connections.get(clientWs);
			if (viteWs) {
				viteWs.close();
				viteHmrWebsocket.connections.delete(clientWs);
			}
		},
	};

	// Register HMR WebSocket route - must be before other routes
	app.get('/__vite_hmr', (c: Context) => {
		const upgradeHeader = c.req.header('upgrade');
		if (upgradeHeader?.toLowerCase() === 'websocket') {
			// Get the Bun server from context using Hono's pattern
			// When app.fetch(req, server) is called, Hono stores server as c.env
			// eslint-disable-next-line @typescript-eslint/no-explicit-any
			const server = 'server' in (c.env as any) ? (c.env as any).server : c.env;

			if (server?.upgrade) {
				// Extract query string to forward to Vite (includes token parameter)
				const url = new URL(c.req.url);
				const queryString = url.search; // Includes the '?' prefix

				const success = server.upgrade(c.req.raw, {
					data: { type: 'vite-hmr', queryString },
				});
				if (success) {
					otel.logger.debug('[HMR Proxy] WebSocket upgrade successful');
					return new Response(null);
				}
				otel.logger.error('[HMR Proxy] WebSocket upgrade returned false');
			} else {
				otel.logger.error('[HMR Proxy] Server upgrade method not available. c.env type: %s, keys: %s',
					typeof c.env,
					Object.keys(c.env || {}).join(', '));
			}
			return c.text('WebSocket upgrade failed', 500);
		}
		// Non-WebSocket request to HMR endpoint - proxy to Vite
		return proxyToVite(c);
	});

	// Vite client scripts and HMR
	app.get('/@vite/*', (c: Context) => proxyToVite(c));
	app.get('/@react-refresh', (c: Context) => proxyToVite(c));

	// Source files for HMR
	app.get('/src/web/*', (c: Context) => proxyToVite(c));
	app.get('/src/*', (c: Context) => proxyToVite(c)); // Catch-all for other source files

	// Workbench source files (in .agentuity/workbench-src/)
	app.get('/.agentuity/workbench-src/*', (c: Context) => proxyToVite(c));

	// Node modules (Vite transforms these)
	app.get('/node_modules/*', (c: Context) => proxyToVite(c));

	// Scoped packages (e.g., @agentuity/*, @types/*)
	app.get('/@*', (c: Context) => proxyToVite(c));

	// File system access (for Vite's @fs protocol)
	app.get('/@fs/*', (c: Context) => proxyToVite(c));

	// Module resolution (for Vite's @id protocol)
	app.get('/@id/*', (c: Context) => proxyToVite(c));

	// Static assets - Vite serves src/web/public/* at root, but code uses /public/* paths
	// In production, the plugin transforms /public/foo.svg to CDN URLs
	// Rewrite /public/foo.svg -> /foo.svg before proxying to Vite
	app.get('/public/*', (c: Context) => {
		const rootPath = c.req.path.replace(/^\\/public/, '');
		return proxyToVite(c, rootPath);
	});

	// Any .js, .jsx, .ts, .tsx files (catch remaining modules)
	app.get('/*.js', (c: Context) => proxyToVite(c));
	app.get('/*.jsx', (c: Context) => proxyToVite(c));
	app.get('/*.ts', (c: Context) => proxyToVite(c));
	app.get('/*.tsx', (c: Context) => proxyToVite(c));
	app.get('/*.css', (c: Context) => proxyToVite(c));
}
`;
    // Runtime mode detection helper (defined at top level for reuse)
    // Dynamic property access prevents Bun.build from inlining NODE_ENV at build time
    const modeDetection = `
// Runtime mode detection helper
// Dynamic string concatenation prevents Bun.build from inlining NODE_ENV at build time
// See: https://github.com/oven-sh/bun/issues/20183
const getEnv = (key: string) => process.env[key];
const isDevelopment = () => getEnv('NODE' + '_' + 'ENV') !== 'production';
`;
    // Web routes (runtime mode detection)
    let webRoutes = '';
    if (hasWebFrontend) {
        webRoutes = `
// Web routes - Runtime mode detection (dev proxies to Vite, prod serves static)
// Note: Session/thread cookies are set by /_agentuity/webanalytics/session.js (loaded via script tag)
// This keeps the HTML response static and cacheable

if (isDevelopment()) {
	// Development mode: Proxy HTML from Vite to enable React Fast Refresh
	const VITE_ASSET_PORT = parseInt(process.env.VITE_PORT || '${vitePort || 5173}', 10);
	
	const devHtmlHandler = async (c: Context) => {
		const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}/src/web/index.html\`;

		try {
			otel.logger.debug('[Proxy] GET /src/web/index.html -> Vite:%d', VITE_ASSET_PORT);
			const res = await fetch(viteUrl, { signal: AbortSignal.timeout(10000) });

			// Get HTML text and transform relative paths to absolute
			let html = await res.text();
			html = html
				.replace(/src="\\.\\//g, 'src="/src/web/')
				.replace(/href="\\.\\//g, 'href="/src/web/');

${analyticsEnabled
            ? `			// Inject analytics config and script (session/thread read from cookies by beacon)
			html = injectAnalytics(html, analyticsConfig);`
            : ''}

			return new Response(html, {
				status: res.status,
				headers: res.headers,
			});
		} catch (err) {
			otel.logger.error('Failed to proxy HTML to Vite: %s', err instanceof Error ? err.message : String(err));
			return c.text('Vite asset server error (HTML)', 500);
		}
	};
	
	app.get('/', devHtmlHandler);
	
	// 404 for unmatched API/system routes
	app.all('/_agentuity/*', (c: Context) => c.notFound());
	app.all('/api/*', (c: Context) => c.notFound());
	if (!(hasWorkbenchConfig && isDevelopment())) {
		app.all('/workbench/*', (c: Context) => c.notFound());
	}
	
	// SPA fallback - serve index.html for client-side routing
	app.get('*', async (c: Context) => {
		const path = c.req.path;
		// If path has a file extension, try proxying to Vite first (serves public files like robots.txt, llms.txt)
		// Fall back to 404 if Vite also returns 404
		if (/\\.[a-zA-Z0-9]+$/.test(path)) {
			try {
				const viteUrl = \`http://127.0.0.1:\${VITE_ASSET_PORT}\${path}\`;
				const res = await fetch(viteUrl, { signal: AbortSignal.timeout(10000) });
				if (res.status !== 404) {
					return new Response(res.body, { status: res.status, headers: res.headers });
				}
			} catch {
				// Vite unavailable, fall through to 404
			}
			return c.notFound();
		}
		return devHtmlHandler(c);
	});
} else {
	// Production mode: Serve static files from bundled output
	const indexHtmlPath = import.meta.dir + '/client/index.html';
	const baseIndexHtml = existsSync(indexHtmlPath)
		? readFileSync(indexHtmlPath, 'utf-8')
		: '';
	
	if (!baseIndexHtml) {
		otel.logger.warn('Production HTML not found at %s', indexHtmlPath);
	}

	const prodHtmlHandler = (c: Context) => {
		if (!baseIndexHtml) {
			return c.text('Production build incomplete', 500);
		}
${analyticsEnabled
            ? `		// Inject analytics config and script (session/thread loaded via session.js)
		const html = injectAnalytics(baseIndexHtml, analyticsConfig);
		return c.html(html);`
            : `		return c.html(baseIndexHtml);`}
	};
	
	app.get('/', prodHtmlHandler);

	// Serve static assets from /assets/* (Vite bundled output)
	app.use('/assets/*', serveStatic({ root: import.meta.dir + '/client', mimes: mimeTypes }));

	// Serve static public assets (favicon.ico, robots.txt, etc.)
	app.use('/*', serveStatic({ root: import.meta.dir + '/client', rewriteRequestPath: (path) => path, mimes: mimeTypes }));

	// 404 for unmatched API/system routes (IMPORTANT: comes before SPA fallback)
	app.all('/_agentuity/*', (c: Context) => c.notFound());
	app.all('/api/*', (c: Context) => c.notFound());
	if (!(hasWorkbenchConfig && isDevelopment())) {
		app.all('/workbench/*', (c: Context) => c.notFound());
	}

	// SPA fallback with asset protection
	app.get('*', (c: Context) => {
		const path = c.req.path;
		// If path has a file extension, it's likely an asset request - return 404
		if (/\\.[a-zA-Z0-9]+$/.test(path)) {
			return c.notFound();
		}
		return prodHtmlHandler(c);
	});
}
`;
    }
    // Workbench UI routes (development only)
    // The workbench UI is only served in development mode; the API routes are always available
    const workbenchRoute = workbench?.route ?? '/workbench';
    const workbenchRoutes = `
// Workbench UI is only available in development mode (API routes are always available)
if (hasWorkbenchConfig && isDevelopment()) {
	const workbenchSrcDir = import.meta.dir + '/workbench-src';
	const workbenchIndexPath = import.meta.dir + '/workbench-src/index.html';
	app.get('${workbenchRoute}', async (c: Context) => {
		const html = await Bun.file(workbenchIndexPath).text();
		// Rewrite script/css paths to use Vite's @fs protocol
		const withVite = html
			.replace('src="./main.tsx"', \`src="/@fs\${workbenchSrcDir}/main.tsx"\`)
			.replace('href="./styles.css"', \`href="/@fs\${workbenchSrcDir}/styles.css"\`);
		return c.html(withVite);
	});
}
`;
    // Server startup (same for dev and prod - Bun.serve with native WebSocket)
    const serverStartup = `
// Start Bun server
if (typeof Bun !== 'undefined') {
	// Enable process exit protection now that we're starting the server
	enableProcessExitProtection();

	const port = parseInt(process.env.PORT || '3500', 10);

	// Create custom WebSocket handler that supports both regular WebSockets and HMR proxy
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	const hmrHandler = (globalThis as any).__AGENTUITY_VITE_HMR_WEBSOCKET__;
	const customWebsocket = {
		...websocket,
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		open(ws: any) {
			// Check if this is an HMR connection
			if (ws.data?.type === 'vite-hmr' && hmrHandler) {
				hmrHandler.open(ws);
			} else if (websocket.open) {
				websocket.open(ws);
			}
		},
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		message(ws: any, message: string | Buffer) {
			// Check if this is an HMR connection
			if (ws.data?.type === 'vite-hmr' && hmrHandler) {
				hmrHandler.message(ws, message);
			} else if (websocket.message) {
				websocket.message(ws, message);
			}
		},
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		close(ws: any, code?: number, reason?: string) {
			// Check if this is an HMR connection
			if (ws.data?.type === 'vite-hmr' && hmrHandler) {
				hmrHandler.close(ws);
			} else if (websocket.close) {
				websocket.close(ws, code, reason);
			}
		},
	};

	const server = Bun.serve({
		fetch: (req, server) => {
			// Get timeout from config on each request (0 = no timeout)
			server.timeout(req, getAppConfig()?.requestTimeout ?? 0);
			return app.fetch(req, server);
		},
		websocket: customWebsocket,
		port,
		hostname: '127.0.0.1',
		development: isDevelopment(),
	});
	
	// Make server available globally for health checks
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	(globalThis as any).__AGENTUITY_SERVER__ = server;
	
	otel.logger.info(\`Server listening on http://127.0.0.1:\${port}\`);
	if (isDevelopment() && process.env.VITE_PORT) {
		otel.logger.debug(\`Proxying Vite assets from port \${process.env.VITE_PORT}\`);
	}

	// Register signal handlers for graceful shutdown (production only)
	// Dev mode has its own handlers in devmode.ts
	if (!isDevelopment()) {
		const handleShutdown = async (signal: string) => {
			otel.logger.info(\`Received \${signal}, initiating graceful shutdown...\`);
			try {
				await runShutdown();
				otel.logger.info('Shutdown complete');
			} catch (err) {
				otel.logger.error(\`Error during shutdown: \${err instanceof Error ? err.message : String(err)}\`);
			}
			process.exit(0);
		};

		process.once('SIGTERM', () => handleShutdown('SIGTERM'));
		process.once('SIGINT', () => handleShutdown('SIGINT'));
	}
}

// FOUND AN ERROR IN THIS FILE?
// Please file an issue at https://github.com/agentuity/sdk/issues
// or if you know the fix please submit a PR!
`;
    const healthRoutes = `
// Health check routes (production only)
if (!isDevelopment()) {
	const healthHandler = (c: Context) => {
		return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
	};
	const idleHandler = (c: Context) => {
		// Check if server is idle (no pending requests/connections)
		// eslint-disable-next-line @typescript-eslint/no-explicit-any
		const server = (globalThis as any).__AGENTUITY_SERVER__;
		if (!server) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
		
		// Check for pending background tasks
		if (hasWaitUntilPending()) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
		
		if (server.pendingRequests > 1) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
		if (server.pendingWebSockets > 0) return c.text('NO', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
		
		return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
	};
	app.get('/_agentuity/health', healthHandler);
	app.get('/_health', healthHandler);
	app.get('/_agentuity/idle', idleHandler);
	app.get('/_idle', idleHandler);
}

// Dev readiness check - verifies Vite asset server is ready to serve frontend
if (isDevelopment()) {
	app.get('/_agentuity/ready', async (c: Context) => {
		const vitePort = process.env.VITE_PORT;
		if (!vitePort) {
			// No Vite port means we're not using Vite proxy
			return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
		}

		try {
			// Probe Vite to check if it can serve the main entry point
			// Use @vite/client as a lightweight check - it's always available
			const viteUrl = \`http://127.0.0.1:\${vitePort}/@vite/client\`;
			const res = await fetch(viteUrl, {
				signal: AbortSignal.timeout(5000),
				method: 'HEAD'
			});

			if (res.ok) {
				return c.text('OK', 200, { 'Content-Type': 'text/plain; charset=utf-8' });
			}
			return c.text('VITE_NOT_READY', 503, { 'Content-Type': 'text/plain; charset=utf-8' });
		} catch (err) {
			otel.logger.debug('Vite readiness check failed: %s', err instanceof Error ? err.message : String(err));
			return c.text('VITE_NOT_READY', 503, { 'Content-Type': 'text/plain; charset=utf-8' });
		}
	});
}
`;
    const devPatchesStep = noBundle
        ? `
// Step 0.1: Apply runtime dev patches (--experimental-no-bundle mode)
// Replaces build-time Bun.build patches for LLM gateway routing, AI SDK telemetry, and OTel spans
if (isDevelopment()) {
	await applyDevPatches();
}
`
        : '';
    const code = `// @generated
// Auto-generated by Agentuity
// DO NOT EDIT - This file is regenerated on every build
// Supports both development and production modes via runtime detection
${imports.join('\n')}

${modeDetection}

// Step 0: Bootstrap runtime environment (load profile-specific .env files)
// Only in development - production env vars are injected by platform
// This must happen BEFORE any imports that depend on environment variables
if (isDevelopment()) {
	// Pass project directory (two levels up from src/generated/) so .env files are loaded correctly
	await bootstrapRuntimeEnv({ projectDir: import.meta.dir + '/../..' });
}

${devPatchesStep}

// Step 0.25: load our runtime metadata and cache it
loadBuildMetadata();

// Step 0.5: Patch Bun's S3 client for Agentuity storage endpoints
// Agentuity storage uses virtual-hosted-style URLs (*.storage.dev)
// This patches s3.file() to automatically set virtualHostedStyle: true
patchBunS3ForStorageDev();

// Step 1: Initialize telemetry and services
const serverUrl = \`http://127.0.0.1:\${process.env.PORT || '3500'}\`;
const otel = register({ processors: getSpanProcessors(), logLevel: (process.env.AGENTUITY_LOG_LEVEL || 'info') as LogLevel });

// Step 2: Create router and set as global
const app = createRouter();
setGlobalRouter(app);

// Step 3: Apply middleware in correct order (BEFORE mounting routes)
// Compression runs first (outermost) so it can compress the final response
app.use('*', createCompressionMiddleware());

app.use('*', createBaseMiddleware({
	logger: otel.logger,
	tracer: otel.tracer,
	meter: otel.meter,
}));

// Workbench routes always get OTel middleware for session tracking
app.use('/_agentuity/workbench/*', createOtelMiddleware());

// Note: /api/* middleware (CORS, OTel, agent context) is applied in Step 6
// after app.ts import, so user-provided routers can specify custom prefixes.

// Step 4: Import user's app.ts (runs createApp, gets state/config)
await import('../../app.js');

// Step 4.5: Import agent registry to ensure all agents are registered
// This is needed for workbench metadata to return JSON schemas
await import('./registry.js');

// Step 5: Initialize providers
const appState = getAppState();
const appConfig = getAppConfig();

createServices(otel.logger, appConfig, serverUrl);

// Make logger and tracer globally available for user's app.ts
setGlobalLogger(otel.logger);
setGlobalTracer(otel.tracer);

const threadProvider = getThreadProvider();
const sessionProvider = getSessionProvider();

await threadProvider.initialize(appState);
await sessionProvider.initialize(appState);

// Step 6: Mount routes (AFTER middleware is applied)

${healthRoutes}

${analyticsEnabled
        ? `// Register analytics routes
registerAnalyticsRoutes(app);`
        : ''}

${assetProxyRoutes}
${apiMount}
${workbenchApiMount}
${workbenchRoutes}
${webRoutes}

// Step 7: Run agent setup to signal completion
await runAgentSetups(appState);

${serverStartup}
`;
    // Collapse 2+ consecutive empty lines into 1 empty line (3+ \n becomes 2 \n)
    const cleanedCode = code.replace(/\n{3,}/g, '\n\n');
    await Bun.write(entryPath, cleanedCode);
    logger.trace(`Generated unified entry file at %s (mode: ${mode})`, entryPath);
}
//# sourceMappingURL=entry-generator.js.map