"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _webpack = require("webpack");

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

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

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

/**
 * this plugin for the download i18n files
 * So, if need to downoad i18n in browser this plugin handle that
 *
 *
 * this plugin works
 * how it works?
 *  this plugin hook the requireEnsure method and we write logic for current compiled file download logic
 *
 * hinds:-
 *  requireEnsure method was loader for all js chunk's ,
 *  which means if we use dynamic import then this function will create script tag for that dynamic resource
 *  so every dynamic js chunk will load by this function,
 *  So we update this function with when  dynamic jschunk call the nour code also execute
 *
 * some thing  missing  in this definetion
 *
 */
// import { REGEXP_HASH, REGEXP_CHUNKHASH } from './utils/hashUtils';
const pluginName = 'DownloadLogicOfI18n'; // const pluginName = 'i18n-plugin';

const MODULE_TYPE = 'json/i18n';

const ltype = locale => `${MODULE_TYPE}/${locale}`;

function hashesWithLength(hashes, length) {
  const shortChunkHashMap = {};

  for (let key of Object.keys(hashes)) {
    if (typeof hashes[key] === 'string') {
      shortChunkHashMap[key] = hashes.slice(0, length);
    }
  }

  return shortChunkHashMap;
}

function getContentHashMaxLength(filenameTemplate) {
  let length = 0;
  let matches = filenameTemplate.match(_hashUtils.REGEXP_CONTENTHASH);

  for (let match of matches) {
    let tem = match.match(/\d+/);

    if (tem) {
      length = Math.max(parseInt(tem[0]), length);
    } else {
      length = 0;
      break;
    }
  }

  return length;
}

class I18nDownlodLogic {
  constructor({
    filenameTemplate,
    publicPath,
    localeVarName = 'document.documentElement.lang',
    locales
  }) {
    this.locales = locales;
    this.publicPath = publicPath;
    this.options = {
      filenameTemplate,
      localeVarName
    };
  }

  getShortContentHashes(chunk, hashLength) {
    let allContenHashes = {};
    const chunkMaps = chunk.getChunkMaps();
    this.locales.forEach(locale => {
      allContenHashes[locale] = chunkMaps.contentHash[ltype(locale)];
    });

    if (!length) {
      return allContenHashes;
    }

    let shortContentHashMap = {};

    for (const locale of this.locales) {
      shortContentHashMap[locale] = hashesWithLength(allContenHashes[locale], hashLength);
    }

    return shortContentHashMap;
  }

  getI18nChunkObject(mainChunk) {
    const obj = {};

    for (const chunk of mainChunk.getAllAsyncChunks()) {
      if (_I18nKeysIdentifer.default.isChunkHasI18n(chunk)) {
        obj[chunk.id] = 1;
      }
    }

    return obj;
  }

  addDownloadLogicOfI18nInMainTemplate(mainTemplate) {
    mainTemplate.hooks.localVars.tap(pluginName, (source, mainChunk) => {
      const chunkMap = this.getI18nChunkObject(mainChunk);

      if (!Object.keys(chunkMap).length) {
        return source;
      }

      return _webpack.Template.asString([source, '', '// object to store loaded I18N chunks', 'var installedI18nChunks = {', _webpack.Template.indent(mainChunk.ids.map(id => `${JSON.stringify(id)}: 0`).join(',\n')), '};']);
    });
    mainTemplate.hooks.requireEnsure.tap(pluginName, (source, mainChunk, hash) => {
      /**
       * for Information this is tapped in mainTemplate ,
       * So this hooks argument chunk is main chunk means entry chunk mostly.
       * if any chunk has i18n then we must write our download  i18n logic,
       */
      const chunkMap = this.getI18nChunkObject(mainChunk);

      if (!Object.keys(chunkMap).length) {
        return source;
      }
      /**
       *   chunkMaps has
       *  @property  {Object} hash  [it has key  as chunk id and value as chunkHash ]
       *  @property  {Object} name  [it has key  as chunk id and value as chunk name ]
       */


      const chunkMaps = mainChunk.getChunkMaps();
      const {
        crossOriginLoading
      } = mainTemplate.outputOptions;
      const {
        filenameTemplate,
        localeVarName = 'document.documentElement.lang'
      } = this.options;
      const i18nSrcPath = mainTemplate.getAssetPath(JSON.stringify(filenameTemplate).replace(/\[locale\]/gi, '"+ locale +"'), // "i18n-chunk/[locale]/[name].[chunkhash].js"
      {
        hash: `" + ${mainTemplate.renderCurrentHashCode(hash)} + "`,
        hashWithLength: length => `" + ${mainTemplate.renderCurrentHashCode(hash, length)} + "`,
        chunk: {
          id: '" + chunkId + "',
          hash: `" + ${JSON.stringify(chunkMaps.hash)}[chunkId] + "`,

          hashWithLength(length) {
            const shortChunkHashMap = hashesWithLength(chunkMaps.hash, length);
            return `" + ${JSON.stringify(shortChunkHashMap)}[chunkId] + "`;
          },

          // contentHash: contentHashMap,
          // contentHashWithLength: contentHashLengthMap,
          contentHash: {
            [MODULE_TYPE]: '" + contentHash + "'
          },
          contentHashWithLength: {
            [MODULE_TYPE]: length => `" + contentHash.slice(0,${length}) + "`
          },
          name: `" + (${JSON.stringify(chunkMaps.name)}[chunkId]||chunkId) + "`
        },
        contentHashType: MODULE_TYPE
      });
      /** IDEA:
       *      as my suggestion ignore this `jsop` approch,
       *      Because json parse is faster then js parse.
       *      my suggestion  is make ajax or fetch request
       */

      const buf = [];
      /*
        if (REGEXP_HASH.test(filenameTemplate)) {
          buf.push(
            `var hash = ${mainTemplate.renderCurrentHashCode(hash)};`
          );
        }
        if (REGEXP_CHUNKHASH.test(filenameTemplate)) {
          buf.push(`var chunkHashes = ${JSON.stringify(chunkMaps.hash)};`);
        }
        */

      if ((0, _hashUtils.hasContentHash)(filenameTemplate)) {
        const hashLength = getContentHashMaxLength(filenameTemplate);
        const contentHashes = this.getShortContentHashes(mainChunk, hashLength);
        buf.push(_webpack.Template.asString(['// all i18n files contentHash ', `var i18nContentHashes = ${JSON.stringify(contentHashes)};`, '', 'if (!i18nContentHashes[locale]) {', '  console.warn("this locale is "+locale + " not has support")', '  return;', '}', 'if (!i18nContentHashes[locale][chunkId]) {', '  console.warn("this chunkId is " + chunkId + " does not have \'"+locale+"\' locale chunk")', '  return;', '}']));
        buf.push('var contentHash = i18nContentHashes[locale][chunkId]');
      }

      const hashUtils = _webpack.Template.asString(buf);

      const publicPath = this.publicPath ? JSON.stringify(this.publicPath) : `${mainTemplate.requireFn}.p`;
      return _webpack.Template.asString([source, `
(function() {
  // ${pluginName} I18N loading
  var i18nChunks = ${JSON.stringify(chunkMap)};
  var locale = ${localeVarName};
  // ${pluginName} I18N hasehes
  ${hashUtils}
  if(installedI18nChunks[chunkId]) {
    promises.push(installedI18nChunks[chunkId]);
  } else if(installedI18nChunks[chunkId] !== 0 && i18nChunks[chunkId]) {
    promises.push(installedI18nChunks[chunkId] = new Promise(function(resolve, reject) {
      var srcPath = (${i18nSrcPath});
      ${'' // TODO: I think this is the area for download chunk of i18n we can
      //  change mainTemplate.requireFn as our cdn server urls !!!
      //  and we can change the i18n download logic here
      }
      var fullsrcPath = ${publicPath} + srcPath;
      var existingLinkTags = document.getElementsByTagName("script");
      for(var i = 0; i < existingLinkTags.length; i++) {
        var tag = existingLinkTags[i];
        var dataSrc = tag.getAttribute("data-src") || tag.getAttribute("src");
        if(dataSrc === srcPath || dataSrc === fullsrcPath){ return resolve();}
      }
      var scriptTag = document.createElement("script");
      scriptTag.onload = () =>  { 
        scriptTag.onerror = scriptTag.onload = null;
        resolve();
      };
      scriptTag.onerror = function(event) {
        scriptTag.onerror = scriptTag.onload = null;
        var request = event && event.target && event.target.src || fullsrcPath;
        var err = new Error("Loading I18N chunk " + chunkId + " failed.\\n(" + request + ")");
        err.code = "I18N_CHUNK_LOAD_FAILED";
        err.request = request;
        delete installedI18nChunks[chunkId];
        scriptTag.parentNode.removeChild(scriptTag);
        reject(err);
      };
      ${mainTemplate.requireFn}.nc && scriptTag.setAttribute("nonce", ${mainTemplate.requireFn}.nc);
      scriptTag.src = fullsrcPath;
      ${crossOriginLoading ? `if (scriptTag.src.indexOf(window.location.origin + '/') !== 0) {
          scriptTag.crossOrigin = ${JSON.stringify(crossOriginLoading)};
        }` : ''}
      document.body.appendChild(scriptTag);
    }).then(function() {
      installedI18nChunks[chunkId] = 0;
    }));
  }
})()`]);
    });
  }

  apply(compiler) {
    compiler.hooks.thisCompilation.tap(pluginName, compilation => {
      const {
        mainTemplate
      } = compilation;
      this.addDownloadLogicOfI18nInMainTemplate(mainTemplate);
    });
  }

}

exports.default = I18nDownlodLogic;