#!/usr/bin/env node

import fs from 'fs-extra';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import * as p from '@clack/prompts';
import { bold, cyan, grey, red, blue } from 'kleur/colors';
import minimist from 'minimist';

import * as langAll from './lang';

// 获取版本号
// Get version
// @ts-ignore
const { version } = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));

// 显示版本号
// Display version number
console.log(`
${grey(`create-stdf@${version}
`)}`);

const spinner = p.spinner();

p.intro('Welcome to use STDF!');

let lang = langAll.en_US;

// 获取命令行参数
// Get command line parameters
const argv = minimist(process.argv.slice(2));

// 获取项目名称和模板名称和语言
// Get project name and template name
const argvProjectName = argv._[0];
const argvTemplate = argv.template || argv.t;
const argvLanguage = argv.language || argv.l;

// 语言列表
// Language list
const languages = [];

// 循环 langAll 对象，将语言列表中的语言名字替换为对应的语言名字，且按照 sort 排序
// Loop through the langAll object and replace the language name in the language list with the corresponding language name, and sort by sort
for (const key in langAll) {
	languages.push({
		value: key,
		label: langAll[key].name,
		sort: langAll[key].sort,
	});
}

// 按照 sort 排序
// Sort by sort
languages.sort((a, b) => a.sort - b.sort);

// 如果命令行参数中有语言，且语言列表中有该语言，使用该语言，否则使用英语
// If there is a language in the command line parameters and the language list has the language, use the language, otherwise use English
lang = argvLanguage && languages.find(item => item.value === argvLanguage) ? langAll[argvLanguage] : langAll.en_US;

// 模板列表
// Template list
const templateOptions = [
	{ value: 'skt', label: 'SvelteKit + Tailwind', template: 'sveltekit-tailwind', finish: true },
	{ value: 'vt', label: 'Vite + Tailwind', template: 'vite-tailwind', finish: true },
	{ value: 'vu', label: 'Vite + UnoCSS', template: 'vite-uno', finish: true },
	{ value: 'sku', label: 'SvelteKit + UnoCSS', template: 'sveltekit-uno', finish: true },
	{ value: 'vtt', label: 'Vite + Tailwind + TypeScript', template: 'vite-tailwind-typescript', finish: false },
	{ value: 'vut', label: 'Vite + UnoCSS + TypeScript', template: 'vite-uno-typescript', finish: false },
	{ value: 'sktt', label: 'SvelteKit + Tailwind + TypeScript', template: 'sveltekit-tailwind-typescript', finish: false },
	{ value: 'skut', label: 'SvelteKit + UnoCSS + TypeScript', template: 'sveltekit-uno-typescript', finish: false },
];

// 如果命令行参数中有项目名称，但没有模板名称，直接使用默认模板 skt
// If there is a project name in the command line parameters, but no template name, use the default template skt directly
if (argvProjectName && !argvTemplate) {
	createFunc(argvProjectName, templateOptions[0]);
}
// 如果命令行参数中有项目名称和模板名称，直接使用命令行参数中的值
// If there is a project name and template name in the command line parameters, use the value in the command line parameters directly
else if (argvProjectName && argvTemplate) {
	const item = templateOptions.find(item => item.value === argvTemplate);
	if (!item) {
		p.intro(red(lang.pectn));
	} else if (!item.finish) {
		p.intro(red(item.label + ' ' + lang.hnay));
	} else {
		createFunc(argvProjectName, item);
	}
} else {
	(async () => {
		// 选择一种语言
		// Select a language
		const languageType = await p.select({
			message: bold('Please select your preferred language'),
			options: languages,
		});
		if (p.isCancel(languageType)) {
			p.cancel(red('⛔ ') + lang.oc);
			process.exit(0);
		}
		lang = langAll[languageType];

		//  选择一个模板
		// Select a template
		let template = await p.select({
			message: bold(lang.psat),
			options: templateOptions,
		});

		if (p.isCancel(template)) {
			p.cancel(red('⛔ ') + lang.oc);
			process.exit(0);
		}

		// 直到选择的 template 所在项 的 finish 为 true 为止，否则一直重新选择
		// Until the finish of the selected template is true, otherwise keep reselecting
		while (!templateOptions.find(item => item.value === template)?.finish) {
			if (p.isCancel(template)) {
				p.cancel(red('⛔ ') + lang.oc);
				process.exit(0);
			}

			p.intro(red(templateOptions.find(item => item.value === template).label + ' ' + lang.hnay + ' ' + lang.pca));
			template = await p.select({
				message: bold(lang.psat),
				options: templateOptions,
			});
		}

		// 是否启用类型检查
		// Whether to enable type checking
		const isTypeCheck = await p.confirm({
			message: bold(lang.wtetcfjsf),
		});

		if (p.isCancel(isTypeCheck)) {
			p.cancel(red('⛔ ') + lang.oc);
			process.exit(0);
		}

		// 输入项目名称
		// Enter the project name
		const projectName = await p.text({
			message: bold(lang.pn),
			placeholder: 'stdf-project',
			validate: value => {
				if (!value) {
					// 判断是否为空，提示 “项目名称不能为空”
					// Determine whether it is empty, prompt "Project name cannot be empty"
					return lang.pncbne;
				}
				if (fs.existsSync(value)) {
					// 判断是否已存在，提示 “项目名称已存在”
					// Determine whether it already exists, prompt "Project name already exists"
					return lang.pane;
				}
			},
		});

		if (p.isCancel(projectName)) {
			p.cancel(red('⛔ ') + lang.oc);
			process.exit(0);
		}

		// 根据 template 的值，复制对应目录下的所有文件到当前目录
		// According to the value of template, copy all files under the corresponding directory to the current directory
		templateOptions.forEach(async item => {
			if (item.value === template) {
				createFunc(projectName, item, isTypeCheck);
			}
		});
	})();
}

function createFunc(projectName, item, isTypeCheck = true) {
	// 如果 projectName 是数字，转为字符串
	// If projectName is a number, convert it to a string
	if (typeof projectName === 'number') {
		projectName = projectName.toString();
	}

	// 项目目录
	// Project directory
	const projectDir = path.join(path.resolve(), projectName);

	spinner.start('🚀 ' + lang.cfsing);

	fs.mkdirSync(projectDir);

	// 获取模板目录的绝对路径，考虑到 Windows 系统的兼容性, 使用 path.join
	// Get the absolute path of the template directory, considering the compatibility of the Windows system, use path.join
	// @ts-ignore
	const templatePath = path.resolve(fileURLToPath(import.meta.url), '../..', `templates/${item.template}`);

	// 将 templatePath 目录下的所有文件复制到 projectDir 目录下
	// Copy all files under the templatePath directory to the projectDir directory\
	fs.copy(templatePath, projectDir)
		.then(() => {
			spinner.stop();
			p.outro(`${projectName} - ${lang.pcsucc} 🎉`);

			// 如果 isTypeCheck 为 true，将 common 目录下的 jsconfig.json 文件复制到项目目录下
			// If isTypeCheck is true, copy the jsconfig.json file under the common directory to the project directory
			if (isTypeCheck) {
				// @ts-ignore
				const commonPath = path.resolve(fileURLToPath(import.meta.url), '../..', 'common');
				fs.copySync(path.join(commonPath, '/jsconfig.json'), path.join(projectDir, 'jsconfig.json'));
			}

			// 读取 package.json 文件
			// Read the package.json file
			const packageJson = JSON.parse(fs.readFileSync(`${projectDir}/package.json`, 'utf-8'));

			// 将项目内的 package.json 中的 name 属性修改为 projectName
			// Modify the name attribute in package.json in the project to projectName
			packageJson.name = projectName;

			// 将修改后的 packageJson 写入到项目内的 package.json 文件中
			// Write the modified packageJson to the package.json file in the project
			fs.writeFileSync(`${projectDir}/package.json`, JSON.stringify(packageJson, null, 4), 'utf-8');

			// 根据 item.value 的值，判断是否使用了 @sveltejs/kit
			// According to the value of item.value, determine whether @sveltejs/kit is used
			const isHasKit = item.value === 'skt' || item.value === 'sku' || item.value === 'sktt' || item.value === 'skut';

			// 根据 item.value 的值，判断使用 Tailwind 还是 UnoCSS
			// According to the value of item.value, determine whether to use Tailwind or UnoCSS
			const isHasUno = item.value === 'vu' || item.value === 'sku' || item.value === 'vut' || item.value === 'skut';

			// 获得依赖的版本号
			// get the version number of the dependency
			const versions = {
				vite: packageJson.devDependencies.vite.replace('^', ''),
				svelte: packageJson.devDependencies.svelte.replace('^', ''),
				stdf: packageJson.devDependencies.stdf.replace('^', ''),
			};

			if (isHasKit) {
				versions['@sveltejs/kit'] = packageJson.devDependencies['@sveltejs/kit'].replace('^', '');
			}

			if (isHasUno) {
				versions['unocss'] = packageJson.devDependencies.unocss.replace('^', '');
			} else {
				versions['tailwindcss'] = packageJson.devDependencies.tailwindcss.replace('^', '');
			}

			// 将 versions 的键值拼接为 bold('Vite:') cyan(versions.vite) bold('Svelte:') cyan(versions.svelte) 的形式
			// Splice the key value of versions into the form of bold('Vite:') cyan(versions.vite) bold('Svelte:') cyan(versions.svelte)
			let versionsString = '';
			for (const key in versions) {
				versionsString += bold(key) + ': ' + cyan(versions[key]) + ' ';
			}

			// 显示版本号
			// Display version number
			console.log(`📦 ${versionsString}
                `);

			// 显示提示信息
			// Display prompt information
			console.log(
				`👉 ${bold(lang.tgs)}

    ${blue(`cd ${projectName}`)}
    ${blue('pnpm i / npm i / yarn / bun i')}
    ${blue('npm dev / bun dev')}
    `,
			);
			// 显示配置主题色
			// Display configuration theme color
			console.log(
				`🎨 ${grey(isHasUno ? lang.pcyt_vu : lang.pcyt_vt)}
    `,
			);
		})
		.catch(err => {
			spinner.stop();
			console.error(red(lang.cferror + '--' + err));
		});
}
