#!/usr/bin/env node

import chalk from "chalk";
import { spawn, SpawnOptions } from "child_process";
import { Command } from "commander";
import { readFileSync } from "fs";
import { dirname, join } from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

// Read version from package.json (go up one level if in dist directory)
const rootDir = __dirname.endsWith("dist") ? dirname(__dirname) : __dirname;
const packageJsonPath = join(rootDir, "package.json");
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
const version = packageJson.version;

const program = new Command();

// generated by figlet.textSync('Mongoku', { horizontalLayout: 'full' }))

// __  __                                   _
// |  \/  |   ___    _ __     __ _    ___   | | __  _   _
// | |\/| |  / _ \  | '_ \   / _` |  / _ \  | |/ / | | | |
// | |  | | | (_) | | | | | | (_| | | (_) | |   <  | |_| |
// |_|  |_|  \___/  |_| |_|  \__, |  \___/  |_|\_\  \__,_|
//                           |___/

const asciiArt = [
	"__  __                                   _            ",
	"|  \\/  |   ___    _ __     __ _    ___   | | __  _   _ ",
	"| |\\/| |  / _ \\  | '_ \\   / _` |  / _ \\  | |/ / | | | |",
	"| |  | | | (_) | | | | | | (_| | | (_) | |   <  | |_| |",
	"|_|  |_|  \\___/  |_| |_|  \\__, |  \\___/  |_|\\_\\  \\__,_|",
	"                           |___/                        ",
].join("\n");

program.name("mongoku").description("MongoDB client for the web").version(version);

// Custom error handler for better error messages
program.configureOutput({
	outputError: (str, write) => {
		// Check for common mistakes
		if (str.includes("too many arguments")) {
			const args = process.argv.slice(2);
			if (args.includes("pm2")) {
				write(chalk.red("❌ Error: Did you mean '--pm2' instead of 'pm2'?\n"));
				return;
			}
		}
		write(chalk.red(str));
	},
});

// Start action handler (shared between default and explicit 'start' command)
async function startAction(options: { pm2?: boolean; port?: string; readonly?: boolean }) {
	console.log(chalk.cyan(asciiArt));

	const port = options.port || process.env.MONGOKU_SERVER_PORT || "3100";
	process.env.MONGOKU_SERVER_PORT = port;
	if (!process.env.MONGOKU_SERVER_ORIGIN) {
		process.env.MONGOKU_SERVER_ORIGIN = `http://localhost:${port}`;
	}

	// Suppress the default "Listening on..." log since we show our own message
	process.env.MONGOKU_SUPPRESS_STARTUP_LOG = "true";

	if (!process.env.MONGOKU_SERVER_BODY_SIZE_LIMIT) {
		process.env.MONGOKU_SERVER_BODY_SIZE_LIMIT = "Infinity";
	}

	// Set read-only mode if specified
	if (options.readonly) {
		process.env.MONGOKU_READ_ONLY_MODE = "true";
		console.log(chalk.yellow("🔒 Read-only mode enabled"));
	}

	if (options.pm2) {
		console.log(chalk.green(`🚀 Starting Mongoku with PM2 on port ${port}...`));
		runCommand("pm2", ["--name", "mongoku", "start", "ecosystem.config.js", "--update-env"], {
			stdio: "inherit",
			cwd: rootDir,
		});
	} else {
		console.log(chalk.green(`🚀 Starting Mongoku on port ${port}...`));
		console.log(chalk.cyan(`📊 Open http://localhost:${port} in your browser`));
		runCommand("node", ["build"], { stdio: "inherit", cwd: rootDir });
	}
}

program
	.command("stop")
	.description("Stop the Mongoku server running with PM2")
	.action(() => {
		runCommand("pm2", ["stop", "mongoku"], { stdio: "inherit" });
	});

program
	.command("start")
	.description("Start the Mongoku server")
	.option("--pm2", "Run with PM2")
	.option("-p, --port <port>", "Port to run on", "3100")
	.option("--readonly", "Enable read-only mode (prevents modifications)")
	.allowExcessArguments(false)
	.action(startAction);

program
	.command("help")
	.description("Show help")
	.action(() => {
		program.outputHelp();
	});

// Default action (start the server)
program
	.option("--pm2", "Run with PM2")
	.option("-p, --port <port>", "Port to run on", "3100")
	.option("--readonly", "Enable read-only mode (prevents modifications)")
	.allowExcessArguments(false)
	.action(startAction);

function runCommand(command: string, args: string[], options: SpawnOptions = {}): Promise<void> {
	return new Promise((resolve, reject) => {
		const childProcess = spawn(command, args, {
			...options,
			env: { ...process.env },
		});

		if (!options.detached && options.stdio !== "inherit") {
			childProcess.stdout?.on("data", (data) => {
				console.log(data.toString());
			});

			childProcess.stderr?.on("data", (data) => {
				console.error(data.toString());
			});
		}

		childProcess.on("error", (error) => {
			console.error(chalk.red(`Error: ${error.message}`));
			reject(error);
		});

		childProcess.on("close", (code) => {
			if (code !== 0 && !options.detached) {
				reject(new Error(`Command failed with exit code ${code}`));
			} else {
				resolve();
			}
		});

		if (options.detached) {
			childProcess.unref();
			resolve();
		}
	});
}

program.parse(process.argv);
