//@ts-check
/** @typedef {import('webpack').Configuration} WebpackConfig **/

const { spawnSync } = require('child_process');
var fs = require('fs');
var glob = require('glob');
const path = require('path');
const { CleanWebpackPlugin: CleanPlugin } = require('clean-webpack-plugin');
const esbuild = require('esbuild');
const ForkTsCheckerPlugin = require('fork-ts-checker-webpack-plugin');
const JSON5 = require('json5');
const nodeExternals = require('webpack-node-externals');

module.exports =
	/**
	 * @param {{ esbuild?: boolean } | undefined } env
	 * @param {{ mode: 'production' | 'development' | 'none' | undefined }} argv
	 * @returns { WebpackConfig[] }
	 */
	function (env, argv) {
		const mode = argv.mode || 'development';

		env = {
			esbuild: true,
			...env,
		};

		return [getExtensionConfig('node', mode, env) /*, getExtensionConfig('webworker', mode, env)*/];
	};

/**
 * @param { 'node' | 'webworker' } target
 * @param { 'production' | 'development' | 'none' } mode
 * @param {{ analyzeBundle?: boolean; analyzeDeps?: boolean; esbuild?: boolean; squoosh?: boolean } | undefined } env
 * @returns { WebpackConfig }
 */
function getExtensionConfig(target, mode, env) {
	/**
	 * @type WebpackConfig['plugins'] | any
	 */
	const plugins = [
		new CleanPlugin({ cleanOnceBeforeBuildPatterns: ['out/**'] }),
		new ForkTsCheckerPlugin({
			async: false,
			// eslint: {
			// 	enabled: true,
			// 	files: 'src/**/*.ts',
			// 	options: {
			// 		// cache: true,
			// 		cacheLocation: path.join(
			// 			__dirname,
			// 			target === 'webworker' ? '.eslintcache.browser' : '.eslintcache',
			// 		),
			// 		overrideConfigFile: path.join(
			// 			__dirname,
			// 			target === 'webworker' ? '.eslintrc.browser.json' : '.eslintrc.json',
			// 		),
			// 	},
			// },
			formatter: 'basic',
			typescript: {
				configFile: path.join(
					__dirname,
					target === 'webworker' ? 'tsconfig.test.browser.json' : 'tsconfig.test.json',
				),
			},
		}),
	];

	return {
		name: `tests:${target}`,
		entry: {
			runTest: './src/test/runTest.ts',
			'suite/index': './src/test/suite/index.ts',
			...glob.sync('./src/test/suite/**/*.test.ts').reduce(function (obj, e) {
				obj['suite/' + path.parse(e).name] = e;
				return obj;
			}, {}),
		},
		mode: mode,
		target: target,
		devtool: 'source-map',
		output: {
			path:
				target === 'webworker'
					? path.join(__dirname, 'out', 'test', 'browser')
					: path.join(__dirname, 'out', 'test'),
			filename: '[name].js',
			sourceMapFilename: '[name].js.map',
			libraryTarget: 'commonjs2',
		},
		externals: [{ vscode: 'commonjs vscode' }, nodeExternals()],
		module: {
			rules: [
				{
					exclude: /\.d\.ts$/,
					include: path.join(__dirname, 'src'),
					test: /\.tsx?$/,
					use: env.esbuild
						? {
								loader: 'esbuild-loader',
								options: {
									implementation: esbuild,
									loader: 'ts',
									target: ['es2020', 'chrome91', 'node14.16'],
									tsconfigRaw: resolveTSConfig(
										path.join(
											__dirname,
											target === 'webworker'
												? 'tsconfig.test.browser.json'
												: 'tsconfig.test.json',
										),
									),
								},
						  }
						: {
								loader: 'ts-loader',
								options: {
									configFile: path.join(
										__dirname,
										target === 'webworker' ? 'tsconfig.test.browser.json' : 'tsconfig.test.json',
									),
									experimentalWatchApi: true,
									transpileOnly: true,
								},
						  },
				},
			],
		},
		resolve: {
			alias: { '@env': path.resolve(__dirname, 'src', 'env', target === 'webworker' ? 'browser' : target) },
			fallback: target === 'webworker' ? { path: require.resolve('path-browserify') } : undefined,
			mainFields: target === 'webworker' ? ['browser', 'module', 'main'] : ['module', 'main'],
			extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
		},
		plugins: plugins,
		infrastructureLogging: {
			level: 'log', // enables logging required for problem matchers
		},
		stats: {
			preset: 'errors-warnings',
			assets: true,
			colors: true,
			env: true,
			errorsCount: true,
			warningsCount: true,
			timings: true,
		},
	};
}

/**
 * @param { string } configFile
 * @returns { string }
 */
function resolveTSConfig(configFile) {
	const result = spawnSync('yarn', ['tsc', `-p ${configFile}`, '--showConfig'], {
		cwd: __dirname,
		encoding: 'utf8',
		shell: true,
	});

	const data = result.stdout;
	const start = data.indexOf('{');
	const end = data.lastIndexOf('}') + 1;
	const json = JSON5.parse(data.substring(start, end));
	return json;
}