const webpack = require("webpack");
const { resolve } = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const nodeExternals = require("webpack-node-externals");
const CopyPlugin = require("copy-webpack-plugin");
const { ServiceWorkerPlugin } = require("service-worker-webpack");
const {
  bundledSyntaxHighlighters,
  lazySyntaxHighlighters,
} = require("./src/shared/build-config");

const banner = `
  hash:[contentHash], chunkhash:[chunkhash], name:[name], filebase:[base], query:[query], file:[file]
  Source code: https://github.com/LemmyNet/lemmy-ui
  Created by dessalines
  @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL v3.0
  `;

const contextPlugin = (() => {
  const check = x => x.match(/^[0-9a-zA-Z-]+$/);
  const eagerNames = bundledSyntaxHighlighters.filter(check).join("|");
  const eagerHljs = new RegExp(`^[.][/\\\\](${eagerNames})[.]js\$`);
  let lazyHljs;
  if (lazySyntaxHighlighters === "*") {
    lazyHljs = new RegExp(`^[.][/\\\\](?!(${eagerNames})[.]js\$)[^.]+[.]js\$`);
  } else {
    const lazyNames = lazySyntaxHighlighters.filter(check).join("|");
    lazyHljs = new RegExp(`^[.][/\\\\](${lazyNames})[.]js\$`);
  }

  // Plugin will be used for all parameterized dynamic imports.
  return new webpack.ContextReplacementPlugin(/.*/, options => {
    if (/^highlight.js\/lib\/languages$/.test(options.request)) {
      if (options.mode == "eager") {
        options.regExp = eagerHljs;
      } else {
        options.regExp = lazyHljs;
      }
    } else if (/^date-fns\/locale$/.test(options.request)) {
    } else {
      return;
    }
    options.recursive = false;
    options.request = resolve(__dirname, "node_modules/" + options.request);
  });
})();

module.exports = (env, argv) => {
  const mode = argv.mode;

  const base = {
    output: {
      hashFunction: "xxhash64",
    },
    resolve: {
      extensions: [".js", ".jsx", ".ts", ".tsx"],
      alias: {
        "@": resolve(__dirname, "src/"),
        "@utils": resolve(__dirname, "src/shared/utils/"),
      },
    },
    performance: {
      hints: false,
    },
    module: {
      rules: [
        {
          test: /\.(scss|css)$/i,
          use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
        },
        {
          test: /\.(js|jsx|tsx|ts)$/, // All ts and tsx files will be process by
          exclude: /node_modules/, // ignore node_modules
          loader: "babel-loader",
        },
        // Due to some weird babel issue: https://github.com/webpack/webpack/issues/11467
        {
          test: /\.m?js/,
          resolve: {
            fullySpecified: false,
          },
        },
      ],
    },
    plugins: [
      new webpack.DefinePlugin({
        "process.env.COMMIT_HASH": `"${env.COMMIT_HASH}"`,
        "process.env.NODE_ENV": `"${mode}"`,
      }),
      new MiniCssExtractPlugin({
        filename: "styles/styles.css",
      }),
      new CopyPlugin({
        patterns: [{ from: "./src/assets", to: "./assets" }],
      }),
      new webpack.BannerPlugin({
        banner,
      }),
      contextPlugin,
    ],
  };

  const serverConfig = {
    ...base,
    entry: "./src/server/index.tsx",
    output: {
      ...base.output,
      filename: "js/server.js",
      publicPath: "/",
      chunkLoading: false, // everything bundled
    },
    target: "node",
    externals: [nodeExternals(), "inferno-helmet"],
  };

  const clientConfig = {
    ...base,
    entry: "./src/client/index.tsx",
    output: {
      ...base.output,
      filename: "js/client.js",
      publicPath: `/static/${env.COMMIT_HASH}/`,
      chunkFilename: "js/[name].client.js", // predictable names for manual preload
    },
    plugins: [
      ...base.plugins,
      new ServiceWorkerPlugin({
        enableInDevelopment: mode !== "development", // this may seem counterintuitive, but it is correct
        workbox: {
          cacheId: "lemmy",
          include: [/(assets|styles|js)\/.+\..+$/g],
          inlineWorkboxRuntime: true,
          runtimeCaching: [
            {
              urlPattern: ({
                sameOrigin,
                url: { pathname, host },
                request: { method },
              }) =>
                (sameOrigin || host.includes("localhost")) &&
                (!(
                  pathname.includes("pictrs") || pathname.includes("static")
                ) ||
                  method === "POST"),
              handler: "NetworkFirst",
              options: {
                cacheName: "instance-cache",
              },
            },
            {
              urlPattern: ({ url: { pathname, host }, sameOrigin }) =>
                (sameOrigin || host.includes("localhost")) &&
                pathname.includes("static"),
              handler: mode === "development" ? "NetworkFirst" : "CacheFirst",
              options: {
                cacheName: "static-cache",
                expiration: {
                  maxAgeSeconds: 60 * 60 * 24,
                },
              },
            },
            {
              urlPattern: ({ url: { pathname }, request: { method } }) =>
                pathname.includes("pictrs") && method === "GET",
              handler: "StaleWhileRevalidate",
              options: {
                cacheName: "image-cache",
                expiration: {
                  maxAgeSeconds: 60 * 60 * 24,
                },
              },
            },
          ],
        },
      }),
    ],
  };

  if (mode === "development") {
    // serverConfig.cache = {
    //   type: "filesystem",
    //   name: "server",
    // };

    const RunNodeWebpackPlugin = require("run-node-webpack-plugin");
    serverConfig.plugins.push(
      new RunNodeWebpackPlugin({ runOnlyInWatchMode: true }),
    );
  } else if (mode === "none") {
    const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
    serverConfig.plugins.push(new BundleAnalyzerPlugin());
  }

  return [serverConfig, clientConfig];
};