const fs = require('fs-extra'),
  path = require('path'),
  chalk = require('chalk'),
  crypto = require('crypto');

const getMarkDownAnchor = paragraphTitle =>
  paragraphTitle
    .trim()
    .toLowerCase()
    .replace(/[^\w\- ]+/g, '')
    .replace(/\s/g, '-')
    .replace(/\-+$/, '');

const getFilesInDir = (directoryPath, withPath, exclude = null) => {
  try {
    let directoryFilenames = fs.readdirSync(directoryPath);
    directoryFilenames.sort((a, b) => {
      a = a.toLowerCase();
      b = b.toLowerCase();
      if (a < b) return -1;
      if (a > b) return 1;
      return 0;
    });

    if (withPath) {
      // a hacky way to do conditional array.map
      return directoryFilenames.reduce((fileNames, fileName) => {
        if (exclude == null || !exclude.some(toExclude => fileName === toExclude))
          fileNames.push(`${directoryPath}/${fileName}`);
        return fileNames;
      }, []);
    }
    return directoryFilenames;
  } catch (err) {
    console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
    process.exit(1);
  }
};

// Synchronously read all snippets and sort them as necessary (case-insensitive)
const readSnippets = snippetsPath => {
  const snippetFilenames = getFilesInDir(snippetsPath, false);

  let snippets = {};
  try {
    for (let snippet of snippetFilenames)
      snippets[snippet] = fs.readFileSync(path.join(snippetsPath, snippet), 'utf8');
  } catch (err) {
    console.log(`${chalk.red('ERROR!')} During snippet loading: ${err}`);
    process.exit(1);
  }
  return snippets;
};
// Creates an object from pairs
const objectFromPairs = arr => arr.reduce((a, v) => ((a[v[0]] = v[1]), a), {});
// Load tag data from the database
const readTags = () => {
  let tagDbData = {};
  try {
    tagDbData = objectFromPairs(
      fs
        .readFileSync('tag_database', 'utf8')
        .split('\n')
        .filter(v => v.trim() !== '')
        .map(v => {
          let data = v.split(':').slice(0, 2);
          data[1] = data[1].split(',').map(t => t.trim());
          return data;
        })
    );
  } catch (err) {
    // Handle errors (hopefully not!)
    console.log(`${chalk.red('ERROR!')} During tag database loading: ${err}`);
    process.exit(1);
  }
  return tagDbData;
};
// Optimizes nodes in an HTML document
const optimizeNodes = (data, regexp, replacer) => {
  let count = 0;
  let output = data;
  do {
    output = output.replace(regexp, replacer);
    count = 0;
    while (regexp.exec(output) !== null) {
      ++count;
    }
  } while (count > 0);
  return output;
};
// Randomizes the order of the values of an array, returning a new array.
const shuffle = ([...arr]) => {
  let m = arr.length;
  while (m) {
    const i = Math.floor(Math.random() * m--);
    [arr[m], arr[i]] = [arr[i], arr[m]];
  }
  return arr;
};
// Capitalizes the first letter of a string
const capitalize = (str, lowerRest = false) =>
  str.slice(0, 1).toUpperCase() + (lowerRest ? str.slice(1).toLowerCase() : str.slice(1));
// Checks if current environment is Travis CI
const isTravisCI = () => 'TRAVIS' in process.env && 'CI' in process.env;
const isNotTravisCronOrAPI = () =>
  process.env['TRAVIS_EVENT_TYPE'] !== 'cron' && process.env['TRAVIS_EVENT_TYPE'] !== 'api';
// Creates a hash for a value using the SHA-256 algorithm.
const hashData = val =>
  crypto
    .createHash('sha256')
    .update(val)
    .digest('hex');
// Gets the code blocks for a snippet file.
const getCodeBlocks = str => {
  const regex = /```[.\S\s]*?```/g;
  const results = [];
  let m = null;
  while ((m = regex.exec(str)) !== null) {
    if (m.index === regex.lastIndex) {
      regex.lastIndex += 1;
    }
    m.forEach((match, groupIndex) => {
      results.push(match);
    });
  }
  return results;
};
// Gets the textual content for a snippet file.
const getTextualContent = str => {
  const regex = /###.*\n*([\s\S]*?)```/g;
  const results = [];
  let m = null;
  while ((m = regex.exec(str)) !== null) {
    if (m.index === regex.lastIndex) {
      regex.lastIndex += 1;
    }
    m.forEach((match, groupIndex) => {
      results.push(match);
    });
  }
  return results[1];
};
const prepTaggedData = tagDbData =>
  [...new Set(Object.entries(tagDbData).map(t => t[1][0]))]
    .filter(v => v)
    .sort(
      (a, b) =>
        capitalize(a, true) === 'Uncategorized'
          ? 1
          : capitalize(b, true) === 'Uncategorized'
            ? -1
            : a.localeCompare(b)
    );
module.exports = {
  getMarkDownAnchor,
  getFilesInDir,
  readSnippets,
  readTags,
  optimizeNodes,
  capitalize,
  objectFromPairs,
  isTravisCI,
  hashData,
  shuffle,
  getCodeBlocks,
  getTextualContent,
  isNotTravisCronOrAPI,
  prepTaggedData
};
