const createError = require('./createError');

/** @typedef {string | string[] | import('webpack').Entry} StaticEntry */
/** @typedef {StaticEntry | import('webpack').EntryFunc} WebpackEntry */

/**
 * Checks if a Webpack entry string is related to socket integrations.
 * @param {string} entry A Webpack entry string.
 * @returns {boolean} Whether the entry is related to socket integrations.
 */
function isSocketEntry(entry) {
  /**
   * Webpack entries related to socket integrations.
   * They have to run before any code that sets up the error overlay.
   * @type {string[]}
   */
  const socketEntries = [
    'webpack-dev-server/client',
    'webpack-hot-middleware/client',
    'webpack-plugin-serve/client',
    'react-dev-utils/webpackHotDevClient',
  ];

  return socketEntries.some((socketEntry) => entry.includes(socketEntry));
}

/**
 * Injects an entry to the bundle for react-refresh.
 * @param {WebpackEntry} [originalEntry] A Webpack entry object.
 * @param {import('./getAdditionalEntries').AdditionalEntries} additionalEntries An object that contains the Webpack entries for prepending and the overlay feature
 * @returns {WebpackEntry} An injected entry object.
 */
function injectRefreshEntry(originalEntry, additionalEntries) {
  const { prependEntries = [], overlayEntries = [] } = additionalEntries;

  // Single string entry point
  if (typeof originalEntry === 'string') {
    if (isSocketEntry(originalEntry)) {
      return [...prependEntries, originalEntry, ...overlayEntries];
    }

    return [...prependEntries, ...overlayEntries, originalEntry];
  }
  // Single array entry point
  if (Array.isArray(originalEntry)) {
    const socketEntryIndex = originalEntry.findIndex(isSocketEntry);

    let socketAndPrecedingEntries = [];
    if (socketEntryIndex !== -1) {
      socketAndPrecedingEntries = originalEntry.splice(0, socketEntryIndex + 1);
    }

    return [...prependEntries, ...socketAndPrecedingEntries, ...overlayEntries, ...originalEntry];
  }
  // Multiple entry points
  if (typeof originalEntry === 'object') {
    return Object.entries(originalEntry).reduce(
      (acc, [curKey, curEntry]) => ({
        ...acc,
        [curKey]:
          typeof curEntry === 'object' && curEntry.import
            ? {
                ...curEntry,
                import: injectRefreshEntry(curEntry.import, additionalEntries),
              }
            : injectRefreshEntry(curEntry, additionalEntries),
      }),
      {}
    );
  }
  // Dynamic entry points
  if (typeof originalEntry === 'function') {
    return (...args) =>
      Promise.resolve(originalEntry(...args)).then((resolvedEntry) =>
        injectRefreshEntry(resolvedEntry, additionalEntries)
      );
  }

  throw createError('Failed to parse the Webpack `entry` object!');
}

module.exports = injectRefreshEntry;
