const path = require('path')
const fs = require('fs')
const webpack = require('webpack')
const WebpackBuildNotifierPlugin = require('webpack-build-notifier')
const _ = require('lodash')
const yaml = require('js-yaml')

const DEV = process.env.NODE_ENV !== 'production'
const ConfigBuilder = require(path.resolve(
  __dirname,
  'fe',
  'lib',
  'webpack',
  'config_builder'
))
const EntriesGenerationWebpackPlugin = require('./fe/lib/webpack/entries-generation-webpack-plugin')

const ParentWindowPlugin = require(path.resolve(
  __dirname,
  'fe',
  'lib',
  'webpack',
  'ParentWindowPlugin'
))

const ManifestPlugin = require('webpack-manifest-plugin')
const HappyPack = require('happypack')
const SuppressExtractedTextChunksWebpackPlugin = require('./fe/lib/webpack/suppress-entry-chunks-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')

const extractLESS = new ExtractTextPlugin({
  filename: DEV ? '[name].bundle.css' : '[name].[contenthash:20].bundle.css',
  disable: process.env.MODE !== 'v4-style',
})

const { PACKMASTER, MODE } = process.env
const { ENABLE_WEBPACK_NOTIFIER } = process.env

/**
 * @typedef MacroFlags
 * @type {object}
 * @property {boolean} __IN_EDITOR__ - in editor or in site
 * @property {boolean} __NATIVE_WEB__ - webiew editor in native app
 * @property {boolean} DEBUGUI - turn on the mode to see all rendering path, deprecated
 * @property {boolean} __SERVER_RENDERING__ - is at server rendering
 * @property {'sxl' | 'strikingly'} __PRODUCT_NAME__ - product name
 * @property {boolean} __EXTERNAL_EDITOR__ - when component is being clicked on, instead of editing
 * the component in place, send the component states out to an external editor
 *
 * @type {MacroFlags}
 */
const defaultVars = {
  __IN_EDITOR__: false,
  __NATIVE_WEB__: false, // new mobile app, test with ?native_web_editor=1
  __NATIVE_IPAD__: false, // new mobile app on ipad
  __IFRAME_EDITOR__: false, // mobile view in web editor
  DEBUGUI: false,
  'process.env': {
    NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development'),
    VERBOSE: JSON.stringify(process.env.VERBOSE),
  },
  __SERVER_RENDERING__: false,
  __PRODUCT_NAME__: JSON.stringify(process.env.PRODUCT_NAME || 'strikingly'),
  __LOCALE__: 'en',
  __EXTERNAL_EDITOR__: false,
  __MODE__: JSON.stringify(MODE || ''),
}

const assetHost = process.env.ASSET_HOST || ''
const hotReload = process.env.REACT_HOT === '1'
const outputPath = path.join(__dirname, 'public', 'webpack')
const V4StylesheetsOutputPath = path.join(__dirname, 'public')
const hostName =
  assetHost ||
  (defaultVars.__PRODUCT_NAME__ === '"strikingly"'
    ? 'https://assets.strikingly.io:1443/assets/v4/'
    : 'https://assets.shangxianle.me:1443/assets/v4/')

// function getPoFilePath() {
//   return path.resolve(
//     __dirname,
//     'fe',
//     'locales',
//     defaultVars.__LOCALE__,
//     `${JSON.parse(defaultVars.__PRODUCT_NAME__)}.po`
//   )
// }

function getOutputPath(name) {
  return {
    path: outputPath,
    filename: DEV
      ? `[name]-${name}-bundle.js`
      : `[name]-${name}-bundle.[hash].js`,
    publicPath: DEV ? hostName : `${hostName}/webpack/`,
    chunkFilename: DEV
      ? `[id]-${name}-bundle.js`
      : `[id].[hash]-${name}-bundle.js`,
    devtoolModuleFilenameTemplate: DEV
      ? '/[absolute-resource-path]'
      : undefined,
  }
}

const withLocales = function(locales, configFn) {
  const configs = configFn('en')
  return configs
}

const DllReferences = {
  app: new webpack.DllReferencePlugin({
    context: path.resolve(__dirname, 'fe'),
    manifest: require('./fe/js/vendor/dll/app-manifest.json'),
  }),
  site: new webpack.DllReferencePlugin({
    context: path.resolve(__dirname, 'fe'),
    manifest: require('./fe/js/vendor/dll/site-manifest.json'),
  }),
}

// const locales =
//   defaultVars.__PRODUCT_NAME__ === '"sxl"'
//     ? ['sxl', 'zh_CN']
//     : ['en', 'es', 'fr', 'ja', 'zh_CN', 'zh_TW']

const EXPOSE_TO_IFRAME = /(actions\/)|(stores\/)|(dispatcher\/)/
const themesPath = path.join(__dirname, 'fe/styles/themes')

const packMasterData = yaml.safeLoad(
  fs.readFileSync(path.join(__dirname, './pack-master.config.yaml'), 'utf8')
)
const masterPackConfigs =
  DEV && PACKMASTER
    ? require(path.join(__dirname, './pack-master.history.json'))
    : packMasterData.default

const styleEntries = masterPackConfigs['v4-style'] || {}
const projectNames = masterPackConfigs.projects || ['editor']
const packMasterRange = Object.keys(packMasterData.default)

function addThemeToStyleEntry(includeShowPage, includeEditor) {
  // Add the theme entry files to collection in editor project
  fs.readdirSync(themesPath).forEach(fileName => {
    const filePath = path.join(themesPath, fileName)
    const stat = fs.statSync(filePath)
    // Search each directory
    if (stat && stat.isDirectory()) {
      if (includeShowPage) {
        styleEntries[`themes/${fileName}/main_v4`] = path.join(
          filePath,
          'main_v4.less'
        ) // For show page
      }
      // TODO: cannot ignore the condition that below files are non-existent
      if (includeEditor) {
        styleEntries[`themes/${fileName}/main_v4_editor`] = path.join(
          filePath,
          'main_v4_editor.less'
        ) // For editor page
      }
    }
  })
}

addThemeToStyleEntry(
  !DEV || projectNames.includes('show-page'),
  !DEV || projectNames.includes('editor')
)

const webpackConfig = withLocales(/* locales */ ['en'], locale => {
  defaultVars.__LOCALE__ = locale
  return [
    {
      name: 'v4-style',
      entry: styleEntries,
      output: {
        // In production environment, output stylesheet bundle files to public folder.
        // In development environment, outputPath is as same as 'app' config due to
        // devServer just receives the identical config path in multiple configs mode.
        path: !DEV ? V4StylesheetsOutputPath : outputPath,
        filename: '[name]-app-bundle.js',
        chunkFilename: DEV ? '[id]-app-bundle.js' : '[id].[hash]-app-bundle.js',
      },
      plugins: [
        extractLESS,
        new SuppressExtractedTextChunksWebpackPlugin(),
        new ManifestPlugin({
          // `.sprickets-manifest` prefix is to avoid to be removed by bash scripts in production environment
          fileName: '.sprockets-manifest-v4-manifest.json',
          publicPath: !DEV ? `${assetHost}/` : '/assets/v4/',
          writeToFileEmit: true,
        }),
        new HappyPack({
          id: 'less',
          threads: 4,
          loaders: [
            'css-loader?importLoaders=1',
            'postcss-loader',
            'less-loader',
          ],
        }),
      ],
    },
    {
      name: 'editor-debug',
      entry: {
        editor: './fe/js/editor.es6',
      },
      output: getOutputPath('app', hotReload),
      plugins: [
        DllReferences.app,
        new webpack.DefinePlugin(
          _.assign({}, defaultVars, {
            __IN_EDITOR__: true,
          })
        ),
      ],
      strikingly: {
        hotReload,
      },
    },
    {
      name: 'app',
      entry: masterPackConfigs.app,
      output: getOutputPath('app', hotReload),
      plugins: [
        new ParentWindowPlugin(EXPOSE_TO_IFRAME, {
          parent: true,
        }),
        DllReferences.app,
        new webpack.DefinePlugin(
          _.assign({}, defaultVars, {
            __IN_EDITOR__: true,
            __LOCALE__: locale,
          })
        ),
      ],
      strikingly: {
        hotReload,
      },
    },
    {
      name: 'miniprogram',
      entry: masterPackConfigs.miniprogram,
      output: getOutputPath('miniprogram', hotReload),
      plugins: [
        new ParentWindowPlugin(EXPOSE_TO_IFRAME, {
          parent: true,
        }),
        DllReferences.app,
        new webpack.DefinePlugin(
          _.assign({}, defaultVars, {
            __IN_EDITOR__: true,
            __LOCALE__: locale,
          })
        ),
      ],
      strikingly: {
        hotReload,
      },
    },
    {
      name: 'iframe-editor',
      entry: masterPackConfigs['iframe-editor'],
      output: getOutputPath('iframe-editor', hotReload),
      plugins: [
        new ParentWindowPlugin(EXPOSE_TO_IFRAME, {}),
        DllReferences.app,
        new webpack.DefinePlugin(
          _.assign({}, defaultVars, {
            __IN_EDITOR__: true,
            __NATIVE_WEB__: false,
            __IFRAME_EDITOR__: true,
            __EXTERNAL_EDITOR__: true,
          })
        ),
      ],
      strikingly: {
        hotReload,
      },
    },
    {
      name: 'native-web',
      entry: masterPackConfigs['native-web'],
      output: getOutputPath('native-web', hotReload),
      plugins: [
        DllReferences.app,
        new webpack.DefinePlugin(
          _.assign({}, defaultVars, {
            __IN_EDITOR__: true,
            __NATIVE_WEB__: true,
            __EXTERNAL_EDITOR__: true,
          })
        ),
      ],
      strikingly: {
        hotReload,
      },
    },
    {
      name: 'native-ipad',
      entry: masterPackConfigs['native-ipad'],
      output: getOutputPath('native-ipad', hotReload),
      plugins: [
        DllReferences.app,
        new webpack.DefinePlugin(
          _.assign({}, defaultVars, {
            __IN_EDITOR__: true,
            __NATIVE_WEB__: true,
            __NATIVE_IPAD__: true,
            __EXTERNAL_EDITOR__: true,
          })
        ),
      ],
      strikingly: {
        hotReload,
      },
    },
    {
      name: 'site',
      entry: masterPackConfigs.site,
      output: getOutputPath('site', hotReload),
      plugins: [
        DllReferences.site,
        new webpack.DefinePlugin(_.assign({}, defaultVars, {})),
        //        new I18nPlugin(getPoFilePath())
      ],
      strikingly: {
        hotReload,
      },
    },
    {
      name: 'prerender',
      target: 'node',
      entry: {
        index: './fe/js/prerender/index.es6',
      },
      externals: {
        // load these things from r-jaugar global
        routes: 'Routes',
        $S: '$S',
        analytics: 'analytics',
        gaq: '_gaq',
        Keen: 'Keen',
        bugsnag: 'Bugsnag',
        pingpp: 'pingpp',
        BackEndI18n: 'I18n',
        lodash: 'lodash',
        React: 'react',
        jquery: 'jquery',
        ReactDOM: 'react-dom',
      },
      output: {
        path: path.join(__dirname, 'app', 'assets', 'javascripts', 'v4'),
        filename: '[name]-prerender-bundle.js',
        publicPath: assetHost ? `${assetHost}/webpack/` : hostName,
        chunkFilename: DEV
          ? '[id]-prerender-bundle.js'
          : '[id].[hash]-prerender-bundle.js',
        devtoolModuleFilenameTemplate: DEV
          ? '/[absolute-resource-path]'
          : undefined,
        libraryTarget: 'commonjs2',
      },
      plugins: [
        new webpack.DefinePlugin(
          Object.assign({}, defaultVars, {
            __SERVER_RENDERING__: true,
          })
        ),
        new webpack.optimize.LimitChunkCountPlugin({
          maxChunks: 1,
        }),
      ],
    },
    {
      name: 'landing',
      entry: '',
    },
    {
      name: 'component',
      entry: masterPackConfigs.component,
      output: {
        filename: DEV
          ? `[name]-component-bundle.js`
          : `[name]-component-bundle.[hash].js`,
        path: outputPath,
        publicPath: DEV ? hostName : `${hostName}/webpack/`,
        libraryTarget: 'umd',
        library: ['ReactComponent', '[name]'],
      },
      plugins: [
        new ParentWindowPlugin(EXPOSE_TO_IFRAME, {
          parent: true,
        }),
        DllReferences.app,
        new webpack.DefinePlugin(
          _.assign({}, defaultVars, {
            __IN_EDITOR__: true,
            __LOCALE__: locale,
          })
        ),
      ],
      strikingly: {
        hotReload,
      },
    },
  ]
})

const chosenConfigs = process.env.CONFIGS || 'app,site,prerender'

// When current config reaches one of below conditions, keep it.
// 1. masterPackConfigs object includes it
// 2. environment is production
// 3. packMasterRange array doesn't include it. That means, this config is not in pack-master business range.
const h = _.reduce(
  chosenConfigs.split(','),
  (p, confName) => {
    const conf = _.select(
      webpackConfig,
      c =>
        // disable locale
        //    return c.name.indexOf("-" + confName) !== -1
        c.name.indexOf(confName) !== -1 &&
        (masterPackConfigs[c.name] ||
          !DEV ||
          packMasterRange.indexOf(c.name) === -1)
    )
    return p.concat(conf)
  },
  []
)

const configs = new ConfigBuilder(h).getConfig()

configs.forEach(config => {
  // Apply the entriesGenerationWebpackPlugin for all javascript configs
  if (config.name !== 'v4-style') {
    config.plugins = config.plugins.concat(
      new EntriesGenerationWebpackPlugin({
        fileName: '.sprockets-manifest-webpack-js-manifest.json',
        setEntryName: prevName => `${prevName}-${config.name}-bundle`,
        publicPath: !DEV ? `${assetHost}/webpack/` : '/assets/v4/',
      })
    )
  }
  // enable a build notification in non-prod env
  if (DEV && ENABLE_WEBPACK_NOTIFIER) {
    config.plugins = config.plugins.concat(
      new WebpackBuildNotifierPlugin({
        title: `Bobcat: ${config.name}`,
      })
    )
  }
})

// hack to change externals in prerender to alias without fiddling too much into config_builder
// const conf = _.find(configs, c => c.name.indexOf('lambda') !== -1)

// if (conf) {
//   _.map(conf.externals, function(v, k) {
//     conf.resolve.alias[k] = __dirname + '/fe/js/prerender/externals/empty.es6'
//   })
//   conf.externals = []
// }

module.exports = configs
