import { visit } from 'unist-util-visit';
import { u } from 'unist-builder';
const DEFAULT_SETTINGS = {
  markerTagName: 'span',
  markerClassName: 'int-style-marker'
};
export const REGEX = /=([a-z|0-9|_|-|:]+?)=\s*([^=]*[^ ])?\s*==/;
export const REGEX_GLOBAL = /=([a-z|0-9|_|-|:]+?)=\s*([^=]*[^ ])?\s*==/g;

/**
 *
 * This plugin turns ==content== into a <span> element with a customizable
 * marker.
 *
 * For example:
 *
 * Here is ==marked text with default marker==
 * Here is =dev=marked text with dem marker==
 *
 * Note that this is ripped and adapted from [`remark-flexible-markers`][rfm].
 *
 * [rfm]: https://github.com/ipikuka/remark-flexible-markers/blob/main/src/index.ts
 */
export const plugin = options => {
  const settings = {
    ...DEFAULT_SETTINGS,
    ...options
  };

  /**
   * constracts the custom <span> node
   */
  const constructMarker = (marker, markedText) => {
    // https://github.com/syntax-tree/mdast-util-to-hast#example-supporting-custom-nodes
    const node = {
      type: 'paragraph',
      children: [{
        type: 'text',
        value: markedText ?? ''
      }],
      data: {
        hName: settings.markerTagName,
        hProperties: {
          marker
        }
      }
    };
    return node;
  };

  /**
   * Visits the text nodes which match with the mark syntax (==markedtextcontent==)
   */
  const visitor = (node, index, parent) => {
    if (!parent) return;
    if (!REGEX.test(node.value)) return;
    const children = [];
    const {
      value
    } = node;
    let tempValue = '';
    let prevMatchIndex = 0;
    let prevMatchLength = 0;
    const matches = Array.from(value.matchAll(REGEX_GLOBAL));
    for (let i = 0; i < matches.length; i += 1) {
      const match = matches[i];
      const mIndex = match.index ?? 0;
      const mLength = match[0].length; // match[0] is the matched input

      // could be a text part before each matched part
      const textPartIndex = i === 0 ? 0 : prevMatchIndex + prevMatchLength;
      prevMatchIndex = mIndex;
      prevMatchLength = mLength;

      // if there is a text part before
      if (mIndex > textPartIndex) {
        const textValue = value.substring(textPartIndex, mIndex);
        const textNode = u('text', textValue);
        children.push(textNode);
      }
      const [, classification, markedText] = match;
      const markerNode = constructMarker(classification, markedText);
      children.push(markerNode);

      // control for the last text node if exists after the last match
      tempValue = value.slice(mIndex + mLength);
    }

    // if there is still text after the last match
    if (tempValue) {
      const textNode = u('text', tempValue);
      children.push(textNode);
    }
    if (children.length) parent.children.splice(index, 1, ...children);
  };
  const transformer = tree => {
    visit(tree, 'text', visitor);
  };
  return transformer;
};
export default plugin;