const { spawn } = require('child_process');
const fs = require('fs');
const compressing = require('compressing');
const { v4, parse } = require('uuid');
const { Base64 } = require('js-base64');
const { copyDir, removeDir, fix2 } = require('./utils');

async function buildForm() {
	const paths = fs.readdirSync('form');

	if (!paths.includes('tsconfig.json')) {
		await copyDir('form', 'dist/form');
		return;
	}

	if (process.platform == 'win32') {
		await execCmd('npm.cmd', ['install'], 'form');
	} else {
		await execCmd('npm', ['install'], 'form');
	}

	if (process.platform == 'win32') {
		await execCmd('npm.cmd', ['run', 'publish'], 'form');
	} else {
		await execCmd('npm', ['run', 'publish'], 'form');
	}

	await compressing.zip.uncompress('form/publish/form.zip', 'dist/form');
}

function execCmd(cmd, params, path) {
	return new Promise((resolve, reject) => {
		const buffer = spawn(cmd, params, { cwd: path });

		buffer.stdout.on('data', (data) => {
			console.log(`stdout: ${data}`);
		});

		buffer.stderr.on('data', (data) => {
			console.error(`stderr: ${data}`);
		});

		buffer.on('close', (code) => {
			console.log(`child process exited with code ${code}`);
			resolve();
		});
	});
}

// let creatorBasePath = 'D:/install/CocosDashboard_1.0.6/resources/.editors/Creator/2.4.5/CocosCreator.exe // 公司
let creatorBasePath = 'D:/Install/cocos/CocosDashboard/resources/.editors/Creator/2.4.5/CocosCreator.exe'; // 家用
if (process.platform !== 'win32') {
	creatorBasePath = '/Applications/CocosCreator/Creator/2.4.5/CocosCreator.app/Contents/MacOS/CocosCreator';
}
const buildCocos = function (args) {
	return new Promise((resolve, reject) => {
		const buffer = spawn(creatorBasePath, args, { cwd: '.' });

		buffer.stdout.on('data', (data) => {
			console.log(`stdout: ${data}`);
		});

		buffer.stderr.on('data', (data) => {
			console.error(`stderr: ${data}`);
		});

		buffer.on('close', (code) => {
			console.log(`child process exited with code ${code}`);
			resolve();
		});
	});
};

function getReleaseFileName(projectName) {
	let date = new Date();
	let fileName = `${projectName}_${date.getFullYear()}${fix2(date.getMonth() + 1)}${fix2(date.getDate())} `;
	fileName += `${fix2(date.getHours())}-${fix2(date.getMinutes())}-${fix2(date.getSeconds())}`;
	return fileName;
}

function getFolderName(path) {
	let folderName = '';
	fs.readdirSync(path).find((fileName) => {
		const st = fs.statSync(`${path}/${fileName}`);
		if (st.isDirectory()) {
			folderName = fileName;
		}
	});
	return folderName;
}
function editFolderMeta(path, folderName, isBundle) {
	const metaPath = `${path}/${folderName}.meta`;
	const metaDataStr = fs.readFileSync(metaPath);
	const metaData = JSON.parse(metaDataStr);
	metaData.isBundle = isBundle;
	metaData.isRemoteBundle = {
		ios: isBundle,
		android: isBundle,
	};
	fs.writeFileSync(metaPath, JSON.stringify(metaData));
}

async function buildWebDesktop() {
	const args = ['--path', './', '--build', 'platform=web-desktop;debug=true', '--force'];
	await buildCocos(args);
}

async function buildAndroidBundle() {
	const args = ['--path', './', '--build', "platform=ios;debug=false;md5Cache=true;buildPath=build_android;appABIs=['armeabi-v7a','x86','x86_64','arm64-v8a'];encryptJs=true;xxteaKey=6bbfce23-28b4-4a;zipCompressJs=true", '--force'];
	await buildCocos(args);
}

async function buildIosBundle() {
	const args = ['--path', './', '--build', 'platform=ios;debug=false;md5Cache=true;buildPath=build_ios;encryptJs=true;xxteaKey=6bbfce23-28b4-4a;zipCompressJs=true', '--force'];
	await buildCocos(args);
}

async function buildWebBundle() {
	const args = ['--path', './', '--build', 'platform=web-desktop;debug=false;buildPath=build_web_desktop', '--force'];
	await buildCocos(args);
}

function createConfigFile(projectName, type) {
	let iosVersion = '';
	let androidVersion = '';
	if (!type) {
		const androidPaths = fs.readdirSync(`dist/android/${projectName}`);
		const androidConfigFileName = androidPaths.find((path) => path.indexOf('config') == 0);
		androidVersion = androidConfigFileName.split('.')[1];
		const iosPaths = fs.readdirSync(`dist/ios/${projectName}`);
		const iosConfigFileName = iosPaths.find((path) => path.indexOf('config') == 0);
		iosVersion = iosConfigFileName.split('.')[1];
	} else {
		if (type == 'android') {
			const androidPaths = fs.readdirSync(`dist/android/${projectName}`);
			const androidConfigFileName = androidPaths.find((path) => path.indexOf('config') == 0);
			androidVersion = androidConfigFileName.split('.')[1];
		} else {
			const iosPaths = fs.readdirSync(`dist/ios/${projectName}`);
			const iosConfigFileName = iosPaths.find((path) => path.indexOf('config') == 0);
			iosVersion = iosConfigFileName.split('.')[1];
		}
	}

	const config = {
		ios: {
			sceneName: projectName,
			version: iosVersion,
		},
		android: {
			sceneName: projectName,
			version: androidVersion,
		},
	};

	fs.writeFileSync('dist/config.json', JSON.stringify(config));
}

function compressAll(projectName) {
	const tarStream = new compressing.zip.Stream();
	tarStream.addEntry('dist/play');
	tarStream.addEntry('dist/form');
	tarStream.addEntry('dist/ios');
	tarStream.addEntry('dist/android');
	tarStream.addEntry('dist/web_desktop');
	tarStream.addEntry('dist/config.json');
	const destStream = fs.createWriteStream(`publish/${getReleaseFileName(projectName)}.zip`);
	tarStream.pipe(destStream);
}

function build_check() {
	const dirNames = process.cwd().split(/\/|\\/);
	const projectName = dirNames[dirNames.length - 1];
	const path = 'assets';
	let folderName = '';
	fs.readdirSync(path).find((fileName) => {
		const st = fs.statSync(`${path}/${fileName}`);
		if (st.isDirectory()) {
			folderName = fileName;
		}
	});

	if (projectName != folderName) {
		throw `项目名(${projectName})与bundle文件夹名(${folderName})不相同`;
	}
	let same = false;
	const files = fs.readdirSync(`${path}/${folderName}/scene`);
	files.forEach((fileName) => {
		fileName.split('.').forEach((str, idx, arr) => {
			if (str == 'fire') {
				const sceneName = arr[idx - 1];
				if (folderName == sceneName) {
					same = true;
				}
			}
		});
	});

	if (!same) {
		throw `bundle文件夹名称(${folderName})与scene名称不相同`;
	}

	return projectName;
}

function changeSettingToWebDesktop() {
	const path = 'assets';
	const folderName = getFolderName(path);
	editFolderMeta(path, folderName, false);
}

function changeSettingsToBundle() {
	const path = 'assets';
	const folderName = getFolderName(path);
	editFolderMeta(path, folderName, true);
}

function replaceUuids() {
	console.log('build_step_0 开始~!');

	const path = 'assets';
	function getFolderName(path) {
		let folderName = '';
		fs.readdirSync(path).find((fileName) => {
			const st = fs.statSync(`${path}/${fileName}`);
			if (st.isDirectory()) {
				folderName = fileName;
			}
		});
		return folderName;
	}
	const folderName = getFolderName(path);

	let oldFireUuid = '';
	let oldJsUuid = '';
	let oldJsShortUuid = '';
	let oldJsId = '';
	const fireMetaStr = fs.readFileSync(`assets/${folderName}/scene/${folderName}.fire.meta`);
	if (fireMetaStr.indexOf('57ea7c61-9b8b-498a-b024-c98ee9124beb') > 0) {
		// 老Cocos脚手架
		oldFireUuid = '57ea7c61-9b8b-498a-b024-c98ee9124beb';
		oldJsUuid = 'f4ede462-f8d7-4069-ba80-915611c058ca';
		oldJsShortUuid = 'f4edeRi+NdAabqAkVYRwFjK';
		oldJsId = 'e687yyoRBIzZAOVRL8Sseh';
	}
	if (fireMetaStr.indexOf('0737ce42-24f0-45c6-8e1a-8bdab4f74ba3') > 0) {
		// 新Cocos脚手架
		oldFireUuid = '0737ce42-24f0-45c6-8e1a-8bdab4f74ba3';
		oldJsUuid = '408a67f8-65fa-4cf1-8cf2-83e20e1a0fd5';
		oldJsShortUuid = '408a6f4ZfpM8Yzyg+IOGg/V';
		oldJsId = 'eaTVUpqahPfZeO9+sUI7RP';
	}
	if (oldFireUuid === '') {
		return;
	}

	function editFolderMeta(path, folderName) {
		const metaPath = `${path}/${folderName}.meta`;
		const metaDataStr = fs.readFileSync(metaPath);
		const metaData = JSON.parse(metaDataStr);
		metaData.isBundle = false;
		fs.writeFileSync(metaPath, JSON.stringify(metaData));
	}
	function fileReplace(path, replaceStr, newStr) {
		if (!fs.existsSync(path)) {
			return;
		}

		const fileStr = fs.readFileSync(path);
		const newFileStr = fileStr.toString().replace(new RegExp(replaceStr.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'), newStr);
		fs.writeFileSync(path, newFileStr);
	}
	function getShortUuid(uuid) {
		const bytes = parse(uuid).subarray(1);
		return uuid.substring(0, 5) + Base64.fromUint8Array(bytes).substring(2);
	}

	editFolderMeta(path, folderName);

	const newFireUuid = v4();
	fileReplace(`assets/${folderName}/scene/${folderName}.fire.meta`, oldFireUuid, newFireUuid);
	fileReplace(`assets/${folderName}/scene/${folderName}.fire`, oldFireUuid, newFireUuid);
	fileReplace('settings/builder.json', oldFireUuid, newFireUuid);

	const newJsUuid = v4();
	const newJsShortUuid = getShortUuid(newJsUuid);
	const newJsId = v4().replace(/-/g, '').substring(0, oldJsId.length);
	fileReplace(`assets/${folderName}/scene/${folderName}.ts.meta`, oldJsUuid, newJsUuid);
	fileReplace(`assets/${folderName}/scene/${folderName}.js.meta`, oldJsUuid, newJsUuid);
	fileReplace(`assets/${folderName}/scene/${folderName}.fire`, oldFireUuid, newFireUuid);
	fileReplace(`assets/${folderName}/scene/${folderName}.fire`, oldJsShortUuid, newJsShortUuid);
	fileReplace(`assets/${folderName}/scene/${folderName}.fire`, oldJsId, newJsId);

	console.log('build_step_0 完成~!');
}

function replaceIndexHtml() {
	const data = fs.readFileSync('index.html');
	fs.writeFileSync('dist/play/index.html', data);
}

module.exports = {
	build: async function () {
		const startTime = new Date().getTime();

		// 构建前检查
		const projectName = build_check();

		// 清理旧文件

		// 构建form
		await removeDir('dist/form');
		await buildForm();

		// 替换uuid
		replaceUuids();

		// 改设置为非bundle
		changeSettingToWebDesktop();

		// 构建play
		await removeDir('dist/play');
		await buildWebDesktop();
		await copyDir('build/web-desktop', 'dist/play');
		replaceIndexHtml();
		console.log('构建 web desktop 成功!');

		// 改设置为bundle
		changeSettingsToBundle();

		await removeDir('dist/android');
		await buildAndroidBundle();
		await copyDir('build_android/jsb-link/remote', 'dist/android');
		console.log('构建 android bundle 成功!');

		await removeDir('dist/ios');
		await buildIosBundle();
		await copyDir('build_ios/jsb-link/remote', 'dist/ios');
		console.log('构建 ios bundle 成功!');

		await removeDir('dist/web_desktop');
		await buildWebBundle();
		await copyDir(`build_web_desktop/web-desktop/assets/${projectName}`, 'dist/web_desktop');
		console.log('构建 web bundle 成功!');

		// 改设置为非bundle
		changeSettingToWebDesktop();

		createConfigFile(projectName);

		compressAll(projectName);

		await removeDir('build');
		await removeDir('build_android');
		await removeDir('build_ios');
		await removeDir('build_web_desktop');

		const endTime = new Date().getTime();
		const duration = new Date(endTime - startTime);
		console.log(`打包完成!`);
		console.log(`用时${duration.getMinutes()}分${duration.getSeconds()}秒。`);
	},

	buildAndroid: async function () {
		// 构建前检查
		const projectName = build_check();
		// 替换uuid
		replaceUuids();
		// 改设置为bundle
		changeSettingsToBundle();

		await removeDir('dist/android');
		await buildAndroidBundle();
		await copyDir('build_android/jsb-link/remote', 'dist/android');

		// 改设置为非bundle
		changeSettingToWebDesktop();
		createConfigFile(projectName, 'android');
		await removeDir('build_android');
		console.log('构建 android bundle 成功!');
	},

	buildIos: async function () {
		// 构建前检查
		const projectName = build_check();
		// 替换uuid
		replaceUuids();
		// 改设置为bundle
		changeSettingsToBundle();

		await removeDir('dist/ios');
		await buildIosBundle();
		await copyDir('build_ios/jsb-link/remote', 'dist/ios');

		// 改设置为非bundle
		changeSettingToWebDesktop();
		createConfigFile(projectName, 'ios');
		await removeDir('build_ios');
		console.log('构建 ios bundle 成功!');
	},
};