/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var MiniCssExtractPlugin = require('mini-css-extract-plugin');
var CopyWebpackPlugin = require('copy-webpack-plugin');
var InsertLiveReloadPlugin = function InsertLiveReloadPlugin(options) {
  this.options = options || {};
  this.port = this.options.port || 35729;
  this.hostname = this.options.hostname || 'localhost';
}
var express = require('express');
var stringReplacePlugin = require('string-replace-webpack-plugin');

InsertLiveReloadPlugin.prototype.autoloadJs = function autoloadJs() {
  return
};

InsertLiveReloadPlugin.prototype.scriptTag = function scriptTag(source) {
  var reloadScriptTag = [
    '// webpack-livereload-plugin',
    '(function() {',
    '  if (typeof window === "undefined") { return };',
    '  var id = "webpack-livereload-plugin-script";',
    '  if (document.getElementById(id)) { return; }',
    '  var el = document.createElement("script");',
    '  el.id = id;',
    '  el.async = true;',
    '  el.src = "http://' + this.hostname + ':' + this.port + '/livereload.js";',
    '  document.getElementsByTagName("head")[0].appendChild(el);',
    '}());',
    ''
  ].join('\n');
  return reloadScriptTag + source;
};

InsertLiveReloadPlugin.prototype.applyCompilation = function applyCompilation(compilation) {
  compilation.mainTemplate.plugin('startup', this.scriptTag.bind(this));
};

InsertLiveReloadPlugin.prototype.apply = function apply(compiler) {
  this.compiler = compiler;
  compiler.plugin('compilation', this.applyCompilation.bind(this));
};

/**
 * Env
 * Get npm lifecycle event to identify the environment
 */
var ENV = process.env.npm_lifecycle_event;
var isTest = ENV === 'test';
var isProd = ENV.startsWith('build')
var isCI = ENV === 'build:ci'

module.exports = function makeWebpackConfig () {
  /**
   * Config
   * Reference: http://webpack.github.io/docs/configuration.html
   * This is the object where all configuration gets set
   */
  var config = {};

  /**
   * Entry
   * Reference: http://webpack.github.io/docs/configuration.html#entry
   * Should be an empty object if it's generating a test build
   * Karma will set this when it's a test build
   */
  config.entry = isTest ? {} : {
    app: './src/index.js'
  };

  var serverPort = process.env.SERVER_PORT || 8080;
  var webPort = process.env.WEB_PORT || 9000;

  /**
   * Output
   * Reference: http://webpack.github.io/docs/configuration.html#output
   * Should be an empty object if it's generating a test build
   * Karma will handle setting it up for you when it's a test build
   */
  config.output = isTest ? {} : {
    // Absolute output directory
    path: __dirname + '/dist',

    // Output path from the view of the page
    // Uses webpack-dev-server in development
    publicPath: isProd ? '' : 'http://localhost:' + webPort + '/',

    // Filename for entry points
    // Only adds hash in build mode
    filename: isProd ? '[name].[hash].js' : '[name].bundle.js',

    // Filename for non-entry points
    // Only adds hash in build mode
    chunkFilename: isProd ? '[name].[hash].js' : '[name].bundle.js'
  };

  /**
   * Devtool
   * Reference: http://webpack.github.io/docs/configuration.html#devtool
   * Type of sourcemap to use per build type
   */
  config.devtool = 'eval-source-map';
  if (isTest) {
    config.devtool = 'inline-source-map';
  } else if (isProd) {
    config.devtool = 'source-map';
  } else {
    config.devtool = 'eval-source-map';
  }

  /**
   * Loaders
   * Reference: http://webpack.github.io/docs/configuration.html#module-loaders
   * List: http://webpack.github.io/docs/list-of-loaders.html
   * This handles most of the magic responsible for converting modules
   */

  // Initialize module
  config.module = {
    rules: [{
      // headroom 0.9.3 doesn't work with webpack
      // https://github.com/WickyNilliams/headroom.js/issues/213#issuecomment-281106943
      test: require.resolve('headroom.js'),
      use: 'imports-loader?this=>window,define=>false,exports=>false'
    }, {
      // JS LOADER
      // Reference: https://github.com/babel/babel-loader
      // Transpile .js files using babel-loader
      // Compiles ES6 and ES7 into ES5 code
      test: /\.(js|jsx)$/,
      use: ['ng-annotate-loader', 'babel-loader'],
      exclude: /(node_modules)/,
    }, {
      // CSS LOADER
      // Reference: https://github.com/webpack/css-loader
      // Allow loading css through js
      //
      // Reference: https://github.com/postcss/postcss-loader
      // Postprocess your css with PostCSS plugins
      test: /\.(sa|sc|c)ss$/,
      // Reference: https://github.com/webpack-contrib/mini-css-extract-plugin
      // Extract css files in production builds
      //
      // Use style-loader in development.
      use: [
        !isProd ? 'style-loader' : MiniCssExtractPlugin.loader,
        'css-loader',
        {
          loader: 'postcss-loader',
          options: {
            ident: 'postcss',
            plugins: [
              require('autoprefixer')()
            ]
          }
        }]
    }, {
      // ASSET LOADER
      // Reference: https://github.com/webpack/file-loader
      // Copy png, jpg, jpeg, gif, svg, woff, woff2, ttf, eot files to output
      // Rename the file using the asset hash
      // Pass along the updated reference to your code
      // You can add here any file extension you want to get copied to your output
      test: /\.(png|jpg|jpeg|gif|svg|woff|woff2|ttf|eot)$/,
      use: 'file-loader'
    }, {
      // HTML LOADER
      // Reference: https://github.com/webpack/raw-loader
      // Allow loading html through js
      test: /\.html$/,
      use: 'raw-loader'
    }, {
      // STRING REPLACE PLUGIN
      // reference: https://www.npmjs.com/package/string-replace-webpack-plugin
      // Allow for arbitrary strings to be replaced as part of the module build process
      // Configure replacements for file patterns
      test: /index.html$/,
      use: stringReplacePlugin.replace({
        replacements: [{
          pattern: /WEB_PORT/ig,
          replacement: function (match, p1, offset, string) {
            return webPort;
          }
        }
      ]})
    }],
  };

  /**
   * Plugins
   * Reference: http://webpack.github.io/docs/configuration.html#plugins
   * List: http://webpack.github.io/docs/list-of-plugins.html
   */
  config.plugins = [
      // Reference: https://github.com/webpack-contrib/mini-css-extract-plugin
      new MiniCssExtractPlugin({
        filename: !isProd ? '[name].css' : '[name].[hash].css',
        chunkFilename: !isProd ? '[id].css' : '[id].[hash].css'
      })
  ];

  // Skip rendering index.html in test mode
  if (!isTest) {
    // Reference: https://github.com/ampedandwired/html-webpack-plugin
    // Render index.html
    config.plugins.push(
      new HtmlWebpackPlugin({
        template: './src/index.html',
        inject: 'body'
      }),
      // Reference: https://webpack.github.io/docs/list-of-plugins.html#defineplugin
      new webpack.DefinePlugin({
        'process.env': {
          HELIUM_BUNDLE_DEV: process.env.HELIUM_BUNDLE_DEV,
          SERVER_PORT: serverPort,
          WEB_PORT: webPort,
          PROD: isProd,
          BUILD_CI: (isCI) ? JSON.stringify(true) : JSON.stringify(false)
        }
      })
    )
  }

  if (isTest) {
    config.module.rules = [
      {
        // COVERAGE
        test: /\.js$/,
        exclude: /(node_modules|\.test\.js)/,
        loader: 'istanbul-instrumenter',
        enforce: 'post'
      }
    ]
  }

  // Add build specific plugins
  if (isProd) {
    config.optimization = {
      noEmitOnErrors: true,
      minimize: true
    }
    config.plugins.push(
      // Copy assets from the public folder
      // Reference: https://github.com/kevlened/copy-webpack-plugin
      new CopyWebpackPlugin([])
    )
  } else {
    config.plugins.push(
      new InsertLiveReloadPlugin(),
      // reference: https://www.npmjs.com/package/string-replace-webpack-plugin
      new stringReplacePlugin()
    )
  }

  /**
   * Dev server configuration
   * Reference : https://v4.webpack.js.org/configuration/dev-server/#devserver
   * Reference (Migration) : https://github.com/webpack/webpack-dev-server/blob/master/migration-v4.md
   */
  config.devServer = {
    client: {
      progress: true,
      overlay: {
        errors: true,
        runtimeErrors: true,
        warnings: false,
      }
    },
    historyApiFallback: true,
    port: webPort,
    hot: true,
    onBeforeSetupMiddleware: function(devServer) {
      devServer.app.use('**/node_modules/', express.static(path.resolve(__dirname, './node_modules/')));
      devServer.app.use('**/app/', express.static(path.resolve(__dirname, './src/app/')));
      devServer.app.use('**/assets/', express.static(path.resolve(__dirname, './src/assets/')));
      devServer.app.use('**/fonts/', express.static(path.resolve(__dirname, './src/fonts/')));
      devServer.app.use('**/components/', express.static(path.resolve(__dirname, './src/components/')));
    },
    devMiddleware: {
      stats: 'minimal', // were moved to devMiddleware
    },
    static: {
      directory: './src'
    }
  };

  return config;
}();
