export interface TextMatch {
  text: string;
  start: number;
  length: number;
  end: number;
}

/**
 * Adapt findMatchesInText for react-highlight-words findChunks handler.
 * See https://github.com/bvaughn/react-highlight-words#props
 */
export function findHighlightChunksInText({
  searchWords,
  textToHighlight,
}: {
  searchWords: string[];
  textToHighlight: string;
}) {
  return searchWords.reduce((acc: any, term: string) => [...acc, ...findMatchesInText(textToHighlight, term)], []);
}

const cleanNeedle = (needle: string): string => {
  return needle.replace(/[[{(][\w,.-?:*+]+$/, '');
};

/**
 * Returns a list of substring regexp matches.
 */
export function findMatchesInText(haystack: string, needle: string): TextMatch[] {
  // Empty search can send re.exec() into infinite loop, exit early
  if (!haystack || !needle) {
    return [];
  }
  const matches: TextMatch[] = [];
  const { cleaned, flags } = parseFlags(cleanNeedle(needle));
  let regexp: RegExp;
  try {
    regexp = new RegExp(`(?:${cleaned})`, flags);
  } catch (error) {
    return matches;
  }
  haystack.replace(regexp, (substring, ...rest) => {
    if (substring) {
      const offset = rest[rest.length - 2];
      matches.push({
        text: substring,
        start: offset,
        length: substring.length,
        end: offset + substring.length,
      });
    }
    return '';
  });
  return matches;
}

const CLEAR_FLAG = '-';
const FLAGS_REGEXP = /\(\?([ims-]+)\)/g;

/**
 * Converts any mode modifiers in the text to the Javascript equivalent flag
 */
export function parseFlags(text: string): { cleaned: string; flags: string } {
  const flags: Set<string> = new Set(['g']);

  const cleaned = text.replace(FLAGS_REGEXP, (str, group) => {
    const clearAll = group.startsWith(CLEAR_FLAG);

    for (let i = 0; i < group.length; ++i) {
      const flag = group.charAt(i);
      if (clearAll || group.charAt(i - 1) === CLEAR_FLAG) {
        flags.delete(flag);
      } else if (flag !== CLEAR_FLAG) {
        flags.add(flag);
      }
    }
    return ''; // Remove flag from text
  });

  return {
    cleaned: cleaned,
    flags: Array.from(flags).join(''),
  };
}