"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getExports = getExports;
exports.renderExports = renderExports;

var _loaderUtils = require("loader-utils");

function forError(item) {
  return typeof item === 'string' ? item : `\n${JSON.stringify(item, null, ' ')}\n`;
}

function splitCommand(command) {
  const result = command.split('|').map(item => item.split(' ')).reduce((acc, val) => acc.concat(val), []);

  for (const item of result) {
    if (!item) {
      throw new Error(`Invalid command "${item}" in "${command}" for exports. There must be only one separator: " ", or "|"`);
    }
  }

  return result;
}

function resolveExports(type, item) {
  let result;

  if (typeof item === 'string') {
    const noWhitespaceItem = item.trim();

    if (noWhitespaceItem.length === 0) {
      throw new Error(`Invalid "${item}" value for export`);
    }

    const splittedItem = splitCommand(noWhitespaceItem);

    if (splittedItem.length > 3) {
      throw new Error(`Invalid "${item}" value for export`);
    }

    if (splittedItem.length === 1) {
      result = {
        syntax: type === 'module' ? 'named' : 'multiple',
        name: splittedItem[0],
        // eslint-disable-next-line no-undefined
        alias: undefined
      };
    } else {
      result = {
        syntax: splittedItem[0],
        name: splittedItem[1],
        alias: // eslint-disable-next-line no-undefined
        typeof splittedItem[2] !== 'undefined' ? splittedItem[2] : undefined
      };
    }
  } else {
    result = {
      syntax: type === 'module' ? 'named' : 'multiple',
      ...item
    };
  }

  if (!['default', 'named', 'single', 'multiple'].includes(result.syntax)) {
    throw new Error(`Unknown "${result.syntax}" syntax export in "${forError(item)}" value`);
  }

  if (['default', 'single'].includes(result.syntax) && typeof result.alias !== 'undefined') {
    throw new Error(`The "${result.syntax}" syntax can't have "${result.alias}" alias in "${forError(item)}" value`);
  }

  if (type === 'commonjs') {
    if (result.syntax === 'default' || result.syntax === 'named') {
      throw new Error(`The "${type}" format can't be used with the "${result.syntax}" syntax export in "${forError(item)}" value`);
    }
  }

  if (type === 'module') {
    if (result.syntax === 'single' || result.syntax === 'multiple') {
      throw new Error(`The "${type}" format can't be used with the "${result.syntax}" syntax export in "${forError(item)}" value`);
    }
  }

  return result;
}

function getIdentifiers(array) {
  return array.reduce((accumulator, item) => {
    if (typeof item.alias !== 'undefined') {
      accumulator.push({
        type: 'alias',
        value: item.alias
      });
      return accumulator;
    }

    accumulator.push({
      type: 'name',
      value: item.name
    });
    return accumulator;
  }, []);
}

function getExports(type, exports) {
  let result;

  if (Array.isArray(exports)) {
    result = exports.map(item => resolveExports(type, item));
  } else {
    result = [resolveExports(type, exports)];
  }

  const hasMultipleDefault = result.filter(({
    syntax
  }) => syntax === 'default' || syntax === 'single');

  if (hasMultipleDefault.length > 1) {
    throw new Error(`The "${type}" format can't have multiple "${type === 'module' ? 'default' : 'single'}" exports in "\n${JSON.stringify(exports, null, ' ')}\n" value`);
  }

  const identifiers = getIdentifiers(result);
  const duplicates = duplicateBy(identifiers, 'value');

  if (duplicates.length > 0) {
    throw new Error(`Duplicate ${duplicates.map(identifier => `"${identifier.value}" (as "${identifier.type}")`).join(', ')} identifiers found in "\n${JSON.stringify(exports, null, ' ')}\n" value`);
  }

  return result;
}

function duplicateBy(array, key) {
  return array.filter((a, aIndex) => array.filter((b, bIndex) => b[key] === a[key] && aIndex !== bIndex).length > 0);
}

function renderExports(loaderContext, type, exports) {
  let code = '';
  const defaultExport = exports.filter(({
    syntax
  }) => syntax === 'default' || syntax === 'single');
  const namedExports = exports.filter(({
    syntax
  }) => syntax === 'named' || syntax === 'multiple');

  if (defaultExport.length > 0) {
    // eslint-disable-next-line default-case
    switch (type) {
      case 'commonjs':
        code += 'module.exports = ';
        break;

      case 'module':
        code += 'export default ';
        break;
    }

    const name = (0, _loaderUtils.interpolateName)(loaderContext, defaultExport[0].name, {});
    code += `${name};\n`;
  }

  if (namedExports.length > 0) {
    // eslint-disable-next-line default-case
    switch (type) {
      case 'commonjs':
        code += 'module.exports = {\n';
        break;

      case 'module':
        code += 'export {\n';
        break;
    }

    namedExports.forEach((namedExport, i) => {
      const needComma = i < namedExports.length - 1;
      const name = (0, _loaderUtils.interpolateName)(loaderContext, namedExport.name, {});
      const alias = namedExport.alias ? (0, _loaderUtils.interpolateName)(loaderContext, namedExport.alias, {}) : // eslint-disable-next-line no-undefined
      undefined;
      code += `  ${type === 'commonjs' ? alias ? `${JSON.stringify(alias)}: (${name})` : `${name}` : `${name}${alias ? ` as ${alias}` : ''}`}${needComma ? ',\n' : ''}`;
    });
    code += '\n};\n';
  }

  return code;
}