const vowels = ['A', 'E', 'I', 'O', 'U']
const consonants = [
  'B',
  'C',
  'D',
  'F',
  'G',
  'H',
  'J',
  'K',
  'L',
  'M',
  'N',
  'P',
  'Q',
  'R',
  'S',
  'T',
  'V',
  'W',
  'X',
  'Y',
  'Z',
]

const startsWiths = ['XR', 'YT', 'AY']

const rule1 = (word: string) =>
  // If a word begins with a vowel, or starts with `"xr"` or `"yt"`,
  // add an `"ay"` sound to the end of the word.
  vowels.concat(startsWiths).some(s => word.toUpperCase().startsWith(s))
    ? `${word}ay`
    : word

const rule2RegExp = new RegExp(`^([${consonants.join('')}]+)(.*)`, 'i')

const rule2 = (word: string): string =>
  // If a word begins with a one or more consonants, first move
  // those consonants to the end of the word and then add an `"ay"`
  // sound to the end of the word.
  word.replace(
    rule2RegExp,
    (_, match, rest) => `${rest}${match}${match ? 'ay' : ''}`,
  )

const rule3RegExp = new RegExp(`^(qu|[${consonants.join('')}]*qu)(.*)`, 'i')

const rule3 = (word: string): string =>
  // If a word starts with zero or more consonants followed by `"qu"`,
  // first move those consonants (if any) and the `"qu"` part to the end
  // of the word, and then add an `"ay"` sound to the end of the word.
  word.replace(
    rule3RegExp,
    (_, match, rest) => `${rest}${match}${match ? 'ay' : ''}`,
  )

const rule4RegExp = new RegExp(`^([${consonants.join('')}]+)(y.*)`, 'i')

const rule4 = (word: string): string =>
  // If a word starts with one or more consonants followed by `"y"`,
  // first move the consonants preceding the `"y"`to the end of the word,
  // and then add an `"ay"` sound to the end of the word.
  word.replace(
    rule4RegExp,
    (_, match, rest) => `${rest}${match}${match ? 'ay' : ''}`,
  )

const applyRule =
  (rule: (word: string) => string) =>
  ({
    originalWord,
    hasChanged,
  }: {
    originalWord: string
    hasChanged: boolean
  }) => {
    if (hasChanged) return { originalWord, hasChanged }
    const word = rule(originalWord)
    return { originalWord: word, hasChanged: word !== originalWord }
  }

export const translate = (text: string): string => {
  return text
    .split(' ')
    .filter(Boolean)
    .map(word => ({ originalWord: word, hasChanged: false }))
    .map(applyRule(rule1))
    .map(applyRule(rule3))
    .map(applyRule(rule4))
    .map(applyRule(rule2))
    .map(({ originalWord }) => originalWord)
    .join(' ')
}
