import { buildAction } from './manifest-helpers';
export const groupBy = (arr, attr, keyRenamer) => arr.reduce((acc, item) => {
  acc[keyRenamer(item[attr])] = item;
  return acc;
}, {});
export function buildMenuItem(manifest, extensionModule) {
  const title = extensionModule.title || manifest.title;
  const key = `${manifest.key}:${extensionModule.key}`;
  const node = buildAction(extensionModule.action, manifest);
  if (!node) {
    throw new Error(`Couldn't find any action for ${title} (${key})`);
  }
  return {
    key,
    title,
    extensionType: manifest.type,
    keywords: extensionModule.keywords || manifest.keywords || [],
    featured: extensionModule.featured || false,
    categories: extensionModule.categories || manifest.categories || [],
    description: extensionModule.description || manifest.description,
    summary: manifest.summary,
    documentationUrl: manifest.documentationUrl,
    icon: extensionModule.icon || manifest.icons['48'],
    node
  };
}
export const getQuickInsertItemsFromModule = (extensions, transformFunction) => {
  const items = extensions.map(manifest => {
    const modules = manifest.modules.quickInsert || [];
    return modules.map(extensionModule => buildMenuItem(manifest, extensionModule));
  });
  const flatItems = [].concat(...items);
  return flatItems.map(transformFunction);
};
export async function getAutoConvertPatternsFromModule(extensions) {
  const items = await Promise.all(extensions.map(async manifest => {
    if (manifest.modules.autoConvert && manifest.modules.autoConvert.url) {
      return manifest.modules.autoConvert.url;
    }
    return [];
  }));
  return [].concat(...items);
}
export const createAutoConverterRunner = autoConvertHandlers => text => {
  for (const handler of autoConvertHandlers) {
    const result = handler(text);
    if (result) {
      return result;
    }
  }
};
export async function getExtensionAutoConvertersFromProvider(extensionProviderPromise) {
  const extensionProvider = await extensionProviderPromise;
  const extensionAutoConverters = await extensionProvider.getAutoConverter();
  return createAutoConverterRunner(extensionAutoConverters);
}
const logError = (msg, ...args) => {
  // eslint-disable-next-line no-console
  console.error(msg, ...args);
};
const toolbarItemToButtonConfig = (toolbarItem, parentKey) => {
  const {
    tooltip,
    key,
    label,
    icon,
    action,
    disabled
  } = toolbarItem;
  const itemKey = [parentKey, key].join(':');
  if (typeof action !== 'function') {
    logError(`Provided action is not a function for extension toolbar button: ${label} (${itemKey})`);
  }
  let labelAndIcon = {};
  switch (toolbarItem.display) {
    case 'icon':
      if (!icon) {
        logError(`icon should be provided for extension toolbar button (${itemKey}), when display is set to 'icon'`);
      }
      labelAndIcon = {
        icon
      };
      break;
    case 'label':
      if (!label) {
        logError(`label should be provided for extension toolbar button (${itemKey}), when display is set to 'label'`);
      }
      labelAndIcon = {
        label
      };
      break;
    default:
      if (!label || !icon) {
        logError(`label and icon should be provided for extension toolbar button (${itemKey}), when display is not set or set to 'icon-and-label'`);
      }
      labelAndIcon = {
        icon,
        label
      };
      break;
  }
  return {
    key: itemKey,
    tooltip,
    action,
    disabled,
    ...labelAndIcon
  };
};
const compareContext = (contextA, contextB) => {
  if (contextA.type === contextB.type && contextA.nodeType === contextB.nodeType) {
    if (contextA.type === 'node') {
      return true;
    }
    if (contextA.type === 'extension' && contextB.type === 'extension') {
      return contextA.extensionKey === contextB.extensionKey && contextA.extensionType === contextB.extensionType;
    }
  }
  return false;
};
const hasDuplicateContext = contextualToolbars => {
  if (contextualToolbars.length > 1) {
    const contexts = contextualToolbars.map(contextualToolbar => contextualToolbar.context);
    return contexts.find((currentContext, currentIndex) => currentIndex !== contexts.findIndex(context => compareContext(currentContext, context)));
  }
};
export const getContextualToolbarItemsFromModule = (extensions, node, api) => {
  return extensions.map(extension => {
    if (extension.modules.contextualToolbars) {
      const duplicateContext = hasDuplicateContext(extension.modules.contextualToolbars);
      if (duplicateContext) {
        logError(`[contextualToolbars] Duplicate context detected - ${JSON.stringify(duplicateContext)}.`);
        return [];
      }
      return extension.modules.contextualToolbars.map(contextualToolbar => {
        if (shouldAddExtensionItemForNode(contextualToolbar, node)) {
          const {
            toolbarItems
          } = contextualToolbar;
          if (typeof toolbarItems === 'function') {
            return toolbarItems(node, api);
          }
          return toolbarItems;
        }
        return [];
      }).flatMap(toolbarButtons => toolbarButtons.map(toolbarButton => toolbarItemToButtonConfig(toolbarButton, extension.key)));
    }
    return [];
  }).flatMap(extensionToolbarButtons => extensionToolbarButtons);
};

// defines whether to add toolbar item for the given node
function shouldAddExtensionItemForNode(item, node) {
  var _node$attrs;
  // if item type is a standard node - should match the nodeType
  if (item.context.type === 'node' && item.context.nodeType === node.type) {
    return true;
  }
  // for other cases should be an extension and match the nodeType ("extension" | "inlineExtension" | "bodiedExtension" | "multiBodiedExtension")
  if (item.context.type !== 'extension' || node.type !== item.context.nodeType) {
    return false;
  }
  const {
    extensionType,
    extensionKey
  } = item.context;
  // if extension type is given - should match extension type
  if (extensionType && extensionType !== ((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionType)) {
    return false;
  }
  // if extension key is a string
  if (typeof extensionKey === 'string') {
    var _node$attrs2;
    return extensionKey === ((_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.extensionKey);
  }
  // if extension key is an array
  if (Array.isArray(extensionKey) && extensionKey.length) {
    var _node$attrs3;
    return extensionKey.includes((_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.extensionKey);
  }
  return false;
}