export interface Token {
  field: string | null
  value: string
}

export function normalizeQuery (query: string): string[] {
  // given a string such as 'this is "my query" go', returns
  // an array of tokens like this: ['this', 'is', 'my query', 'go']
  if (!query) return []

  const match = query.match(/\\?.|^$/g)
  if (!match) return []

  const { tokens } = match.reduce((state, c) => {
    if (c === '"') {
      state.quote ^= 1
    } else if (!state.quote && c === ' ') {
      state.tokens.push('')
    } else {
      state.tokens[state.tokens.length - 1] += c.replace(/\\(.)/, '$1')
    }

    return state
  }, { tokens: [''], quote: 0 })

  return tokens
}

const unquote = (str: string) => {
  if (str[0] === '"') str = str.slice(1)
  if (str[str.length - 1] === '"') str = str.slice(0, -1)
  return str
}

export function parseTokens (normalizedQuery: string[]): Token[] {
  // given an array of tokens as returned by normalizeQuery,
  // returns a list of objects such as [
  //  {
  //    field: 'status',
  //    value: 'pending'
  //  },
  //  {
  //    field: null,
  //    value: 'hello'
  //  }
  // ]
  return normalizedQuery.map(t => {
    // we split the token on ":"
    const parts = t.split(/:(.+)/)
    if (parts.length === 1) {
      // no field specified
      return { field: null, value: t }
    }

    // first item is the field, second is the value, possibly quoted
    const [field, value] = parts

    // we remove surrounding quotes if any
    return { field, value: unquote(value) }
  })
}

export function compileTokens (tokens: Token[]) {
  // given a list of tokens as returned by parseTokens,
  // returns a string query
  const parts = tokens.map(token => {
    const { field } = token
    let { value } = token

    if (value.includes(' ')) {
      value = `"${value}"`
    }

    if (field) {
      return `${field}:${value}`
    }

    return value
  })

  return parts.join(' ')
}
