/**
 * Builds the `_30s` module in UMD and ESM formats.
 * Also builds the test module file for testing snippets.
 */
const fs = require('fs-extra');
const path = require('path');
const { green, red } = require('kleur');
const util = require('./util');
const { rollup } = require('rollup');
const babel = require('rollup-plugin-babel');
const minify = require('rollup-plugin-babel-minify');

const MODULE_NAME = '_30s';
const SNIPPETS_PATH = './snippets';
const SNIPPETS_ARCHIVE_PATH = './snippets_archive';
const DIST_PATH = './dist';
const ROLLUP_INPUT_FILE = './imports.temp.js';
const TEST_MODULE_FILE = './test/_30s.js';
const CODE_RE = /```\s*js([\s\S]*?)```/;

/**
 * Returns the raw markdown string.
 */
function getRawSnippetString(snippetPath, snippet) {
  return fs.readFileSync(path.join(snippetPath, snippet), 'utf8');
}

/**
 * Returns the JavaScript code from the raw markdown string.
 */
function getCode(rawSnippetString) {
  return rawSnippetString.match(CODE_RE)[1].replace('\n', '');
}

/**
 * Builds the UMD + ESM files to the ./dist directory.
 */
async function doRollup() {
  // Plugins
  const es5 = babel({ presets: ['@babel/preset-env'] });
  const min = minify({ comments: false });

  const output = format => file => ({
    format,
    file,
    name: MODULE_NAME
  });

  const umd = output('umd');
  const esm = output('es');

  const bundle = await rollup({ input: ROLLUP_INPUT_FILE });
  const bundleES5 = await rollup({ input: ROLLUP_INPUT_FILE, plugins: [es5] });
  const bundleES5Min = await rollup({
    input: ROLLUP_INPUT_FILE,
    plugins: [es5, min]
  });

  const baseName = `${DIST_PATH}/${MODULE_NAME}`;

  // UMD ES2018
  await bundle.write(umd(`${baseName}.js`));
  // ESM ES2018
  await bundle.write(esm(`${baseName}.esm.js`));
  // UMD ES5
  await bundleES5.write(umd(`${baseName}.es5.js`));
  // UMD ES5 min
  await bundleES5Min.write(umd(`${baseName}.es5.min.js`));
}

/**
 * Starts the build process.
 */
async function build() {
  console.time('Packager');

  let requires = [];
  let esmExportString = '';
  let cjsExportString = '';

  try {
    if (!fs.existsSync(DIST_PATH)) fs.mkdirSync(DIST_PATH);
    fs.writeFileSync(ROLLUP_INPUT_FILE, '');
    fs.writeFileSync(TEST_MODULE_FILE, '');

    // All the snippets that are Node.js-based and will break in a browser
    // environment
    const nodeSnippets = fs
      .readFileSync('tag_database', 'utf8')
      .split('\n')
      .filter(v => v.search(/:.*node/g) !== -1)
      .map(v => v.slice(0, v.indexOf(':')));

    const snippets = fs.readdirSync(SNIPPETS_PATH);
    const archivedSnippets = fs
      .readdirSync(SNIPPETS_ARCHIVE_PATH)
      .filter(v => v !== 'README.md');

    snippets.forEach(snippet => {
      const rawSnippetString = getRawSnippetString(SNIPPETS_PATH, snippet);
      const snippetName = snippet.replace('.md', '');
      let code = getCode(rawSnippetString);
      if (nodeSnippets.includes(snippetName)) {
        requires.push(code.match(/const.*=.*require\(([^\)]*)\);/g));
        code = code.replace(/const.*=.*require\(([^\)]*)\);/g, '');
      }
      esmExportString += `export ${code}`;
      cjsExportString += code;
    });
    archivedSnippets.forEach(snippet => {
      const rawSnippetString = getRawSnippetString(
        SNIPPETS_ARCHIVE_PATH,
        snippet
      );
      cjsExportString += getCode(rawSnippetString);
    });

    requires = [
      ...new Set(
        requires
          .filter(Boolean)
          .map(v =>
            v[0].replace(
              'require(',
              'typeof require !== "undefined" && require('
            )
          )
      )
    ].join('\n');

    fs.writeFileSync(ROLLUP_INPUT_FILE, `${requires}\n\n${esmExportString}`);

    const testExports = `module.exports = {${[...snippets, ...archivedSnippets]
      .map(v => v.replace('.md', ''))
      .join(',')}}`;

    fs.writeFileSync(
      TEST_MODULE_FILE,
      `${requires}\n\n${cjsExportString}\n\n${testExports}`
    );

    // Check Travis builds - Will skip builds on Travis if not CRON/API
    if (util.isTravisCI() && util.isNotTravisCronOrAPI()) {
      fs.unlink(ROLLUP_INPUT_FILE);
      console.log(
        `${green(
          'NOBUILD'
        )} Module build terminated, not a cron job or a custom build!`
      );
      console.timeEnd('Packager');
      process.exit(0);
    }

    await doRollup();

    // Clean up the temporary input file Rollup used for building the module
    fs.unlink(ROLLUP_INPUT_FILE);

    console.log(`${green('SUCCESS!')} Snippet module built!`);
    console.timeEnd('Packager');
  } catch (err) {
    console.log(`${red('ERROR!')} During module creation: ${err}`);
    process.exit(1);
  }
}

build();
