// @flow
type StyledTextToken = {|
  start: string,
  end: string,
|};

const DEFAULT_STYLED_TEXT_TOKEN: StyledTextToken = {
  start: '[[',
  end: ']]',
};

type StyledTokenIndexes = {|
  start: number,
  end: number,
|};

/**
 *  getTokenIndexes
 *
 *  @param {string} text The string being searched
 *  @param {StyledTextToken} token An object specifying the characters used as begining and the end of the token
 *
 *  @returns {StyledTokenIndexes} an object storing the indexes of the start and end of the token inside of `text`
 */
export const getTokenIndexes = (
  text: string,
  token: StyledTextToken,
): StyledTokenIndexes => ({
  start: text.indexOf(token.start),
  end: text.indexOf(token.end),
});

/**
 *  isTokenValid
 *
 *  @param {StyledTokenIndexes} tokenIndexes
 *
 *  @returns {boolean} Whether the start and end tokens exist in the string and are in the correct order
 */
export const isTokenValid = ({ start, end }: StyledTokenIndexes): boolean =>
  start !== -1 && end !== -1 && start < end;

type TokenizedTextSegments = {|
  prefix: string,
  styled: string,
  suffix: string,
|};

/**
 *  splitStringByFirstToken
 *  @param {string} text The string being split
 *  @param {StyledTextToken} token An object specifying the begining and the end of the string token
 *  @param {StyledTokenIndexes} tokenIndexes An object specifying the indexes of where `token` starts and ends in `text`
 *
 *  @returns {TokenizedTextSegments} An object including the substrings of `text` before, in between, and after the `token`
 */
export const splitStringByFirstToken = (
  text: string,
  token: StyledTextToken,
  tokenIndexes: StyledTokenIndexes,
): TokenizedTextSegments => ({
  prefix: text.slice(0, tokenIndexes.start),
  styled: text.slice(tokenIndexes.start + token.start.length, tokenIndexes.end),
  suffix: text.slice(tokenIndexes.end + token.end.length),
});

type StyledTextSegment = {|
  applyInlineStyle: boolean,
  text: string,
|};
/**
 *  splitStyledTextByToken
 *
 *  @param {string} text The tokenized string you want to split by the given token
 *  @param {StyledTextToken} token An object specifying the begining and the end of the string token
 *
 *  @returns {StyledTextSegment[]} An array of objects containing a flag to apply the inline styles or not and the split text segment
 */
export const splitStyledTextByToken = (
  text: string = '',
  token: StyledTextToken = DEFAULT_STYLED_TEXT_TOKEN,
): StyledTextSegment[] => {
  if (!text.length) return [];

  const tokenIndexes = getTokenIndexes(text, token);

  if (!isTokenValid(tokenIndexes)) return [{ applyInlineStyle: false, text }];

  const { prefix, styled, suffix } = splitStringByFirstToken(
    text,
    token,
    tokenIndexes,
  );

  return [
    ...(prefix.length ? [{ applyInlineStyle: false, text: prefix }] : []),
    ...(styled.length ? [{ applyInlineStyle: true, text: styled }] : []),
    ...splitStyledTextByToken(suffix),
  ];
};
