import { loadEnvironmentFiles } from "../env.js";
import { generateCompliantPassword } from "../auth/password-policy.js";
import { copyFileSync, existsSync, openSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import dotenv from "dotenv";
import { generateKeyPairSync, randomBytes } from "node:crypto";
import { promisify } from "node:util";
import { exec, spawn } from "node:child_process";
import { readFile, writeFile } from "node:fs/promises";

//#region src/setup/setup.ts
const colors = {
	reset: "\x1B[0m",
	bright: "\x1B[1m",
	dim: "\x1B[2m",
	green: "\x1B[32m",
	yellow: "\x1B[33m",
	red: "\x1B[31m",
	blue: "\x1B[34m",
	cyan: "\x1B[36m"
};
function logStep(step, message) {
	console.log(`${colors.bright}${colors.blue}[Step ${step}]${colors.reset} ${message}`);
}
function logSuccess(message) {
	console.log(`${colors.green}✓${colors.reset} ${message}`);
}
function logError(message, error) {
	console.error(`${colors.red}✗ ${message}${colors.reset}`);
	if (error) {
		const msg = error instanceof Error ? error.message : String(error);
		console.error(`${colors.dim}  Error details: ${msg}${colors.reset}`);
	}
}
function logWarning(message) {
	console.warn(`${colors.yellow}⚠${colors.reset} ${message}`);
}
function logInfo(message) {
	console.log(`${colors.cyan}ℹ${colors.reset} ${message}`);
}
const execAsync = promisify(exec);
function isLocalDatabaseUrl(url) {
	if (!url) return false;
	return /[@/](localhost|127\.0\.0\.1)[:/]/.test(url);
}
function hasExternalDatabases() {
	const manageUrl = process.env.INKEEP_AGENTS_MANAGE_DATABASE_URL;
	const runUrl = process.env.INKEEP_AGENTS_RUN_DATABASE_URL;
	if (!manageUrl || !runUrl) return false;
	if (process.env.CI) return true;
	return !isLocalDatabaseUrl(manageUrl) || !isLocalDatabaseUrl(runUrl);
}
const REQUIRED_PORTS = [
	{
		port: 5432,
		service: "DoltgreSQL"
	},
	{
		port: 5433,
		service: "PostgreSQL"
	},
	{
		port: 5434,
		service: "SpiceDB PostgreSQL"
	}
];
async function checkPort(port) {
	try {
		await execAsync(`lsof -i :${port} -sTCP:LISTEN -t`, { timeout: 5e3 });
		return true;
	} catch {
		return false;
	}
}
async function getContainerOnPort(port) {
	try {
		const { stdout } = await execAsync(`docker ps --format '{{.Names}}' --filter "publish=${port}"`, { timeout: 5e3 });
		const name = stdout.trim();
		if (!name) return null;
		let project = null;
		try {
			const { stdout: labelOut } = await execAsync(`docker inspect ${name} --format '{{index .Config.Labels "com.docker.compose.project"}}'`, { timeout: 5e3 });
			project = labelOut.trim() || null;
		} catch {}
		return {
			name,
			project
		};
	} catch {
		return null;
	}
}
async function runPreflightChecks(composeFile) {
	const issues = [];
	const fixes = [];
	try {
		await execAsync("docker info", { timeout: 1e4 });
	} catch {
		if (hasExternalDatabases()) {
			logWarning("Docker is not available, but database URLs point to external hosts");
			logInfo("Assuming databases are managed externally.");
			return {
				passed: true,
				dockerAvailable: false,
				portsAvailable: false,
				composeCmd: ""
			};
		}
		logError("Docker is not running or not installed.");
		logInfo("  Start Docker Desktop, or install Docker: https://docs.docker.com/get-docker/");
		process.exit(1);
	}
	let composeCmd = "docker-compose";
	try {
		await execAsync("docker-compose version", { timeout: 5e3 });
	} catch {
		try {
			await execAsync("docker compose version", { timeout: 5e3 });
			composeCmd = "docker compose";
		} catch {
			if (hasExternalDatabases()) {
				logWarning("Docker Compose not available, but database URLs point to external hosts");
				logInfo("Assuming databases are managed externally.");
				return {
					passed: true,
					dockerAvailable: true,
					portsAvailable: false,
					composeCmd
				};
			}
			logError("Docker Compose is not installed.");
			logInfo("  Install it: https://docs.docker.com/compose/install/");
			process.exit(1);
		}
	}
	let ownProjectPrefix;
	try {
		const { stdout } = await execAsync(`${composeCmd} -f ${composeFile} config --format json`, { timeout: 1e4 });
		ownProjectPrefix = (JSON.parse(stdout).name || "").replace(/[^a-z0-9]/g, "");
	} catch {
		ownProjectPrefix = (process.cwd().split("/").pop() || "").replace(/[^a-z0-9]/g, "").toLowerCase();
	}
	const occupiedPorts = [];
	await Promise.all(REQUIRED_PORTS.map(async ({ port, service }) => {
		if (!await checkPort(port)) return;
		const info = await getContainerOnPort(port);
		if (!info) return;
		if (info.name.replace(/[^a-z0-9]/g, "").toLowerCase().startsWith(ownProjectPrefix)) return;
		occupiedPorts.push({
			port,
			service,
			container: info.name,
			project: info.project
		});
	}));
	if (occupiedPorts.length > 0) {
		issues.push(`Port${occupiedPorts.length > 1 ? "s" : ""} occupied by another Docker project:`);
		for (const { port, service, container } of occupiedPorts) issues.push(`  :${port} (${service}) → ${container}`);
		const projectNames = new Set(occupiedPorts.map(({ project }) => project).filter((p) => p !== null));
		for (const project of projectNames) fixes.push(`  docker compose -p ${project} down`);
		const standaloneContainers = occupiedPorts.filter(({ project }) => project === null).map(({ container }) => container);
		for (const name of standaloneContainers) fixes.push(`  docker rm -f ${name}`);
	}
	if (issues.length > 0) {
		logWarning("Pre-flight check: port conflicts detected");
		for (const issue of issues) console.log(`${colors.yellow}  ${issue}${colors.reset}`);
		console.log(`${colors.cyan}  Fix:${colors.reset}`);
		for (const fix of fixes) console.log(`${colors.cyan}  ${fix}${colors.reset}`);
		console.log();
		logInfo("Databases on those ports will be used as-is. Continuing with setup...");
		return {
			passed: true,
			dockerAvailable: true,
			portsAvailable: false,
			composeCmd
		};
	}
	return {
		passed: true,
		dockerAvailable: true,
		portsAvailable: true,
		composeCmd
	};
}
const SETUP_COMPLETE_FILE = ".setup-complete";
async function ensureEnvFile() {
	if (!existsSync(".env") && existsSync(".env.example")) {
		copyFileSync(".env.example", ".env");
		logSuccess("Created .env from .env.example");
	}
}
async function generateSecrets() {
	const envContent = await readFile(".env", "utf-8").catch(() => "");
	const lines = envContent.split("\n");
	let modified = false;
	if (!(envContent.includes("INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY=") && !envContent.includes("# INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY=") && !!envContent.match(/INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY=(.+)/)?.[1]?.trim())) try {
		const { privateKey, publicKey } = generateKeyPairSync("rsa", {
			modulusLength: 2048,
			privateKeyEncoding: {
				type: "pkcs8",
				format: "pem"
			},
			publicKeyEncoding: {
				type: "spki",
				format: "pem"
			}
		});
		const privateKeyBase64 = Buffer.from(privateKey).toString("base64");
		const publicKeyBase64 = Buffer.from(publicKey).toString("base64");
		let privateKeyFound = false;
		let publicKeyFound = false;
		for (let i = 0; i < lines.length; i++) {
			if (lines[i].startsWith("# INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY=") || lines[i].startsWith("INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY=")) {
				lines[i] = `INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY=${privateKeyBase64}`;
				process.env.INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY = privateKeyBase64;
				privateKeyFound = true;
			}
			if (lines[i].startsWith("# INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY=") || lines[i].startsWith("INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY=")) {
				lines[i] = `INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY=${publicKeyBase64}`;
				process.env.INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY = publicKeyBase64;
				publicKeyFound = true;
			}
		}
		if (!privateKeyFound) {
			lines.push(`INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY=${privateKeyBase64}`);
			process.env.INKEEP_AGENTS_TEMP_JWT_PRIVATE_KEY = privateKeyBase64;
		}
		if (!publicKeyFound) {
			lines.push(`INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY=${publicKeyBase64}`);
			process.env.INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY = publicKeyBase64;
		}
		modified = true;
		logSuccess("JWT keys generated and added to .env");
	} catch (error) {
		logError("Failed to generate JWT keys - playground may not work", error);
		logInfo("You can manually run: pnpm run generate-jwt-keys");
	}
	else logInfo("JWT keys already configured, skipping generation");
	const secretDefs = [
		{
			varName: "INKEEP_AGENTS_JWT_SIGNING_SECRET",
			placeholders: [],
			generate: () => randomBytes(32).toString("hex")
		},
		{
			varName: "BETTER_AUTH_SECRET",
			placeholders: ["your-secret-key-change-in-production"],
			generate: () => randomBytes(32).toString("hex")
		},
		{
			varName: "INKEEP_AGENTS_MANAGE_UI_PASSWORD",
			placeholders: ["change-me-Pass!@1234"],
			generate: () => generateCompliantPassword()
		},
		{
			varName: "INKEEP_ANON_JWT_SECRET",
			placeholders: [],
			generate: () => randomBytes(32).toString("hex")
		}
	];
	for (const { varName, placeholders, generate } of secretDefs) {
		let found = false;
		for (let i = 0; i < lines.length; i++) if (lines[i].startsWith(`# ${varName}=`) || lines[i].startsWith(`${varName}=`)) {
			found = true;
			if (lines[i].startsWith(`# ${varName}=`)) {
				const value = generate();
				lines[i] = `${varName}=${value}`;
				process.env[varName] = value;
				modified = true;
				break;
			}
			const eqIdx = lines[i].indexOf("=");
			const currentValue = lines[i].substring(eqIdx + 1).trim();
			if (currentValue === "" || placeholders.includes(currentValue)) {
				const value = generate();
				lines[i] = `${varName}=${value}`;
				process.env[varName] = value;
				modified = true;
			}
			break;
		}
		if (!found) {
			const value = generate();
			lines.push(`${varName}=${value}`);
			process.env[varName] = value;
			modified = true;
		}
	}
	if (modified) {
		await writeFile(".env", lines.join("\n"));
		logSuccess("Secrets generated and added to .env");
	}
}
async function waitForDockerHealth(composeFile, serviceName, composeCmd, timeout = 3e4) {
	const start = Date.now();
	let lastError = null;
	while (Date.now() - start < timeout) {
		try {
			const { stdout } = await execAsync(`docker inspect --format='{{.State.Health.Status}}' $(${composeCmd} -f ${composeFile} ps -q ${serviceName})`);
			if (stdout.trim() === "healthy") return;
		} catch (error) {
			lastError = error;
		}
		await new Promise((resolve$1) => setTimeout(resolve$1, 1e3));
	}
	const errorMsg = lastError instanceof Error ? lastError.message : String(lastError);
	throw new Error(`${serviceName} not healthy after ${timeout}ms${lastError ? ` (last error: ${errorMsg})` : ""}`);
}
async function startDockerDatabases(composeFile, composeCmd) {
	logInfo("Starting database containers...");
	try {
		await execAsync(`${composeCmd} -f ${composeFile} up -d`, { timeout: 12e4 });
		logSuccess("Database containers started");
	} catch (error) {
		const combined = `${error instanceof Error ? error.message : String(error)}\n${error?.stderr || ""}`;
		const isTimeout = error?.killed === true;
		const isPortConflict = !isTimeout && (combined.includes("port is already allocated") || combined.includes("address already in use") || combined.includes("Bind for 0.0.0.0"));
		if (isTimeout) {
			logWarning("docker compose up timed out — containers may still be starting");
			logInfo("Will check health status below...");
		} else if (isPortConflict) {
			logWarning("Database port conflict during startup (databases might already be running)");
			logInfo("Continuing with setup...");
			return;
		} else {
			logError("Failed to start database containers", error);
			process.exit(1);
		}
	}
	logInfo("Polling Docker health status...");
	const [doltgresResult, postgresResult, spicedbResult] = await Promise.allSettled([
		waitForDockerHealth(composeFile, "doltgres-db", composeCmd, 6e4),
		waitForDockerHealth(composeFile, "postgres-db", composeCmd, 3e4),
		waitForDockerHealth(composeFile, "spicedb-postgres", composeCmd, 3e4)
	]);
	if (doltgresResult.status === "fulfilled") logSuccess("DoltgreSQL is healthy");
	else logWarning(`DoltgreSQL health check timed out: ${doltgresResult.reason.message}`);
	if (postgresResult.status === "fulfilled") logSuccess("PostgreSQL is healthy");
	else logWarning(`PostgreSQL health check timed out: ${postgresResult.reason.message}`);
	if (spicedbResult.status === "fulfilled") logSuccess("SpiceDB PostgreSQL is healthy");
	else logWarning(`SpiceDB PostgreSQL health check timed out: ${spicedbResult.reason.message}`);
	if (doltgresResult.status === "rejected" && postgresResult.status === "rejected") {
		logError("Both primary databases failed health checks - cannot proceed");
		process.exit(1);
	}
	logSuccess("Database health checks complete");
}
async function runMigrations(config) {
	const isFirstRun = !existsSync(SETUP_COMPLETE_FILE);
	if (config.upgradeCommand && !isFirstRun && process.env.SKIP_UPGRADE !== "true") {
		logInfo("Running database migrations and upgrading packages");
		try {
			const { stdout } = await execAsync(config.upgradeCommand);
			if (stdout) console.log(`${colors.dim}  ${stdout.trim()}${colors.reset}`);
			logSuccess("Upgrades completed");
		} catch (error) {
			logError("Failed to run upgrades", error);
			logWarning("This may cause issues. Consider checking your database schema.");
		}
		return;
	}
	logInfo(isFirstRun ? "Fresh install detected - running migrations only" : "Running migrations...");
	const execOpts = {
		cwd: process.cwd(),
		env: process.env
	};
	const [manageResult, runResult] = await Promise.allSettled([execAsync(config.manageMigrateCommand, execOpts), execAsync(config.runMigrateCommand, execOpts)]);
	if (manageResult.status === "fulfilled") logSuccess("Manage database migrations completed");
	else logWarning(`Manage migrations failed: ${manageResult.reason.message}`);
	if (runResult.status === "fulfilled") logSuccess("Runtime database migrations completed");
	else logWarning(`Runtime migrations failed: ${runResult.reason.message}`);
	if (manageResult.status === "rejected" && runResult.status === "rejected") {
		logError("Both database migrations failed");
		process.exit(1);
	}
	if (manageResult.status === "fulfilled" && runResult.status === "fulfilled") writeFileSync(SETUP_COMPLETE_FILE, (/* @__PURE__ */ new Date()).toISOString());
	else logWarning(`Partial migration success — ${SETUP_COMPLETE_FILE} not written so next run retries`);
}
async function initAuth(authInitCommand) {
	if (process.env.INKEEP_AGENTS_MANAGE_UI_USERNAME && process.env.INKEEP_AGENTS_MANAGE_UI_PASSWORD && process.env.BETTER_AUTH_SECRET) {
		logInfo("Initializing default organization and admin user...");
		try {
			await execAsync(authInitCommand);
			logSuccess("Auth initialization complete");
		} catch (error) {
			logWarning(`Auth initialization failed - you may need to run manually: ${authInitCommand}`);
			logInfo(`Error: ${error instanceof Error ? error.message : String(error)}`);
		}
	} else {
		logWarning("Skipping auth initialization - credentials not configured");
		logInfo("To create a default admin user, set in .env:");
		logInfo("  INKEEP_AGENTS_MANAGE_UI_USERNAME=admin@example.com");
		logInfo("  INKEEP_AGENTS_MANAGE_UI_PASSWORD=your-password");
		logInfo("  BETTER_AUTH_SECRET=your-secret-key");
		logInfo(`Then run: ${authInitCommand}`);
	}
}
async function checkServerRunning(url, timeout = 5e3) {
	try {
		const response = await fetch(url, { signal: AbortSignal.timeout(timeout) });
		return response.ok || response.status === 204;
	} catch {
		return false;
	}
}
async function waitForServerReady(url, timeout = 18e4) {
	const start = Date.now();
	let lastError = null;
	while (Date.now() - start < timeout) {
		try {
			const response = await fetch(url, { signal: AbortSignal.timeout(5e3) });
			if (response.ok || response.status === 204) return;
			lastError = `HTTP ${response.status}`;
		} catch (error) {
			if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) lastError = "fetch timeout (>5s per attempt)";
			else lastError = error instanceof Error ? error.message : String(error);
		}
		await new Promise((resolve$1) => setTimeout(resolve$1, 1e3));
	}
	throw new Error(`Server not ready at ${url} after ${timeout}ms. Last error: ${lastError}`);
}
async function startServersIfNeeded(config) {
	const result = {
		startedApi: false,
		startedUi: false,
		apiPid: null,
		uiPid: null
	};
	if (!config.apiHealthUrl) return result;
	if (await checkServerRunning(config.apiHealthUrl)) logSuccess("API server already running");
	else if (config.devApiCommand) {
		logInfo("Starting API server temporarily...");
		const apiLogPath = join(tmpdir(), `setup-dev-api-${Date.now()}.log`);
		const apiLogFd = openSync(apiLogPath, "a");
		const proc = spawn("sh", ["-c", config.devApiCommand], {
			stdio: [
				"ignore",
				apiLogFd,
				apiLogFd
			],
			detached: true,
			cwd: process.cwd()
		});
		proc.unref();
		result.startedApi = true;
		result.apiPid = proc.pid ?? null;
		logSuccess(`API server process started (PID: ${proc.pid})`);
		logInfo(`API server logs: ${apiLogPath}`);
	}
	if (config.uiHealthUrl) {
		if (await checkServerRunning(config.uiHealthUrl)) logSuccess("Dashboard already running");
		else if (config.devUiCommand) {
			logInfo("Starting Dashboard temporarily...");
			const uiLogPath = join(tmpdir(), `setup-dev-ui-${Date.now()}.log`);
			const uiLogFd = openSync(uiLogPath, "a");
			const proc = spawn("sh", ["-c", config.devUiCommand], {
				stdio: [
					"ignore",
					uiLogFd,
					uiLogFd
				],
				detached: true,
				cwd: process.cwd(),
				env: process.env
			});
			proc.unref();
			result.startedUi = true;
			result.uiPid = proc.pid ?? null;
			logSuccess(`Dashboard process started (PID: ${proc.pid})`);
			logInfo(`Dashboard logs: ${uiLogPath}`);
		}
	}
	const waitPromises = [];
	if (result.startedApi && config.apiHealthUrl) {
		logInfo(`Waiting for API at ${config.apiHealthUrl}...`);
		waitPromises.push(waitForServerReady(config.apiHealthUrl));
	}
	if (result.startedUi && config.uiHealthUrl) {
		logInfo(`Waiting for Dashboard at ${config.uiHealthUrl}...`);
		waitPromises.push(waitForServerReady(config.uiHealthUrl));
	}
	if (waitPromises.length > 0) {
		const results = await Promise.allSettled(waitPromises);
		for (const r of results) if (r.status === "rejected") logWarning(`Server readiness check failed: ${r.reason.message}`);
	}
	return result;
}
function stopProcessGroup(pid, name) {
	try {
		process.kill(-pid, "SIGTERM");
	} catch {}
	try {
		process.kill(pid, "SIGTERM");
	} catch {}
	setTimeout(() => {
		try {
			process.kill(-pid, "SIGKILL");
		} catch {}
		try {
			process.kill(pid, "SIGKILL");
		} catch {}
	}, 2e3);
	logSuccess(`${name} stopped`);
}
async function pushProject(pushConfig) {
	logInfo(`Pushing project: ${pushConfig.projectPath}`);
	const pushEnv = pushConfig.apiKey ? {
		...process.env,
		INKEEP_CI: "true",
		INKEEP_API_KEY: pushConfig.apiKey,
		INKEEP_TENANT_ID: process.env.INKEEP_TENANT_ID || "default",
		...pushConfig.apiUrl ? { INKEEP_AGENTS_API_URL: pushConfig.apiUrl } : {}
	} : { ...process.env };
	try {
		const { stdout } = await execAsync(`pnpm exec inkeep push --project ${pushConfig.projectPath} --config ${pushConfig.configPath}`, {
			env: pushEnv,
			cwd: process.cwd()
		});
		if (stdout) console.log(`${colors.dim}${stdout.trim()}${colors.reset}`);
		logSuccess("Project pushed successfully");
		return true;
	} catch (error) {
		logError("Project push failed", error);
		const err = error;
		if (err.stdout?.trim()) console.error(`${colors.dim}  stdout:\n${err.stdout.trim()}${colors.reset}`);
		if (err.stderr?.trim()) console.error(`${colors.dim}  stderr:\n${err.stderr.trim()}${colors.reset}`);
		logWarning("The project may not have been seeded. You can manually run:");
		logInfo(`  pnpm exec inkeep push --project ${pushConfig.projectPath} --config ${pushConfig.configPath}`);
		return false;
	}
}
async function runSetup(config) {
	console.log(`\n${colors.bright}=== Project Setup ===${colors.reset}\n`);
	logStep(1, "Checking environment configuration");
	await ensureEnvFile();
	loadEnvironmentFiles();
	dotenv.config();
	if (!config.isCloud) {
		const missing = [];
		if (!process.env.INKEEP_AGENTS_MANAGE_DATABASE_URL) missing.push("INKEEP_AGENTS_MANAGE_DATABASE_URL");
		if (!process.env.INKEEP_AGENTS_RUN_DATABASE_URL) missing.push("INKEEP_AGENTS_RUN_DATABASE_URL");
		if (missing.length > 0) {
			logError("Missing required database environment variables:");
			for (const v of missing) logInfo(`  - ${v}`);
			logInfo("Check your .env file and ensure these variables are set.");
			process.exit(1);
		}
	}
	logStep(2, "Checking secrets");
	await generateSecrets();
	if (config.isCloud) logStep(3, "Cloud setup: Skipping Docker database startup");
	else if (config.skipDocker) {
		logStep(3, "Skipping Docker database startup");
		logInfo("Using externally managed databases from configured URLs.");
	} else {
		logStep(3, "Starting databases with Docker");
		const preflight = await runPreflightChecks(config.dockerComposeFile);
		if (preflight.dockerAvailable && preflight.portsAvailable) await startDockerDatabases(config.dockerComposeFile, preflight.composeCmd);
		else if (preflight.dockerAvailable && !preflight.portsAvailable) logInfo("Skipping Docker startup — using databases on existing ports.");
	}
	logStep(4, "Running database migrations");
	await runMigrations(config);
	logStep(5, "Initializing authentication");
	await initAuth(config.authInitCommand);
	const pushConfigs = config.pushProject ? Array.isArray(config.pushProject) ? config.pushProject : [config.pushProject] : [];
	if (pushConfigs.length > 0 && !config.skipPush) {
		logStep(6, "Checking server availability");
		const servers = await startServersIfNeeded(config);
		let allPushSuccess = true;
		try {
			logStep(7, "Pushing projects to API");
			for (const push of pushConfigs) if (!await pushProject({
				...push,
				apiKey: push.apiKey || process.env.INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET
			})) allPushSuccess = false;
			if (config.afterPush) {
				const apiUrl = config.apiHealthUrl?.replace("/health", "") || "http://localhost:3002";
				await config.afterPush(apiUrl);
			}
		} finally {
			if (servers.startedApi || servers.startedUi) {
				logStep(8, "Cleaning up temporarily started servers");
				if (servers.startedApi && servers.apiPid) stopProcessGroup(servers.apiPid, "API server");
				if (servers.startedUi && servers.uiPid) stopProcessGroup(servers.uiPid, "Dashboard");
			}
		}
		console.log(`\n${colors.bright}=== Setup Complete ===${colors.reset}\n`);
		if (allPushSuccess) logSuccess("All steps completed successfully!");
		else logWarning("Setup completed with some warnings. See details above.");
	} else {
		console.log(`\n${colors.bright}=== Setup Complete ===${colors.reset}\n`);
		logSuccess("Database setup completed!");
		if (pushConfigs.length === 0) logInfo("No project push configured. Run \"pnpm dev\" to start development servers.");
	}
}

//#endregion
export { runSetup };