'use strict';

const htmlEscapeMap = {
  "&": "&amp;",
  "<": "&lt;",
  ">": "&gt;",
  "'": "&#39;",
  '"': "&quot;"
};
const htmlEscapeRegexp = /[&<>'"]/g;
const htmlEscape = (str) => str.replace(
  htmlEscapeRegexp,
  (char) => htmlEscapeMap[char]
);

const htmlUnescapeMap = {
  "&amp;": "&",
  "&#38;": "&",
  "&lt;": "<",
  "&#60;": "<",
  "&gt;": ">",
  "&#62;": ">",
  "&apos;": "'",
  "&#39;": "'",
  "&quot;": '"',
  "&#34;": '"'
};
const htmlUnescapeRegexp = /&(amp|#38|lt|#60|gt|#62|apos|#39|quot|#34);/g;
const htmlUnescape = (str) => str.replace(
  htmlUnescapeRegexp,
  (char) => htmlUnescapeMap[char]
);

const resolveTitleFromToken = (token, { shouldAllowHtml, shouldEscapeText }) => {
  const children = token.children ?? [];
  const titleTokenTypes = ["text", "emoji", "code_inline"];
  if (shouldAllowHtml) {
    titleTokenTypes.push("html_inline");
  }
  const titleTokens = children.filter(
    (item) => titleTokenTypes.includes(item.type) && // filter permalink symbol that generated by markdown-it-anchor
    !item.meta?.isPermalinkSymbol
  );
  return titleTokens.reduce((result, item) => {
    if (shouldEscapeText) {
      if (item.type === "code_inline" || item.type === "text") {
        return `${result}${htmlEscape(item.content)}`;
      }
    }
    return `${result}${item.content}`;
  }, "").trim();
};

const resolveHeadersFromTokens = (tokens, {
  level,
  shouldAllowHtml,
  shouldAllowNested,
  shouldEscapeText,
  slugify,
  format
}) => {
  const headers = [];
  const stack = [];
  const push = (header) => {
    while (stack.length !== 0 && header.level <= stack[0].level) {
      stack.shift();
    }
    if (stack.length === 0) {
      headers.push(header);
      stack.push(header);
    } else {
      stack[0].children.push(header);
      stack.unshift(header);
    }
  };
  for (let i = 0; i < tokens.length; i += 1) {
    const token = tokens[i];
    if (token.type !== "heading_open") {
      continue;
    }
    if (token.level !== 0 && !shouldAllowNested) {
      continue;
    }
    const headerLevel = Number.parseInt(token.tag.slice(1), 10);
    if (!level.includes(headerLevel)) {
      continue;
    }
    const nextToken = tokens[i + 1];
    /* istanbul ignore if -- @preserve */
    if (!nextToken) {
      continue;
    }
    const title = resolveTitleFromToken(nextToken, {
      shouldAllowHtml,
      shouldEscapeText
    });
    const slug = token.attrGet("id") ?? slugify(title);
    push({
      level: headerLevel,
      title: format?.(title) ?? title,
      slug,
      link: `#${slug}`,
      children: []
    });
  }
  return headers;
};

const rControl = /[\u0000-\u001f]/g;
const rSpecial = /[\s~`!@#$%^&*()\-_+=[\]{}|\\;:"'“”‘’<>,.?/]+/g;
const rCombining = /[\u0300-\u036F]/g;
const slugify = (str) => str.normalize("NFKD").replace(rCombining, "").replace(rControl, "").replace(rSpecial, "-").replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "").replace(/^(\d)/, "_$1").toLowerCase();

exports.htmlEscape = htmlEscape;
exports.htmlUnescape = htmlUnescape;
exports.resolveHeadersFromTokens = resolveHeadersFromTokens;
exports.resolveTitleFromToken = resolveTitleFromToken;
exports.slugify = slugify;
