const pkgJson = require('./package.json');
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const importHelper = require('@babel/helper-module-imports');

/* Allow to customise builds through env-vars */
const env = process.env;

const addSubtitleSupport = !!env.SUBTITLE || !!env.USE_SUBTITLES;
const addAltAudioSupport = !!env.ALT_AUDIO || !!env.USE_ALT_AUDIO;
const addEMESupport = !!env.EME_DRM || !!env.USE_EME_DRM;

const createDefinePlugin = (type) => {
  const buildConstants = {
    __VERSION__: JSON.stringify(pkgJson.version),
    __USE_SUBTITLES__: JSON.stringify(type === 'main' || addSubtitleSupport),
    __USE_ALT_AUDIO__: JSON.stringify(type === 'main' || addAltAudioSupport),
    __USE_EME_DRM__: JSON.stringify(type === 'main' || addEMESupport),
  };
  return new webpack.DefinePlugin(buildConstants);
};

const basePlugins = [
  new webpack.BannerPlugin({
    entryOnly: true,
    raw: true,
    banner: 'typeof window !== "undefined" &&',
  }), // SSR/Node.js guard
];
const mainPlugins = [...basePlugins, createDefinePlugin('main')];
const lightPlugins = [...basePlugins, createDefinePlugin('light')];

const baseConfig = {
  mode: 'development',
  entry: './src/hls',
  optimization: {
    splitChunks: false,
  },
  resolve: {
    // Add `.ts` as a resolvable extension.
    extensions: ['.ts', '.js'],
  },
  module: {
    strictExportPresence: true,
    rules: [
      {
        test: /\.(ts|js)$/,
        exclude: [path.resolve(__dirname, 'node_modules')],
        loader: 'babel-loader',
        options: {
          babelrc: false,
          presets: [
            '@babel/preset-typescript',
            [
              '@babel/preset-env',
              {
                loose: true,
                modules: false,
                targets: {
                  browsers: [
                    'chrome >= 47',
                    'firefox >= 51',
                    'ie >= 11',
                    'safari >= 8',
                    'ios >= 8',
                    'android >= 4',
                  ],
                },
              },
            ],
          ],
          plugins: [
            [
              '@babel/plugin-proposal-class-properties',
              {
                loose: true,
              },
            ],
            '@babel/plugin-proposal-object-rest-spread',
            {
              visitor: {
                CallExpression: function (espath) {
                  if (espath.get('callee').matchesPattern('Number.isFinite')) {
                    espath.node.callee = importHelper.addNamed(
                      espath,
                      'isFiniteNumber',
                      path.resolve('src/polyfills/number')
                    );
                  } else if (
                    espath
                      .get('callee')
                      .matchesPattern('Number.MAX_SAFE_INTEGER')
                  ) {
                    espath.node.callee = importHelper.addNamed(
                      espath,
                      'MAX_SAFE_INTEGER',
                      path.resolve('src/polyfills/number')
                    );
                  }
                },
              },
            },
            ['@babel/plugin-transform-object-assign'],
            ['@babel/plugin-proposal-optional-chaining'],
          ],
        },
      },
    ],
  },
  node: {
    global: false,
    process: false,
    __filename: false,
    __dirname: false,
    Buffer: false,
    setImmediate: false,
  },
};

function getAliasesForLightDist() {
  let aliases = {};

  if (!addEMESupport) {
    aliases = Object.assign({}, aliases, {
      './controller/eme-controller': './empty.js',
    });
  }

  if (!addSubtitleSupport) {
    aliases = Object.assign(aliases, {
      './utils/cues': './empty.js',
      './controller/timeline-controller': './empty.js',
      './controller/subtitle-track-controller': './empty.js',
      './controller/subtitle-stream-controller': './empty.js',
    });
  }

  if (!addAltAudioSupport) {
    aliases = Object.assign(aliases, {
      './controller/audio-track-controller': './empty.js',
      './controller/audio-stream-controller': './empty.js',
    });
  }

  return aliases;
}

const multiConfig = [
  {
    name: 'debug',
    mode: 'development',
    output: {
      filename: 'hls.js',
      chunkFilename: '[name].js',
      sourceMapFilename: 'hls.js.map',
      path: path.resolve(__dirname, 'dist'),
      publicPath: '/dist/',
      library: 'Hls',
      libraryTarget: 'umd',
      libraryExport: 'default',
      globalObject: 'this', // https://github.com/webpack/webpack/issues/6642#issuecomment-370222543
    },
    plugins: mainPlugins,
    devtool: 'source-map',
  },
  {
    name: 'dist',
    mode: 'production',
    output: {
      filename: 'hls.min.js',
      chunkFilename: '[name].js',
      path: path.resolve(__dirname, 'dist'),
      publicPath: '/dist/',
      library: 'Hls',
      libraryTarget: 'umd',
      libraryExport: 'default',
      globalObject: 'this',
    },
    plugins: mainPlugins,
    devtool: 'source-map',
  },
  {
    name: 'light',
    mode: 'development',
    output: {
      filename: 'hls.light.js',
      chunkFilename: '[name].js',
      sourceMapFilename: 'hls.light.js.map',
      path: path.resolve(__dirname, 'dist'),
      publicPath: '/dist/',
      library: 'Hls',
      libraryTarget: 'umd',
      libraryExport: 'default',
      globalObject: 'this',
    },
    resolve: {
      alias: getAliasesForLightDist(),
    },
    plugins: lightPlugins,
    devtool: 'source-map',
  },
  {
    name: 'light-dist',
    mode: 'production',
    output: {
      filename: 'hls.light.min.js',
      chunkFilename: '[name].js',
      path: path.resolve(__dirname, 'dist'),
      publicPath: '/dist/',
      library: 'Hls',
      libraryTarget: 'umd',
      libraryExport: 'default',
      globalObject: 'this',
    },
    resolve: {
      alias: getAliasesForLightDist(),
    },
    plugins: lightPlugins,
    devtool: 'source-map',
  },
  {
    name: 'demo',
    entry: './demo/main',
    mode: 'development',
    output: {
      filename: 'hls-demo.js',
      chunkFilename: '[name].js',
      sourceMapFilename: 'hls-demo.js.map',
      path: path.resolve(__dirname, 'dist'),
      publicPath: '/dist/',
      library: 'HlsDemo',
      libraryTarget: 'umd',
      libraryExport: 'default',
      globalObject: 'this', // https://github.com/webpack/webpack/issues/6642#issuecomment-370222543
    },
    plugins: [
      ...mainPlugins,
      new webpack.DefinePlugin({
        __NETLIFY__: JSON.stringify(
          process.env.NETLIFY === 'true'
            ? {
                branch: process.env.BRANCH,
                commitRef: process.env.COMMIT_REF,
                reviewID:
                  process.env.PULL_REQUEST === 'true'
                    ? parseInt(process.env.REVIEW_ID)
                    : null,
              }
            : {}
        ),
      }),
    ],
    devtool: 'source-map',
  },
].map((config) => {
  const baseClone = merge({}, baseConfig);
  // Strip console.assert statements from production webpack targets
  if (config.mode === 'production') {
    // eslint-disable-next-line no-restricted-properties
    baseClone.module.rules
      .find((rule) => rule.loader === 'babel-loader')
      .options.plugins.push([
        'transform-remove-console',
        {
          exclude: ['log', 'warn', 'error'],
        },
      ]);
  }
  return merge(baseClone, config);
});

// webpack matches the --env arguments to a string; for example, --env.debug.min translates to { debug: true, min: true }
module.exports = (envArgs) => {
  const requestedConfigs = Object.keys(envArgs).filter(
    (key) => !/^WEBPACK_/.test(key)
  );
  let configs;
  if (!requestedConfigs.length) {
    // If no arguments are specified, return every configuration
    configs = multiConfig;
  } else {
    // Filter out enabled configs
    const enabledConfigs = multiConfig.filter((config) =>
      requestedConfigs.includes(config.name)
    );
    if (!enabledConfigs.length) {
      throw new Error(
        `Couldn't find a valid config with the names ${JSON.stringify(
          requestedConfigs
        )}. Known configs are: ${multiConfig
          .map((config) => config.name)
          .join(', ')}`
      );
    }

    configs = enabledConfigs;
  }

  console.log(
    `Building configs: ${configs.map((config) => config.name).join(', ')}.\n`
  );
  return configs;
};