const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const SymlinkWebpackPlugin = require('symlink-webpack-plugin');
const { VueLoaderPlugin } = require('vue-loader');

const ROOT = path.resolve(__dirname, '..');
const SRC = path.resolve(ROOT, 'src');
const PUBLIC = path.resolve(ROOT, 'public');

module.exports = (env, argv) => {
	const mode = argv.mode || 'production';
	const isDev = (mode === 'development');

	return {
		mode,
		devtool: isDev ? 'inline-source-map' : false,
		watch: isDev,
		entry: {
			app: path.resolve(SRC, 'app.js')
		},
		output: {
			path: PUBLIC,
			filename: isDev ? 'assets/[name].js' : 'assets/[name].[contenthash:8].js',
			chunkFilename: isDev ? 'assets/[name].js' : 'assets/[name].[contenthash:8].js',
			publicPath: './',
			clean: isDev ? false : {
				keep: /^(index\.php|files|geo|assets\/images\/icons)(\/.*)?$/
			}
		},
		optimization: {
			splitChunks: {
				chunks: 'all',
				cacheGroups: {
					maplibre: {
						test: /[\\/]node_modules[\\/]maplibre-gl[\\/]/,
						name: 'maplibre',
						chunks: 'all',
						priority: 30,
						enforce: true
					},
					uppy: {
						test: /[\\/]node_modules[\\/](@uppy|@transloadit|namespace-emitter|nanoid)[\\/]/,
						name: 'uppy',
						chunks: 'async',
						priority: 20,
						enforce: true
					}
				}
			}
		},
		performance: {
			hints: isDev ? false : 'warning',
			maxEntrypointSize: 1500 * 1024,
			maxAssetSize: 1100 * 1024
		},
		module: {
			rules: [{
					test: /\.vue$/,
					loader: 'vue-loader'
				}, {
					test: /\.js$/,
					exclude: file => (/node_modules/.test(file) && !/\.vue\.js/.test(file)),
					use: {
						loader: 'babel-loader',
						options: {
							presets: ['@babel/preset-env']
						}
					}
				}, {
					test: /\.s[ac]ss$/i,
					use: [
						'vue-style-loader',
						'css-loader',
						{
							loader: 'sass-loader',
							options: {
								implementation: require('sass'),
								sourceMap: isDev
							}
						}
					]
				}, {
					test: /\.css$/i,
					use: ['vue-style-loader', 'css-loader']
				}, {
					test: /\.(png|svg|jpg|jpeg|gif|webp)$/i,
					type: 'asset',
					parser: {
						dataUrlCondition: {
							maxSize: 1 * 1024
						}
					},
					generator: {
						filename: isDev ? 'assets/images/[name][ext]' : 'assets/images/[name].[contenthash:8][ext]'
					}
				}]
		},
		plugins: [
			new SymlinkWebpackPlugin([
				{ origin: '../files/', symlink: 'files' },
				{ origin: '../resources/geo/', symlink: 'geo' },
				{ origin: '../src/images/icons/', symlink: 'assets/images/icons' }
			]),
			new webpack.DefinePlugin({
				__VUE_OPTIONS_API__: 'true',
				__VUE_PROD_DEVTOOLS__: 'false',
				__VUE_PROD_HYDRATION_MISMATCH_DETAILS__: 'false'
			}),
			{
				apply(compiler) {
					compiler.hooks.done.tap('EntryPointManifestPlugin', (stats) => {
						const manifest = {
							entrypoints: mapChunkGroups(stats.compilation.entrypoints),
							chunkGroups: mapChunkGroups(stats.compilation.chunkGroups)
						};

						const manifestPath = path.resolve(PUBLIC, 'assets', 'entrypoints.json');
						const tmpManifestPath = `${manifestPath}.tmp`;

						fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
						fs.writeFileSync(tmpManifestPath, JSON.stringify(manifest, null, '\t'));
						fs.renameSync(tmpManifestPath, manifestPath);
					});
				}
			},
			new VueLoaderPlugin()
		],
		resolve: {
			extensions: ['.vue', '.scss', '...'],
			alias: {
				'@components': path.resolve(SRC, 'components'),
				'@images': path.resolve(SRC, 'images'),
				'@scripts': path.resolve(SRC, 'scripts'),
				'@styles': path.resolve(SRC, 'styles')
			}
		}
	};
};

function mapChunkGroups(chunkGroups = {}) {
	const chunkGroupEntries = (chunkGroups instanceof Map)?Array.from(chunkGroups.entries()):Array.from(chunkGroups).map((chunkGroup) => [chunkGroup.name, chunkGroup]);
	return Object.fromEntries(
		chunkGroupEntries
			.filter(([name]) => name)
			.map(([name, chunkGroup]) => [
				name,
				Array.from((typeof chunkGroup.getFiles === 'function')?chunkGroup.getFiles():chunkGroup.assets)
					.map((asset) => (typeof asset === 'string')?asset:asset.name)
					.filter((file) => file.endsWith('.js'))
			])
	);
}
