import { Configuration, EntryObject } from 'webpack';
import path from 'path';
import TerserPlugin from 'terser-webpack-plugin';
import { moduleResolutionPlugins } from './plugins/moduleResolutionPlugins';
import loaders from './loaders';
import { getBuildPlugins } from './plugins/buildProcessPlugins';

export interface Entries {
    content: string[];
    background: string[];
    popup: string[];
    // only used in development
    debug?: string[];
}

export type EntryId = keyof Entries;

/**
 * This function will generate the webpack configuration for the extension
 * @param mode the mode that webpack is running in (development or production)
 * * @param manifest the manifest.json object for the extension
 * @returns the webpack configuration
 */
export default function config(mode: Environment, manifest: chrome.runtime.ManifestV3): Configuration {
    const outDirectory = path.resolve('build');

    // the entry points for the extension (the files that webpack will start bundling from)
    const entry: Entries = {
        content: [path.resolve('src', 'views')],
        popup: [path.resolve('src', 'views')],
        background: [path.resolve('src', 'background', 'background')],
    };

    // the entries that need an html file to be generated
    const htmlEntries: EntryId[] = mode === 'development' ? ['popup', 'debug'] : ['popup'];

    if (mode === 'development') {
        // TODO: add hot reloading script to the debug entry
        entry.debug = [path.resolve('src', 'debug')];

        // we need to import react-devtools before the react code in development
        entry.content = [path.resolve('src', 'debug', 'reactDevtools'), ...entry.content];
        entry.popup = [path.resolve('src', 'debug', 'reactDevtools'), ...entry.popup];
    }

    /** @see https://webpack.js.org/configuration for documentation */
    const config: Configuration = {
        mode,
        devtool: mode === 'development' ? 'cheap-module-source-map' : undefined,
        bail: true,
        cache: true,
        // entry and resolve is what webpack uses for figuring out where to start bundling and how to resolve modules
        entry: entry as unknown as EntryObject,
        resolve: {
            modules: ['node_modules'],
            extensions: ['.js', '.jsx', '.ts', '.tsx'],
            plugins: moduleResolutionPlugins,
            // this is to polyfill some node-only modules
            fallback: {
                crypto: 'crypto-browserify',
                stream: 'stream-browserify',
                buffer: 'buffer',
            },
        },
        // this is where we define the loaders for different file types
        module: {
            strictExportPresence: true,
            rules: loaders,
        },
        output: {
            clean: true,
            path: outDirectory,
            pathinfo: mode === 'development',
            filename: 'static/js/[name].js',
            publicPath: '/',
            // this is for windows support (which uses backslashes in paths)
            devtoolModuleFilenameTemplate: info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
            // this is to make sure that the global chunk loading function name is unique
            chunkLoadingGlobal: `webpackJsonp${manifest.short_name}`,
            globalObject: 'this',
        },
        stats: {
            errorDetails: true,
            errorsCount: true,
        },
        // this is where we define the plugins that webpack will use
        plugins: getBuildPlugins(mode, htmlEntries, manifest),
    };

    if (mode === 'production') {
        config.optimization = {
            minimize: true,
            minimizer: [
                new TerserPlugin({
                    extractComments: false,
                    parallel: false,
                    terserOptions: {
                        compress: {
                            ecma: 2020,
                            drop_console: true,
                            drop_debugger: true,
                            comparisons: false,
                            inline: 2,
                        },
                        keep_classnames: false,
                        keep_fnames: false,
                        output: {
                            ecma: 2020,
                            comments: false,
                            ascii_only: true,
                        },
                    },
                }),
            ],
        };
    }

    return config;
}
