import type { ExecException } from 'node:child_process';
import { cp, mkdir, stat, readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import { styleText } from 'node:util';
import type { PackageManager } from './helpers/packageManager.js';
import { install } from './helpers/packageManager.js';
import { GUIDE_URL } from './util/constants.js';
import { isFolderEmpty } from './util/isFolderEmpty.js';

interface Options {
	directory: string;
	installPackages: boolean;
	packageManager: PackageManager;
	typescript?: boolean;
}

export async function createDiscordBot({ directory, installPackages, typescript, packageManager }: Options) {
	const root = path.resolve(directory);
	const directoryName = path.basename(root);

	console.log();

	const directoryStats = await stat(root).catch(async (error) => {
		// Create a new directory if the specified one does not exist.
		if (error.code === 'ENOENT') {
			await mkdir(root, { recursive: true });
			return stat(root);
		}

		throw error;
	});

	// If the directory is actually a file or if it's not empty, throw an error.
	if (!directoryStats.isDirectory() || !isFolderEmpty(root, directoryName)) {
		console.error(
			styleText(
				'red',
				`The directory ${styleText('yellow', `"${directoryName}"`)} is either not a directory or is not empty.`,
			),
		);
		console.error(styleText('red', `Please specify an empty directory.`));
		process.exit(1);
	}

	console.log(`Creating ${directoryName} in ${styleText('green', root)}.`);

	const deno = packageManager === 'deno';
	const bun = packageManager === 'bun';

	const lang = typescript ? 'TypeScript' : 'JavaScript';
	const templateBasePath = deno ? 'Deno' : bun ? `Bun/${lang}` : lang;

	await cp(new URL(`../template/${templateBasePath}`, import.meta.url), root, {
		recursive: true,
	});

	process.chdir(root);

	const newVSCodeSettings = await readFile('./.vscode/settings.json', { encoding: 'utf8' });
	await writeFile(
		'./.vscode/settings.json',
		newVSCodeSettings.replace(
			/"npm\.packageManager":\s*"[^"]+"/,
			`"npm.packageManager": "${deno || bun ? 'auto' : packageManager}"`,
		),
	);

	if (!deno) {
		const newPackageJSON = await readFile('./package.json', { encoding: 'utf8' });
		await writeFile('./package.json', newPackageJSON.replace(/"name":\s*"[^"]+"/, `"name": "${directoryName}"`));
	}

	if (installPackages) {
		try {
			install(packageManager);
		} catch (error) {
			console.log();
			const err = error as ExecException;
			if (err.signal === 'SIGINT') {
				console.log(styleText('red', 'Installation aborted.'));
			} else {
				console.error(styleText('red', 'Installation failed.'));
				process.exit(1);
			}
		}
	}

	console.log();
	console.log(styleText('green', 'All done! Be sure to read through the discord.js guide for help on your journey.'));
	console.log(`Link: ${styleText('cyan', GUIDE_URL)}`);
}
