// SPDX-FileCopyrightText: 2022-2024 Sarah Vaupel <sarah.vaupel@uniworx.de>, David Mosbach <david.mosbach@uniworx.de>, Gregor Kleen <gregor.kleen@ifi.lmu.de>, Sarah Vaupel <sarah.vaupel@ifi.lmu.de>, Sarah Vaupel <vaupel.sarah@campus.lmu.de>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

const webpack = require('webpack');
const path = require('node:path');
// import { execSync } from 'node:child_process';
const tmp = require('tmp');
tmp.setGracefulCleanup();
const fs = require('fs-extra');

const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const { WebpackManifestPlugin } = require('webpack-manifest-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const yaml = require('js-yaml');
const crypto = require('crypto');

// import { version as webpackVersion } from 'webpack/package.json' assert { type: 'json' }; // version.split('.').slice(0, 2).join('.');
// import { version as packageVersion } from './package.json' assert { type: 'json' };

const webpackJson = require('webpack/package.json');
const webpackVersion = webpackJson.version.split('.').slice(0, 2).join('.');
const packageJson = require('./package.json');
const packageVersion = packageJson.version;
const i18nJson = require('./config/i18n.json');

async function webpackConfig() {
  const wellKnownDir = 'well-known';
  const wellKnownCacheDir = '.cache/well-known';
  const staticDir = 'static';
  const assetsDir = 'assets';
  const faviconsDir = `${assetsDir}/favicons`;

  return {
    module: {
      rules: [
        {
          loader: 'babel-loader',
          options: {
            plugins: ['@babel/plugin-syntax-dynamic-import'],
            presets: [
              [
                '@babel/preset-env',
                {
                  modules: false,
                  targets: {
                    edge: "17",
                    firefox: "50",
                    chrome: "60",
                    safari: "11.1",
                    ie: "11",
                  },
                  useBuiltIns: "usage",
                  corejs: 3
                }
              ]
            ]
          },
          test: /\.js$/i,
          exclude: /node_modules/,
        },
        {
          test: /\.(sa|sc|c)ss$/i,
          use: [
            MiniCssExtractPlugin.loader,
            { loader: 'css-loader', options: { sourceMap: true }},
            { loader: 'postcss-loader', options: {
              sourceMap: true,
              postcssOptions: { plugins: [ 'postcss-preset-env', 'autoprefixer' ], },
            }},
            { loader: 'path.resolve-url-loader', options: { sourceMap: true }},
            { loader: 'sass-loader', options: { implementation: require('sass'), sourceMap: true }},
          ],
        },
        {
          test: /\.(woff(2)?|ttf|eot|svg)(\?.*)?$/i,
          type: 'asset'
        }
      ]
    },

    entry: {
      main: [ path.resolve('frontend/src/polyfill.js'),
              path.resolve('frontend/src/main.js'),
            ]
    },

    plugins: [
      new MiniCssExtractPlugin({
        // Options similar to the same options in webpackOptions.output
        // all options are optional
        filename: '[chunkhash].css',
        chunkFilename: '[chunkhash].css',
        ignoreOrder: false, // Enable to remove warnings about conflicting order
      }),
      new WebpackManifestPlugin({
        fileName: path.resolve('config/webpack.yml'),
        publicPath: `wp-${webpackVersion}`,
        generate: (seed, files, entrypoints) => Object.keys(entrypoints).reduce((acc, fs) => ({...acc, [fs]: files.filter(file => entrypoints[fs].filter(basename => !(/\.map$/.test(basename))).some(basename => file.path.endsWith(basename))).filter(file => file.isInitial).map(file => file.path)}), {}),
        serialize: yaml.dump
      }),
      new CleanWebpackPlugin({
        cleanOnceBeforeBuildPatterns: [ path.resolve(staticDir),
                                        path.resolve(wellKnownDir),
                                      ]
      }),
      new webpack.IgnorePlugin({
        resourceRegExp: /^\.\/locale$/,
        contextRegExp: /moment$/
      }),
      new webpack.DefinePlugin({
        VERSION: JSON.stringify(packageVersion)
      }),
      ...(() => {
        const langs = new Set();
        function findLangs(json, langs = new Set()) {
          if (json && json._i18n) {
            Object.keys(json).forEach(key => {
              if (key !== '_i18n') {
                langs.add(key);
              }
            })
          } else {
            console.error('Invalid i18nJson format!');
          }
          return langs;
        }
        findLangs(i18nJson, langs);

        const cacheHash = crypto.createHash('sha256');
        cacheHash.update(JSON.stringify(langs));

        const cacheFiles = new Set([
          path.resolve('config/robots.txt'),
          path.resolve('assets/favicon.svg'),
        ]);

        for (const cacheFile of cacheFiles) {
          cacheHash.update(fs.readFileSync(cacheFile));
        }

        const cacheDigest = cacheHash.copy().digest('hex');

        let cachedVersion = undefined;

        const versionFile = path.resolve(wellKnownCacheDir, `${cacheDigest}.version`);
        try {
          if (fs.existsSync(versionFile)) {
            cachedVersion = fs.readFileSync(versionFile, 'utf8');
          }
        } catch (e) {
          console.error(e);
        }

        const versionDigest = cacheHash.digest('hex');

        return Array.from(langs).map(lang => {
          return [
            new CopyPlugin({
              patterns: [
                { from: 'config/robots.txt', to: path.resolve(wellKnownDir, lang, 'robots.txt') },
                { from: `${faviconsDir}/include.html`, to: path.resolve(wellKnownDir, lang, 'html_code.html') },
                { from: `${faviconsDir}/*.png`, to: path.resolve(wellKnownDir, lang, '[name][ext]') },
              ]
            }),
          ];
        }).flat(1);
      })()
    ],

    output: {
      chunkFilename: '[chunkhash].js',
      filename: '[chunkhash].js',
      path: path.resolve(staticDir, `wp-${webpackVersion}`),
      publicPath: `/${staticDir}/res/wp-${webpackVersion}/`,
      hashFunction: 'shake256',
      hashDigestLength: 36
    },

    optimization: {
      minimize: true,
      minimizer: [
        new TerserPlugin({
          parallel: true,
          terserOptions: {
            sourceMap: true,
          },
        }),
        new CssMinimizerPlugin({
          parallel: true,
          minimizerOptions: {
            preset: [
              'default',
              { discardComments: { removeAll: true, } },
            ],
          },
        }),
      ],
      moduleIds: 'named',
      chunkIds: 'named',
      runtimeChunk: 'single',
      realContentHash: false
    },

    mode: 'production',

    recordsPath: path.join(path.resolve('records.json')),

    performance: {
      assetFilter: (assetFilename) => !(/\.(map|svg|ttf|eot)$/.test(assetFilename))
    },

    devtool: 'source-map'
  };
};

module.exports = webpackConfig;