const pluginConfig = require('../pluginConfig.json');
const fs = require('fs');
const os = require('os');
const glob = require('glob');

const autogenerationDisclaimer = `
// THIS FILE IS AUTOGENERATED AS PART OF THE EXTENSION AND MODE PLUGIN PROCESS.
// IT SHOULD NOT BE MODIFIED MANUALLY \n`;

const extractName = val => (typeof val === 'string' ? val : val.packageName);

function constructLines(input, categoryName) {
  let pluginCount = 0;

  const lines = {
    importLines: [],
    addToWindowLines: [],
  };

  if (!input) return lines;

  input.forEach(entry => {
    if (entry.default === false) return;

    const packageName = extractName(entry);

    lines.addToWindowLines.push(`${categoryName}.push("${packageName}");\n`);

    pluginCount++;
  });

  return lines;
}

function getFormattedImportBlock(importLines) {
  let content = '';
  // Imports
  importLines.forEach(importLine => {
    content += importLine;
  });

  return content;
}

function getFormattedWindowBlock(addToWindowLines) {
  let content =
    'const extensions = [];\n' +
    'const modes = [];\n' +
    '\n// Not required any longer\n' +
    'window.extensions = extensions;\n' +
    'window.modes = modes;\n\n';

  addToWindowLines.forEach(addToWindowLine => {
    content += addToWindowLine;
  });

  return content;
}

function getRuntimeLoadModesExtensions(modules) {
  const dynamicLoad = [];
  dynamicLoad.push(
    '\n\n// Add a dynamic runtime loader',
    'async function loadModule(module) {',
    "  if (typeof module !== 'string') return module;"
  );
  modules.forEach(module => {
    const packageName = extractName(module);
    if (!packageName) {
      return;
    }
    if (module.importPath) {
      dynamicLoad.push(
        `  if( module==="${packageName}") {`,
        `    const imported = await window.browserImportFunction('${module.importPath}');`,
        '    return ' + (module.globalName ? `window["${module.globalName}"];` : `imported["${module.importName || 'default'}"];`),
        '  }'
      );
      return;
    }
    dynamicLoad.push(
      `  if( module==="${packageName}") {`,
      `    const imported = await import("${packageName}");`,
      '    return imported.default;',
      '  }'
    );
  });
  // TODO - handle more cases for import than just default
  dynamicLoad.push(
    '  return (await window.browserImportFunction(module)).default;',
    '}\n',
    '// Import a list of items (modules or string names)',
    '// @return a Promise evaluating to a list of modules',
    'export default function importItems(modules) {',
    '  return Promise.all(modules.map(loadModule));',
    '}\n',
    'export { loadModule, modes, extensions, importItems };\n\n'
  );
  return dynamicLoad.join('\n');
}

const fromDirectory = (srcDir, path) => {
  if (!path) return;
  if (path[0] === '.') return srcDir + '/../../..' + path.substring(1);
  if (path[0] === '~') return os.homedir() + path.substring(1);
  return path;
};

const createCopyPluginToDistForLink = (srcDir, distDir, plugins, folderName) => {
  return plugins
    .map(plugin => {
      const fromDir = fromDirectory(srcDir, plugin.directory);
      const from = fromDir || `${srcDir}/../node_modules/${plugin.packageName}/${folderName}/`;
      const exists = fs.existsSync(from);
      return exists
        ? {
            from,
            to: distDir,
            toType: 'dir',
          }
        : undefined;
    })
    .filter(x => !!x);
};

const createCopyPluginToDistForBuild = (SRC_DIR, DIST_DIR, plugins, folderName) => {
  return plugins
    .map(plugin => {
      const from = `${SRC_DIR}/../../../node_modules/${plugin.packageName}/${folderName}/`;
      const exists = fs.existsSync(from);
      return exists
        ? {
            from,
            to: DIST_DIR,
            toType: 'dir',
          }
        : undefined;
    })
    .filter(x => !!x);
};

function writePluginImportsFile(SRC_DIR, DIST_DIR) {
  let pluginImportsJsContent = autogenerationDisclaimer;

  const extensionLines = constructLines(pluginConfig.extensions, 'extensions');
  const modeLines = constructLines(pluginConfig.modes, 'modes');

  pluginImportsJsContent += getFormattedImportBlock([
    ...extensionLines.importLines,
    ...modeLines.importLines,
  ]);
  pluginImportsJsContent += getFormattedWindowBlock([
    ...extensionLines.addToWindowLines,
    ...modeLines.addToWindowLines,
  ]);

  pluginImportsJsContent += getRuntimeLoadModesExtensions([
    ...pluginConfig.extensions,
    ...pluginConfig.modes,
    ...pluginConfig.public,
  ]);

  fs.writeFileSync(`${SRC_DIR}/pluginImports.js`, pluginImportsJsContent, { flag: 'w+' }, err => {
    if (err) {
      console.error(err);
      return;
    }
  });

  // Build packages using cli add-mode and add-extension
  // will get added to the root node_modules, but the linked packages
  // will be hosted at the viewer node_modules.

  const copyPluginPublicToDistBuild = createCopyPluginToDistForBuild(
    SRC_DIR,
    DIST_DIR,
    [...pluginConfig.modes, ...pluginConfig.extensions],
    'public'
  );

  const copyPluginPublicToDistLink = createCopyPluginToDistForLink(
    SRC_DIR,
    DIST_DIR,
    [...pluginConfig.modes, ...pluginConfig.extensions, ...pluginConfig.public],
    'public'
  );

  // Temporary way to copy chunks from the dist folder so that the become
  // available
  const copyPluginDistToDistBuild = createCopyPluginToDistForBuild(
    SRC_DIR,
    DIST_DIR,
    [...pluginConfig.modes, ...pluginConfig.extensions],
    'dist'
  );

  const copyPluginDistToDistLink = createCopyPluginToDistForLink(
    SRC_DIR,
    DIST_DIR,
    [...pluginConfig.modes, ...pluginConfig.extensions],
    'dist'
  );

  console.warn('copy plugins', [
    ...copyPluginPublicToDistBuild,
    ...copyPluginPublicToDistLink,
    ...copyPluginDistToDistBuild,
    ...copyPluginDistToDistLink,
  ]);
  return [
    ...copyPluginPublicToDistBuild,
    ...copyPluginPublicToDistLink,
    ...copyPluginDistToDistBuild,
    ...copyPluginDistToDistLink,
  ];
}

module.exports = writePluginImportsFile;
