import path from "path"
import webpack, { Configuration, WebpackPluginInstance, RuleSetUseItem } from "webpack"
import { WebpackManifestPlugin } from "webpack-manifest-plugin"
import TerserPlugin from "terser-webpack-plugin"
import MiniCssExtractPlugin from "mini-css-extract-plugin"
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin"
import LoadablePlugin from "@loadable/webpack-plugin"
import { BundleAnalyzerPlugin } from "webpack-bundle-analyzer"

export const isDev = process.env.NODE_ENV === "development"
const isLocal = process.env.LOCAL === "true"
const getStyleLoaders = (isWeb: boolean, isSass?: boolean) => {
    let loaders: RuleSetUseItem[] = [
        {
            loader: "css-loader",
            options: {
                importLoaders: isSass ? 2 : 1,
                modules: {
                    auto: true,
                    localIdentName: "[path][name]__[local]", // Don't use hash:base64, a bug makes it generate different hashes in the .js and the .css files
                    exportOnlyLocals: !isWeb,
                },
            },
        },
        { loader: "postcss-loader" },
    ]

    if (isWeb) loaders = [MiniCssExtractPlugin.loader, ...loaders]

    if (isSass) loaders = [...loaders, { loader: "sass-loader" }]

    return loaders
}

const getPlugins = (isWeb: boolean) => {
    let plugins = [
        new webpack.ProgressPlugin(),
        new WebpackManifestPlugin({
            fileName: path.resolve(process.cwd(), "public/webpack-assets.json"),
            filter: (file) => file.isInitial,
        }),
        new LoadablePlugin({
            writeToDisk: true,
            filename: "../loadable-stats.json",
        }),
        // Setting global variables
        new webpack.DefinePlugin({
            __CLIENT__: isWeb,
            __SERVER__: !isWeb,
            __DEV__: isDev,
            __LOCAL__: isLocal,
        }),
    ]

    if (isDev)
        plugins = [
            ...plugins,
            // Runs TypeScript type checker on a separate process
            new ForkTsCheckerWebpackPlugin({
                // (Required) Same as eslint command
                eslint: { files: "./src/**/*.{js,jsx,ts,tsx}" },
            }),
        ]

    if (!isDev)
        plugins = [
            ...plugins,
            // Visualize size of webpack output files, see: https://github.com/webpack-contrib/webpack-bundle-analyzer
            new BundleAnalyzerPlugin({
                analyzerMode: process.env.NODE_ENV === "analyze" ? "server" : "disabled",
            }),
        ]

    return plugins
}

const config = (isWeb = false): Configuration => ({
    mode: isDev ? "development" : "production",
    stats: "minimal",
    context: path.resolve(process.cwd()),
    output: { clean: true },
    optimization: {
        minimizer: [
            new TerserPlugin({
                // See more options: https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
                terserOptions: { compress: { drop_console: false } },
            }),
        ],
    },
    plugins: getPlugins(isWeb) as WebpackPluginInstance[],
    module: {
        rules: [
            {
                test: /\.(t|j)sx?$/,
                exclude: /node_modules/,
                loader: "babel-loader",
                options: {
                    caller: { target: isWeb ? "web" : "node" },
                    cacheDirectory: isDev,
                },
            },
            {
                test: /\.css$/,
                use: getStyleLoaders(isWeb),
            },
            {
                test: /\.(scss|sass)$/,
                use: getStyleLoaders(isWeb, true),
            },
            {
                test: /\.(woff2?|eot|ttf|otf)$/i,
                type: "asset",
                generator: { emit: isWeb },
            },
            {
                test: /\.(png|svg|jpe?g|gif)$/i,
                type: "asset",
                generator: { emit: isWeb },
            },
        ],
    },
    resolve: {
        modules: ["src", "node_modules"],
        extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
    },
})

export function getClientEnvironment(allowedKeys: string[]): any {
    const raw = Object.keys(process.env)
        // Custom regex to allow only a certain category of variables available to the application
        .filter((key) => allowedKeys.indexOf(key) >= 0)
        .reduce(
            (env: any, key: string) => {
                env[key] = process.env[key]
                return env
            },
            {
                NODE_ENV: process.env.NODE_ENV || "development",
            }
        )
    // Stringify all values so we can feed into Webpack DefinePlugin
    const stringified = {
        "process.env": Object.keys(raw).reduce((env: any, key: string) => {
            env[key] = JSON.stringify(raw[key])
            return env
        }, {}),
    }

    return stringified
}

export default config
