/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the Source EULA. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';
const gulp = require('gulp');
const tsfmt = require('typescript-formatter');
const es = require('event-stream');
const filter = require('gulp-filter');
const path = require('path');
const ext = require('./lib/extensions');
const loc = require('./lib/locFunc');
const task = require('./lib/task');
const glob = require('glob');
const vsce = require('@vscode/vsce');
const mkdirp = require('mkdirp');
const rename = require('gulp-rename');
const fs = require('fs');

gulp.task('fmt', () => formatStagedFiles());
const formatFiles = (some) => {
	const formatting = es.map(function (file, cb) {

		tsfmt.processString(file.path, file.contents.toString('utf8'), {
			replace: true,
			tsfmt: true,
			tslint: true,
			tsconfig: true
			// verbose: true
		}).then(result => {
			console.info('ran formatting on file ' + file.path + ' result: ' + result.message);
			if (result.error) {
				console.error(result.message);
			}
			cb(null, file);

		}, err => {
			cb(err);
		});
	});
	return gulp.src(some, {
		base: '.'
	})
		.pipe(filter(f => !f.stat.isDirectory()))
		.pipe(formatting);

};

const formatStagedFiles = () => {
	const cp = require('child_process');
	cp.exec('git diff --name-only', {
		maxBuffer: 2000 * 1024
	}, (err, out) => {
		if (err) {
			console.error();
			console.error(err);
			process.exit(1);
		}

		const some = out
			.split(/\r?\n/)
			.filter(l => !!l)
			.filter(l => l.match(/.*.ts$/i));

		formatFiles(some).on('error', err => {
			console.error();
			console.error(err);
			process.exit(1);
		});
	});

	cp.exec('git diff --cached --name-only', {
		maxBuffer: 2000 * 1024
	}, (err, out) => {
		if (err) {
			console.error();
			console.error(err);
			process.exit(1);
		}

		const some = out
			.split(/\r?\n/)
			.filter(l => !!l)
			.filter(l => l.match(/.*.ts$/i));

		formatFiles(some).on('error', err => {
			console.error();
			console.error(err);
			process.exit(1);
		});
	});
};

const root = path.dirname(__dirname);

gulp.task('package-external-extensions', task.series(
	task.define('bundle-external-extensions-build', () => ext.packageExternalExtensionsStream().pipe(gulp.dest('.build/external'))),
	task.define('create-external-extension-vsix-build', async () => {
		const vsixes = glob.sync('.build/external/extensions/*/package.json').map(manifestPath => {
			const extensionPath = path.dirname(path.join(root, manifestPath));
			const extensionName = path.basename(extensionPath);
			return { name: extensionName, path: extensionPath };
		})
			.filter(element => ext.vscodeExternalExtensions.indexOf(element.name) === -1) // VS Code external extensions are bundled into ADS so no need to create a normal VSIX for them
			.map(async element => {
				try {
					const pkgJson = require(path.join(element.path, 'package.json'));
					const vsixDirectory = path.join(root, '.build', 'extensions');
					mkdirp.sync(vsixDirectory);
					const packagePath = path.join(vsixDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`);
					console.info(`Creating vsix for ${element.path}, result: ${packagePath}`);
					return await vsce.createVSIX({
						cwd: element.path,
						packagePath: packagePath,
						useYarn: false
					});
				} catch (e) {
					console.error(`Failed to create vsix for ${element.path}, error occurred: ${e}`);
					throw e;
				}
			});
		// Wait for all the initial VSIXes to be completed before making the VS Code ones since we'll be overwriting
		// values in the package.json for those.
		await Promise.all(vsixes);

		// Go through and find the extensions which build separate versions of themselves for VS Code.
		// This is currently a pretty simplistic process, essentially just replacing certain values in
		// the package.json. It doesn't handle more complex tasks such as replacing localized strings.
		const vscodeVsixes = glob.sync('.build/external/extensions/*/package.vscode.json')
			.map(async vscodeManifestRelativePath => {
				try {
					const vscodeManifestFullPath = path.join(root, vscodeManifestRelativePath);
					const packageDir = path.dirname(vscodeManifestFullPath);
					const packageManifestPath = path.join(packageDir, 'package.json');
					const json = require('gulp-json-editor');
					const packageJsonStream = gulp.src(packageManifestPath) // Create stream for the original package.json
						.pipe(json(data => {
							// And now use gulp-json-editor to modify the contents
							const updateData = JSON.parse(fs.readFileSync(vscodeManifestFullPath)); // Read in the set of values to replace from package.vscode.json
							Object.keys(updateData).forEach(key => {
								if (key !== 'contributes') {
									data[key] = updateData[key];
								}
							});
							if (data.contributes?.menus) {
								// Remove ADS-only menus. This is a subset of the menus listed in https://github.com/microsoft/azuredatastudio/blob/main/src/vs/workbench/api/common/menusExtensionPoint.ts
								// More can be added to the list as needed.
								['objectExplorer/item/context', 'dataExplorer/context', 'dashboard/toolbar'].forEach(menu => {
									delete data.contributes.menus[menu];
								});
							}

							// Add any configuration properties from the package.vscode.json
							// Currently only supports bringing over properties in the first config object found and doesn't support modifying the title
							if (updateData.contributes?.configuration[0]?.properties) {
								Object.keys(updateData.contributes.configuration[0].properties).forEach(key => {
									data.contributes.configuration[0].properties[key] = updateData.contributes.configuration[0].properties[key];
								});
							}

							return data;
						}, { beautify: false }))
						.pipe(gulp.dest(packageDir));
					await new Promise(resolve => packageJsonStream.on('finish', resolve)); // Wait for the files to finish being updated before packaging
					const pkgJson = JSON.parse(fs.readFileSync(packageManifestPath));
					const vsixDirectory = path.join(root, '.build', 'extensions');
					const packagePath = path.join(vsixDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`);
					console.info(`Creating vsix for ${packageDir} result: ${packagePath}`);
					return await vsce.createVSIX({
						cwd: packageDir,
						packagePath: packagePath,
						useYarn: false
					});
				} catch (e) {
					console.error(`Failed to create vsix for ${packageDir}, error occurred: ${e}`);
					throw e;
				}
			});
		return Promise.all(vscodeVsixes);
	})
));

gulp.task('package-langpacks', task.series(
	task.define('bundle-external-langpack-build', () => loc.packageLangpacksStream().pipe(gulp.dest('.build/external'))),
	task.define('create-external-langpack-vsix-build', () => {
		const vsixes = glob.sync('.build/external/langpacks/*/package.json').map(manifestPath => {
			const extensionPath = path.dirname(path.join(root, manifestPath));
			const extensionName = path.basename(extensionPath);
			return { name: extensionName, path: extensionPath };
		}).map(async element => {
			try {
				const pkgJson = require(path.join(element.path, 'package.json'));
				const vsixDirectory = path.join(root, '.build', 'langpacks');
				mkdirp.sync(vsixDirectory);
				const packagePath = path.join(vsixDirectory, `${pkgJson.name}-${pkgJson.version}.vsix`);
				console.info('Creating vsix for ' + element.path + ' result:' + packagePath);
				return await vsce.createVSIX({
					cwd: element.path,
					packagePath: packagePath,
					useYarn: false
				});
			} catch (e) {
				console.error(`Failed to create vsix for ${element.path}, error occurred: ${e}`);
				throw e;
			}
		});

		return Promise.all(vsixes);
	})
));


gulp.task('package-rebuild-extensions', task.series(
	task.define('clean-rebuild-extensions', () => ext.cleanRebuildExtensions('.build/extensions')),
	task.define('rebuild-extensions-build', () => ext.packageRebuildExtensionsStream().pipe(gulp.dest('.build'))),
));

gulp.task('update-langpacks', task.series(
	task.define('rename-vscode-packs', () => loc.renameVscodeLangpacks()),
	task.define('refresh-langpack-resources', () => loc.refreshLangpacks())
));

