const webpack = require("webpack");
const fs = require("fs");
const path = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const WorkboxPlugin = require("workbox-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const zopfli = require("@gfx/zopfli");
const ManifestPlugin = require("webpack-manifest-plugin");
const paths = require("./paths.js");
const { babelLoaderConfig } = require("./babel.js");

let version = fs
  .readFileSync(path.resolve(paths.polymer_dir, "setup.py"), "utf8")
  .match(/\d{8}\.\d+/);
if (!version) {
  throw Error("Version not found");
}
version = version[0];

const genMode = (isProdBuild) => (isProdBuild ? "production" : "development");
const genDevTool = (isProdBuild) =>
  isProdBuild ? "cheap-source-map" : "inline-cheap-module-source-map";
const genFilename = (isProdBuild, dontHash = new Set()) => ({ chunk }) => {
  if (!isProdBuild || dontHash.has(chunk.name)) {
    return `${chunk.name}.js`;
  }
  return `${chunk.name}.${chunk.hash.substr(0, 8)}.js`;
};
const genChunkFilename = (isProdBuild, isStatsBuild) =>
  isProdBuild && !isStatsBuild ? "chunk.[chunkhash].js" : "[name].chunk.js";

const resolve = {
  extensions: [".ts", ".js", ".json", ".tsx"],
  alias: {
    react: "preact-compat",
    "react-dom": "preact-compat",
    // Not necessary unless you consume a module using `createClass`
    "create-react-class": "preact-compat/lib/create-react-class",
    // Not necessary unless you consume a module requiring `react-dom-factories`
    "react-dom-factories": "preact-compat/lib/react-dom-factories",
  },
};

const cssLoader = {
  test: /\.css$/,
  use: "raw-loader",
};
const htmlLoader = {
  test: /\.(html)$/,
  use: {
    loader: "html-loader",
    options: {
      exportAsEs6Default: true,
    },
  },
};

const plugins = [
  // Ignore moment.js locales
  new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
  // Color.js is bloated, it contains all color definitions for all material color sets.
  new webpack.NormalModuleReplacementPlugin(
    /@polymer\/paper-styles\/color\.js$/,
    path.resolve(paths.polymer_dir, "src/util/empty.js")
  ),
  // Ignore roboto pointing at CDN. We use local font-roboto-local.
  new webpack.NormalModuleReplacementPlugin(
    /@polymer\/font-roboto\/roboto\.js$/,
    path.resolve(paths.polymer_dir, "src/util/empty.js")
  ),
  // Ignore mwc icons pointing at CDN.
  new webpack.NormalModuleReplacementPlugin(
    /@material\/mwc-icon\/mwc-icon-font\.js$/,
    path.resolve(paths.polymer_dir, "src/util/empty.js")
  ),
];

const optimization = (latestBuild) => ({
  minimizer: [
    new TerserPlugin({
      cache: true,
      parallel: true,
      extractComments: true,
      terserOptions: {
        ecma: latestBuild ? undefined : 5,
      },
    }),
  ],
});

const createAppConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
  const isCI = process.env.CI === "true";

  // Create an object mapping browser urls to their paths during build
  const translationMetadata = require("../build-translations/translationMetadata.json");
  const workBoxTranslationsTemplatedURLs = {};
  const englishFP = translationMetadata["translations"]["en"]["fingerprints"];
  Object.keys(englishFP).forEach((key) => {
    workBoxTranslationsTemplatedURLs[
      `/static/translations/${englishFP[key]}`
    ] = `build-translations/output/${key}.json`;
  });

  const entry = {
    app: "./src/entrypoints/app.ts",
    authorize: "./src/entrypoints/authorize.ts",
    onboarding: "./src/entrypoints/onboarding.ts",
    core: "./src/entrypoints/core.ts",
    compatibility: "./src/entrypoints/compatibility.ts",
    "custom-panel": "./src/entrypoints/custom-panel.ts",
    "hass-icons": "./src/entrypoints/hass-icons.ts",
  };

  return {
    mode: genMode(isProdBuild),
    devtool: genDevTool(isProdBuild),
    entry,
    module: {
      rules: [babelLoaderConfig({ latestBuild }), cssLoader, htmlLoader],
    },
    optimization: optimization(latestBuild),
    plugins: [
      new ManifestPlugin(),
      new webpack.DefinePlugin({
        __DEV__: JSON.stringify(!isProdBuild),
        __DEMO__: false,
        __BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
        __VERSION__: JSON.stringify(version),
        __STATIC_PATH__: "/static/",
        "process.env.NODE_ENV": JSON.stringify(
          isProdBuild ? "production" : "development"
        ),
      }),
      ...plugins,
      isProdBuild &&
        !isCI &&
        !isStatsBuild &&
        new CompressionPlugin({
          cache: true,
          exclude: [/\.js\.map$/, /\.LICENSE$/, /\.py$/, /\.txt$/],
          algorithm(input, compressionOptions, callback) {
            return zopfli.gzip(input, compressionOptions, callback);
          },
        }),
      latestBuild &&
        new WorkboxPlugin.InjectManifest({
          swSrc: "./src/entrypoints/service-worker-hass.js",
          swDest: "service_worker.js",
          importWorkboxFrom: "local",
          include: [/\.js$/],
          templatedURLs: {
            ...workBoxTranslationsTemplatedURLs,
            "/static/icons/favicon-192x192.png":
              "public/icons/favicon-192x192.png",
            "/static/fonts/roboto/Roboto-Light.ttf":
              "node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Light.ttf",
            "/static/fonts/roboto/Roboto-Medium.ttf":
              "node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Medium.ttf",
            "/static/fonts/roboto/Roboto-Regular.ttf":
              "node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Regular.ttf",
            "/static/fonts/roboto/Roboto-Bold.ttf":
              "node_modules/@polymer/font-roboto-local/fonts/roboto/Roboto-Bold.ttf",
          },
        }),
    ].filter(Boolean),
    output: {
      filename: genFilename(isProdBuild),
      chunkFilename: genChunkFilename(isProdBuild, isStatsBuild),
      path: latestBuild ? paths.output : paths.output_es5,
      publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
    },
    resolve,
  };
};

const createDemoConfig = ({ isProdBuild, latestBuild, isStatsBuild }) => {
  return {
    mode: genMode(isProdBuild),
    devtool: genDevTool(isProdBuild),
    entry: {
      main: "./demo/src/entrypoint.ts",
      compatibility: "./src/entrypoints/compatibility.ts",
    },
    module: {
      rules: [babelLoaderConfig({ latestBuild }), cssLoader, htmlLoader],
    },
    optimization: optimization(latestBuild),
    plugins: [
      new ManifestPlugin(),
      new webpack.DefinePlugin({
        __DEV__: !isProdBuild,
        __BUILD__: JSON.stringify(latestBuild ? "latest" : "es5"),
        __VERSION__: JSON.stringify("DEMO"),
        __DEMO__: true,
        __STATIC_PATH__: "/static/",
        "process.env.NODE_ENV": JSON.stringify(
          isProdBuild ? "production" : "development"
        ),
      }),
      ...plugins,
    ].filter(Boolean),
    resolve,
    output: {
      filename: genFilename(isProdBuild),
      chunkFilename: genChunkFilename(isProdBuild, isStatsBuild),
      path: path.resolve(
        paths.demo_root,
        latestBuild ? "frontend_latest" : "frontend_es5"
      ),
      publicPath: latestBuild ? "/frontend_latest/" : "/frontend_es5/",
    },
  };
};

module.exports = {
  resolve,
  plugins,
  optimization,
  createAppConfig,
  createDemoConfig,
};