"use strict";

var _webpack = require("webpack");

var _htmlWebpackPlugin = _interopRequireDefault(require("html-webpack-plugin"));

var _constants = require("./constants");

var _I18nKeysIdentifer = _interopRequireDefault(require("./I18nKeysIdentifer"));

var _getI18nModule = require("./getI18nModule");

var _getI18nDependency = require("./getI18nDependency");

var _optionsHandler = require("./optionsHandler");

var _getI18nLoadingRuntimeModule = require("./getI18nLoadingRuntimeModule");

var _createHash = require("./createHash");

var _pathCreator = require("./pathCreator");

var _templateFileName = require("./templateFileName");

var _hashUtils = require("./utils/hashUtils");

var _propertiesUtils = require("./utils/propertiesUtils");

var _urlConcat = require("../../../../server/urlConcat");

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* eslint-disable no-restricted-syntax */

/* eslint-disable no-nested-ternary */
// import getI18nKeysFormModules from './utils/getI18nKeysFormModules';
const pluginName = 'mini-i18n-extract-plugin';
const pluginSymbol = Symbol(pluginName);

function getChunkModules(chunk, chunkGraph) {
  return typeof chunkGraph !== 'undefined' ? chunkGraph.getChunkModules(chunk) : chunk.modulesIterable;
}

class I18nExtractPlugin {
  constructor(options = {}) {
    // initialize options
    this.options = (0, _optionsHandler.optionsHandler)(options);
  }

  getI18nKeysForChunk(chunk, chunkGraph) {
    const {
      modulesI18nKeysMap
    } = this.i18nKeysIdentifer;
    const i18nKeysMap = {};

    for (const mod of getChunkModules(chunk, chunkGraph)) {
      const keys = modulesI18nKeysMap[mod.resource];

      if (keys) {
        for (const key of keys) {
          i18nKeysMap[key] = 1;
        }
      }
    }

    return Object.keys(i18nKeysMap);
  }

  getI18nContentForkeys(i18nKeys, locale) {
    const {
      allI18nObject
    } = this.options;
    const data = {};

    for (const key of i18nKeys) {
      data[key] = allI18nObject[locale][key];
    }

    return data;
  }

  getI18nChunkFileContent(i18nKeys, loc) {
    const {
      jsonpFunc
    } = this.options;
    const data = this.getI18nContentForkeys(i18nKeys, loc);
    return `${jsonpFunc}(${(0, _propertiesUtils.jsonToString)(data)})`;
  }

  filterI18nUsedChunks(chunks) {
    const {
      localeChunkAssestMap
    } = this;
    return Array.from(chunks).filter(c => localeChunkAssestMap[c.id]);
  }

  getContentHashes(locale) {
    const {
      localeChunkAssestMap
    } = this;
    const contentHashes = {};
    Object.keys(localeChunkAssestMap).forEach(chunkId => {
      if (localeChunkAssestMap[chunkId] && localeChunkAssestMap[chunkId][locale] && localeChunkAssestMap[chunkId][locale].contentHash) {
        contentHashes[chunkId] = localeChunkAssestMap[chunkId][locale].contentHash;
      } // console.log(localeChunkAssestMap[chunkId][locale]);

    }); // console.log({ contentHashes });

    return contentHashes;
  }

  apply(compiler) {
    const {
      webpack
    } = compiler;
    this.addToDefaultTypes(compiler);
    const I18nModule = (0, _getI18nModule.getI18nModule)(webpack);
    const I18nDependency = (0, _getI18nDependency.getI18nDependency)(webpack); // initialize base data's and plugis

    this.i18nKeysIdentifer = new _I18nKeysIdentifer.default(this.options, {
      I18nModule,
      I18nDependency
    });
    this.i18nKeysIdentifer.apply(compiler);
    compiler.hooks.thisCompilation.tap(pluginName, compilation => {
      this.localeChunkAssestMap = {};
      compilation.hooks.beforeHash.tap(pluginName, () => {
        const {
          locales
        } = this.options;
        const {
          chunks
        } = compilation;
        this.localeChunkAssestMap = {};
        const {
          localeChunkAssestMap
        } = this;

        for (const chunk of chunks) {
          const {
            chunkGraph
          } = compilation;
          const i18nKeys = this.getI18nKeysForChunk(chunk, chunkGraph);

          if (i18nKeys.length) {
            localeChunkAssestMap[chunk.id] = {};

            for (const loc of locales) {
              const content = this.getI18nChunkFileContent(i18nKeys, loc);
              localeChunkAssestMap[chunk.id][loc] = {
                content
              };
            }
          }
        }
      });
      compilation.hooks.chunkHash.tap(pluginName, (chunk, hash) => {
        const {
          localeChunkAssestMap
        } = this;
        const {
          locales
        } = this.options;

        if (localeChunkAssestMap[chunk.id]) {
          for (const loc of locales) {
            const {
              content
            } = localeChunkAssestMap[chunk.id][loc];
            hash.update(content);
          }
        }
      });

      function createFilenameWithHash({
        fileNameTemplate,
        chunk,
        locale,
        content
      }) {
        const fileName = fileNameTemplate.replaceAll('[locale]', locale);
        const contentHash = (0, _createHash.createHash)({
          compiler,
          outputOptions: compilation.outputOptions,
          content
        });
        const filename = (0, _pathCreator.pathCreator)(fileName, compilation, {
          hash: compilation.hash,
          chunkId: chunk.id,
          chunkHash: chunk.hash,
          contentHash
        });
        return {
          contentHash,
          filename
        };
      }

      compilation.hooks.contentHash.tap(pluginName, chunk => {
        const {
          RawSource
        } = webpack.sources;
        const {
          locales,
          chunkFilename,
          filename
        } = this.options;
        const fileNameTemplate = chunk.canBeInitial() ? filename : chunkFilename; // const { chunks } = compilation;

        const {
          localeChunkAssestMap
        } = this; // function filterI18nUsedChunks(chunks) {
        //   return Array.from(chunks).filter(c => localeChunkAssestMap[c.id]);
        // }
        // for (const chunk of chunks) {

        if (localeChunkAssestMap[chunk.id]) {
          for (const locale of locales) {
            const localObject = localeChunkAssestMap[chunk.id][locale];
            const {
              content
            } = localObject;
            const {
              contentHash,
              filename
            } = createFilenameWithHash({
              fileNameTemplate,
              chunk,
              locale,
              content
            });
            localObject.contentHash = contentHash;
            localObject.filename = filename;
            compilation.emitAsset(filename, new RawSource(content));
          }
        }
      });

      if (!this.options.runtime) {
        return;
      }

      this.createI18nDownloadCodeInRuntimeFile(webpack, compilation, compiler); // ## compilation.hooks.runtimeRequirementInTree
      // ##   .for(RuntimeGlobals.hmrDownloadUpdateHandlers)
      // ##   .tap(pluginName, handler);

      _htmlWebpackPlugin.default.getHooks(compilation).beforeAssetTagGeneration.tap(pluginName, hookOptions => {
        const {
          assets
        } = hookOptions;
        const {
          templateLabel,
          filename,
          publicPath = ''
        } = this.options;
        const dummyContent = '// dummy comment';
        const initialChunks = compilation.chunks.filter(c => c.canBeInitial() && c.id !== c.runtime);
        const initialI18nAssets = initialChunks.map(c => {
          const data = createFilenameWithHash({
            fileNameTemplate: filename,
            chunk: c,
            locale: templateLabel,
            content: dummyContent
          });
          return (0, _urlConcat.urlConcat)(publicPath, data.filename);
        });
        const allJSAssets = [...initialI18nAssets, ...assets.js].map(url => url.replaceAll('[locale]', templateLabel).replaceAll('%5Blocale%5D', templateLabel));
        console.log('HtmlWebpackPluginallJSAssets', {
          assets,
          filename,
          allJSAssets
        });
        return {
          assets: { ...assets,
            js: allJSAssets
          }
        };
      });
    });
  }

  createI18nDownloadCodeInRuntimeFile(webpack, compilation, compiler) {
    const {
      RuntimeGlobals
    } = webpack;
    const {
      RawSource
    } = webpack.sources;
    const {
      chunkFilename,
      locales
    } = this.options;
    const chunkFilenameHasContentHash = (0, _hashUtils.hasContentHash)(chunkFilename);

    const getI18nChunks = mainChunk => this.filterI18nUsedChunks(mainChunk.getAllAsyncChunks());

    const I18nLoadingRuntimeModule = (0, _getI18nLoadingRuntimeModule.getI18nLoadingRuntimeModule)({
      getI18nChunks,
      webpack
    });
    const enabledChunks = new WeakSet();

    const chunkFilenameUrlgenerator = (i18nChunks, locale) => (0, _templateFileName.templateFileName)({
      compiler,
      compilation,
      locale,
      dynamicFilename: chunkFilename,
      contentHashes: this.getContentHashes(locale),
      chunks: i18nChunks
    });

    const prefixFunc = () => _constants.I18N_CHUNK_NAME_GENERATION_SEGMENT;

    const handler = (entryRuntimeChunk, set) => {
      if (enabledChunks.has(entryRuntimeChunk)) {
        return;
      }

      enabledChunks.add(entryRuntimeChunk);
      const {
        chunkFilename,
        // filename: initialFilename,
        runtimeOptions
      } = this.options;

      if (typeof chunkFilename === 'string' && /\[(full)?hash(:\d+)?\]/.test(chunkFilename)) {
        set.add(RuntimeGlobals.getFullHash);
      }

      set.add(RuntimeGlobals.publicPath);
      compilation.addRuntimeModule(entryRuntimeChunk, new I18nLoadingRuntimeModule(set, {
        prefixFunc: chunkFilenameHasContentHash ? prefixFunc : chunkFilenameUrlgenerator,
        compiler,
        runtimeOptions,
        chunkFilename
      }));
    };

    compilation.hooks.runtimeRequirementInTree.for(RuntimeGlobals.ensureChunkHandlers).tap(pluginName, handler);

    if (chunkFilenameHasContentHash) {
      compilation.hooks.processAssets.tap({
        name: pluginName,
        stage: _webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED // additionalAssets: true

      }, assets => {
        // eslint-disable-next-line no-underscore-dangle
        const entries = compilation._getChunkGraphEntries();

        function createRuntimeFilePerLocale({
          locale,
          runtimeFileName,
          runtimeFileSourceStr,
          i18nChunks
        }) {
          const newRuntimeName = runtimeFileName.replaceAll('[locale]', locale);
          const replaceText = chunkFilenameUrlgenerator(i18nChunks, locale);
          const replacedStr = runtimeFileSourceStr.replace(_constants.I18N_CHUNK_NAME_GENERATION_SEGMENT, replaceText);
          compilation.emitAsset(newRuntimeName, new RawSource(replacedStr));
        }

        for (const entryChunk of entries) {
          const i18nChunks = getI18nChunks(entryChunk);
          const runtimeFileName = entryChunk.files[0];
          const runtimeFileSourceStr = assets[runtimeFileName].source();
          locales.forEach(locale => createRuntimeFilePerLocale({
            locale,
            entryChunk,
            runtimeFileName,
            runtimeFileSourceStr,
            i18nChunks
          })); // NOTE: we don't delete, Because of HTML plugin needs to add runtime file in html
          // compilation.deleteAsset(runtimeFileName);
        }
      });
    }
  } // eslint-disable-next-line class-methods-use-this


  addToDefaultTypes(compiler) {
    const {
      splitChunks
    } = compiler.options.optimization;

    if (splitChunks) {
      if (splitChunks.defaultSizeTypes.includes('...')) {
        splitChunks.defaultSizeTypes.push(_constants.MODULE_TYPE);
      }
    }
  }

}

I18nExtractPlugin.pluginName = pluginName;
I18nExtractPlugin.pluginSymbol = pluginSymbol; // I18nExtractPlugin.loader = require.resolve('./loader');

module.exports = I18nExtractPlugin;
/**
 * TODO:
 * move `localeChunkAssestMap` as separate module or file or class
 */