'use strict';

function markedHighlight(options) {
  if (typeof options === 'function') {
    options = {
      highlight: options,
    };
  }

  if (!options || typeof options.highlight !== 'function') {
    throw new Error('Must provide highlight function');
  }

  if (typeof options.langPrefix !== 'string') {
    options.langPrefix = 'language-';
  }

  if (typeof options.emptyLangClass !== 'string') {
    options.emptyLangClass = '';
  }

  return {
    async: !!options.async,
    walkTokens(token) {
      if (token.type !== 'code') {
        return;
      }

      const lang = getLang(token.lang);

      if (options.async) {
        return Promise.resolve(options.highlight(token.text, lang, token.lang || '')).then(updateToken(token));
      }

      const code = options.highlight(token.text, lang, token.lang || '');
      if (code instanceof Promise) {
        throw new Error('markedHighlight is not set to async but the highlight function is async. Set the async option to true on markedHighlight to await the async highlight function.');
      }
      updateToken(token)(code);
    },
    useNewRenderer: true,
    renderer: {
      code(code, infoString, escaped) {
        // istanbul ignore next
        if (typeof code === 'object') {
          escaped = code.escaped;
          infoString = code.lang;
          code = code.text;
        }
        const lang = getLang(infoString);
        const classValue = lang ? options.langPrefix + escape(lang) : options.emptyLangClass;
        const classAttr = classValue
          ? ` class="${classValue}"`
          : '';
        code = code.replace(/\n$/, '');
        return `<pre><code${classAttr}>${escaped ? code : escape(code, true)}\n</code></pre>`;
      },
    },
  };
}

function getLang(lang) {
  return (lang || '').match(/\S*/)[0];
}

function updateToken(token) {
  return (code) => {
    if (typeof code === 'string' && code !== token.text) {
      token.escaped = true;
      token.text = code;
    }
  };
}

// copied from marked helpers
const escapeTest = /[&<>"']/;
const escapeReplace = new RegExp(escapeTest.source, 'g');
const escapeTestNoEncode = /[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/;
const escapeReplaceNoEncode = new RegExp(escapeTestNoEncode.source, 'g');
const escapeReplacements = {
  '&': '&amp;',
  '<': '&lt;',
  '>': '&gt;',
  '"': '&quot;',
  "'": '&#39;',
};
const getEscapeReplacement = (ch) => escapeReplacements[ch];
function escape(html, encode) {
  if (encode) {
    if (escapeTest.test(html)) {
      return html.replace(escapeReplace, getEscapeReplacement);
    }
  } else {
    if (escapeTestNoEncode.test(html)) {
      return html.replace(escapeReplaceNoEncode, getEscapeReplacement);
    }
  }

  return html;
}

exports.markedHighlight = markedHighlight;
