import path from 'path';
import dotenv from 'dotenv';
import webpack, { WebpackPluginInstance } from 'webpack';
import { EntryId } from 'webpack/webpack.config';
import CreateFileWebpack from 'create-file-webpack';
import CopyWebpackPlugin from 'copy-webpack-plugin';
import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin';
import WebpackBuildNotifierPlugin from 'webpack-build-notifier';
import CaseSensitivePathsPlugin from 'case-sensitive-paths-webpack-plugin';
import HTMLWebpackPlugin from 'html-webpack-plugin';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import { CleanWebpackPlugin } from 'clean-webpack-plugin';
import TypeErrorNotifierPlugin from './custom/TypeErrorNotifierPlugin';

/**
 *  Gets the plugins that are used in the build process
 * @param mode the environment that the build is running in
 * @param htmlEntries the entry points that need an html file
 * @param manifest the manifest.json file
 * @returns an array of webpack plugins
 */
export function getBuildPlugins(mode: Environment, htmlEntries: EntryId[], manifest: chrome.runtime.ManifestV3) {
    let plugins: WebpackPluginInstance[] = [];

    // show the progress of the build
    plugins.push(new webpack.ProgressPlugin());

    // make sure that the paths are case sensitive
    plugins.push(new CaseSensitivePathsPlugin());

    plugins.push(new CleanWebpackPlugin());

    // specify how the outputed css files should be named
    plugins.push(
        new MiniCssExtractPlugin({
            filename: 'static/css/[name].css',
            chunkFilename: 'static/css/[name].chunk.css',
        })
    );

    // create an html file for each entry point that needs one
    for (const entryId of htmlEntries) {
        // if (!entries[entryId]) return;
        plugins.push(
            new HTMLWebpackPlugin({
                hash: false,
                filename: `${entryId}.html`,
                chunks: [entryId],
                title: `${manifest.short_name} ${entryId} `,
                template: path.resolve('webpack', 'plugins', 'template.html'),
            })
        );
    }

    // write the manifest.json file to the build directory
    plugins.push(
        new CreateFileWebpack({
            path: path.resolve('build'),
            fileName: 'manifest.json',
            content: JSON.stringify(manifest, null, 2),
        })
    );

    // copy the public directory to the build directory, but only copy the icons for the current mode
    plugins.push(
        new CopyWebpackPlugin({
            patterns: [
                {
                    from: path.resolve('public'),
                    filter: path => (path.includes('icons/icon') ? path.includes(mode) : true),
                },
            ],
        })
    );

    // run the typescript checker in a separate process
    plugins.push(
        new ForkTsCheckerWebpackPlugin({
            async: false,
        })
    );

    // notify the developer of build events when in development mode
    if (mode === 'development') {
        plugins.push(
            new WebpackBuildNotifierPlugin({
                title: `${manifest.short_name} v${manifest.version} ${mode}`,
                logo: path.resolve('public', 'icons', 'icon_production_128.png'),
                failureSound: 'Ping',
                showDuration: true,
                suppressWarning: true,
            })
        );
    }

    // notify the developer of type errors
    plugins.push(new TypeErrorNotifierPlugin());

    // define the environment variables that are available within the extension code
    plugins.push(
        new webpack.DefinePlugin({
            'process.env': JSON.stringify({
                SEMANTIC_VERSION: process.env.SEMANTIC_VERSION,
                NODE_ENV: mode,
                ...dotenv.config({ path: `.env.${mode}` }).parsed,
            } satisfies typeof process.env),
        })
    );

    // provide some global nodejs variables so that nodejs libraries can be used
    plugins.push(
        new webpack.ProvidePlugin({
            Buffer: ['buffer', 'Buffer'],
            process: 'process/browser',
        })
    );

    return plugins;
}
