/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-env node */

require('@babel/register')({
    extensions: ['.es6', '.es', '.jsx', '.js', '.mjs', '.ts', '.tsx'],
});

const path = require('path');
const webpack = require('webpack');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const SitemapPlugin = require('sitemap-webpack-plugin').default;
const CSPPlugin = require('csp-webpack-plugin');
const WebpackBar = require('webpackbar');
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const EagerImportsPlugin = require('eager-imports-webpack-plugin').default;
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const config = require('./config');
const SUPPORTED_LANGUAGES = Object.keys(require('app/i18n').default);
const { getCountriesList } = require('app/components/i18n/localeFlags');
const rootPath = path.resolve('./packages');
const outputPath = path.join(__dirname, 'build');

const isProduction = process.env.NODE_ENV === 'production';
const isAnalyze = process.argv.some((arg) => arg === '--analyze');

const isDockerized = !!process.env.DOCKERIZED;
const isStorybook = process.env.APP_ENV === 'storybook';
const isCI = !!process.env.CI;
const isSilent = isCI || process.argv.some((arg) => /quiet/.test(arg));
const isCspEnabled = false;
const enableDll = !isProduction && !isStorybook;
const webpackEnv = isProduction ? 'production' : 'development';

process.env.NODE_ENV = webpackEnv;

const smp = new SpeedMeasurePlugin();

const webpackConfig = {
    mode: webpackEnv,

    cache: true,

    entry: {
        app: path.join(__dirname, 'packages/app'),
    },

    output: {
        path: outputPath,
        publicPath: '/',
        filename: `[name].js?[${isProduction ? 'chunkhash' : 'hash'}]`,
    },

    resolve: {
        modules: [rootPath, 'node_modules'],
        extensions: ['.js', '.jsx', '.json', '.ts', '.tsx'],
        alias: {
            'react-dom': isProduction ? 'react-dom' : '@hot-loader/react-dom',
        },
    },

    devtool: 'cheap-module-source-map',

    stats: {
        hash: false,
        version: false,
        timings: true,
        assets: false,
        chunks: false,
        modules: false,
        reasons: false,
        children: false,
        source: false,
        errors: true,
        errorDetails: true,
        warnings: false,
        publicPath: false,
    },

    plugins: [
        new WebpackBar(),
        new webpack.DefinePlugin({
            'window.SENTRY_DSN': config.sentryDSN ? JSON.stringify(config.sentryDSN) : undefined,
            'window.GA_ID': config.ga && config.ga.id ? JSON.stringify(config.ga.id) : undefined,
        }),
        new webpack.EnvironmentPlugin({
            NODE_ENV: process.env.NODE_ENV,
            __VERSION__: config.version || '',
        }),
        new HtmlWebpackPlugin({
            template: 'packages/app/index.ejs',
            favicon: 'packages/app/favicon.ico',
            scripts: enableDll ? ['/dll/vendor.dll.js'] : [],
            hash: false, // webpack does this for all our assets automagically
            filename: 'index.html',
            inject: false,
            minify: {
                collapseWhitespace: isProduction,
            },
            isCspEnabled,
        }),
        new SitemapPlugin(
            'https://account.ely.by',
            ['/', '/register', '/resend-activation', '/activation', '/forgot-password', '/rules'],
            {
                lastMod: true,
                changeFreq: 'weekly',
            },
        ),
        // restrict webpack import context, to create chunks only for supported locales
        // @see services/i18n/intlPolyfill.js
        new webpack.ContextReplacementPlugin(/locale-data/, new RegExp(`/(${SUPPORTED_LANGUAGES.join('|')})\\.js$`)),
        // @see components/i18n/localeFlags.js
        new webpack.ContextReplacementPlugin(
            /flag-icon-css\/flags\/4x3/,
            new RegExp(`/(${getCountriesList().join('|')})\\.svg`),
        ),
        // @see components/i18n/localeFlags.js
        new webpack.ContextReplacementPlugin(
            /app\/components\/i18n\/flags/,
            new RegExp(`/(${getCountriesList().join('|')})\\.svg`),
        ),
    ],

    module: {
        rules: [
            {
                test: /\.s?css$/,
                use: [
                    {
                        loader: 'style-loader',
                        options: {
                            // style-loader@1.1.2 is still buggy. It breaks our icon styles
                            // (vertical align is broken and styles applied multiple times)
                            // so we can not use it and it's new `esModule` options
                            // esModule: true,
                        },
                    },
                    {
                        loader: 'css-loader',
                        options: {
                            modules: {
                                localIdentName: isProduction ? '[hash:base64:5]' : '[path][name]-[local]',
                            },
                            esModule: true,
                            importLoaders: 2,
                            sourceMap: !isProduction,
                        },
                    },
                    {
                        loader: 'sass-loader',
                        options: {
                            sourceMap: !isProduction,
                        },
                    },
                    {
                        loader: 'postcss-loader',
                        options: {
                            sourceMap: !isProduction,
                        },
                    },
                ],
            },
            {
                test: /\.[tj]sx?$/,
                exclude: /node_modules/,
                use: [
                    {
                        loader: 'babel-loader',
                        options: {
                            envName: `browser-${webpackEnv}`,
                            cacheDirectory: true,
                        },
                    },
                ],
            },
            {
                test: /\.(png|gif|jpg|svg)$/,
                loader: 'file-loader',
                query: {
                    name: 'assets/[name].[ext]?[contenthash]',
                },
            },
            {
                test: /\.(woff|woff2|ttf)$/,
                loader: 'file-loader',
                query: {
                    name: 'assets/fonts/[name].[ext]?[contenthash]',
                },
            },
            {
                test: /\.html$/,
                loader: 'html-loader',
            },
            {
                // this is consumed by postcss-import
                // @see postcss.config.js
                test: /\.font\.(js|json)$/,
                type: 'javascript/auto',
                use: ['fontgen-loader'],
            },
        ],
    },

    resolveLoader: {
        alias: {
            'fontgen-loader': path.resolve('./packages/webpack-utils/fontgen-loader'),
        },
    },
};

if (isAnalyze) {
    webpackConfig.plugins.push(new BundleAnalyzerPlugin());
}

if (isProduction) {
    if (!isStorybook) {
        let cssExtractApplied = false;

        webpackConfig.module.rules.forEach((rule) => {
            if (rule.use && (rule.use[0] === 'style-loader' || rule.use[0].loader === 'style-loader')) {
                // replace `style-loader` with `MiniCssExtractPlugin`
                rule.use[0] = MiniCssExtractPlugin.loader;
                cssExtractApplied = true;
            }
        });

        if (!cssExtractApplied) {
            throw new Error('Can not locate style-loader to replace it with mini-css-extract-plugin loader');
        }

        webpackConfig.plugins.push(
            new MiniCssExtractPlugin({
                filename: '[name].css?[contenthash]',
            }),
        );
    }

    webpackConfig.devtool = 'hidden-source-map';

    webpackConfig.optimization = {
        moduleIds: 'hashed',
        runtimeChunk: 'single',
        splitChunks: {
            cacheGroups: {
                vendor: {
                    name: 'vendors',
                    priority: 0,
                    test: (m) => String(m.context).includes('node_modules'),
                    chunks: 'all',
                },
                polyfills: {
                    name: 'intl-polyfills',
                    priority: 1,
                    chunks: ({ name }) => ['intl', 'intl-pluralrules', 'intl-relativetimeformat'].includes(name),
                    enforce: true,
                },
                ...SUPPORTED_LANGUAGES.reduce((acc, locale) => {
                    const localePolyfills = [
                        `intl-${locale}-js`,
                        `intl-pluralrules-${locale}-js`,
                        `intl-relativetimeformat-${locale}-js`,
                    ];

                    acc[locale] = {
                        name: `locale-${locale}`,
                        priority: 1,
                        chunks: ({ name }) => name === `locale-${locale}-json`,
                        enforce: true,
                    };
                    acc[`${locale}Polyfill`] = {
                        name: `locale-${locale}-polyfill`,
                        priority: 2,
                        chunks: ({ name }) => localePolyfills.includes(name),
                        enforce: true,
                    };

                    return acc;
                }, {}),
            },
        },
    };
} else {
    webpackConfig.plugins.push(
        // force webpack to use mode: eager chunk imports in dev mode
        // this will improve build performance
        // this mode will be default for dev builds in webpack 5
        new EagerImportsPlugin(),
    );

    if (enableDll) {
        webpackConfig.plugins.push(
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: require('./dll/vendor.json'),
            }),
        );
    }

    webpackConfig.devServer = {
        host: 'localhost',
        port: 8080,
        proxy: config.apiHost && {
            '/api': {
                target: config.apiHost,
                changeOrigin: true, // add host http-header, based on target
                secure: false, // allow self-signed certs
            },
        },
        hot: true,
        inline: true,
        historyApiFallback: true,
    };
}

if (isCspEnabled) {
    webpackConfig.plugins.push(
        new CSPPlugin({
            'default-src': "'none'",
            'style-src': ["'self'", "'unsafe-inline'"],
            'script-src': [
                "'self'",
                "'nonce-edge-must-die'",
                "'unsafe-inline'",
                'https://www.google-analytics.com',
                'https://recaptcha.net/recaptcha/',
                'https://www.gstatic.com/recaptcha/',
                'https://www.gstatic.cn/recaptcha/',
            ],
            'img-src': ["'self'", 'data:', 'www.google-analytics.com'],
            'font-src': ["'self'", 'data:'],
            'connect-src': ["'self'", 'https://sentry.ely.by'].concat(isProduction ? [] : ['ws://localhost:8080']),
            'frame-src': ['https://www.google.com/recaptcha/', 'https://recaptcha.net/recaptcha/'],
            'report-uri': 'https://sentry.ely.by/api/2/csp-report/?sentry_key=088e7718236a4f91937a81fb319a93f6',
        }),
    );
}

if (isDockerized) {
    webpackConfig.watchOptions = {
        poll: 2000,
    };
    webpackConfig.devServer.host = '0.0.0.0';
}

if (isSilent) {
    webpackConfig.stats = {
        hash: false,
        version: false,
        timings: true,
        assets: false,
        chunks: false,
        modules: false,
        reasons: false,
        children: false,
        source: false,
        errors: true,
        errorDetails: true,
        warnings: false,
        publicPath: false,
    };
}

module.exports = smp.wrap(webpackConfig);
