import { debounced, isArray } from '@/helpers'
import Vue from 'vue'
import { createBrowserHistory } from 'history'
import queryString from 'qs'

function isString(value) {
  return typeof value === 'string' || value instanceof String
}
const encoder = function(val, defaultEncoder, charset, type) {
  if (type === 'key') {
    return val
  } else if (type === 'value') {
    if (!isString(val)) return val
    return val.replace(/&/g, '%26')
  }
}

const stringifyOptions = {
  allowDots: true,
  addQueryPrefix: true,
  arrayFormat: 'comma',
  skipNulls: true,
  encoder
}

const parseOptions = {
  ignoreQueryPrefix: true,
  allowDots: true,
  comma: true
}

class QueryState {
  constructor() {
    this.history = createBrowserHistory()
    this.queryParam = 'q'
    this.pageParam = 'page'
  }

  // Listen for changes to the current location.
  onChange(callback) {
    this.unlisten = this.history.listen((location, action) => {
      // console.log(action, location.pathname, location.search)

      // skip for PUSH action
      if (action === 'PUSH') return

      debounced(250, callback(this.parseUrl(location)))
    })
  }

  parseUrl(location) {
    // if location is empty, then take from history
    location = location || this.history.location

    const intermediate = queryString.parse(location.search, parseOptions)

    // convert single value to array with one element
    intermediate.selections = []
    if (intermediate.mz) {
      Object.keys(intermediate.mz).forEach(key => {
        const value = intermediate.mz[key]
        if (value && !isArray(value)) {
          intermediate.mz[key] = [value]
        }
        // initialize selections that will be split and updated inside RUN_QUERY mutation
        intermediate.selections.push({
          action: 'init',
          name: key,
          key: key,
          value: value,
          label: key
        })
      })
    }

    const keyword = intermediate[this.queryParam] || ''
    if (keyword) {
      // initialize selections that will be split and updated inside RUN_QUERY mutation
      intermediate.selections.push({
        action: 'replace',
        name: 'Keyword',
        key: '_Keyword',
        value: keyword,
        label: Vue.prototype.$t('Keyword') || 'Keyword'
      })
    }

    const final = {
      keyword,
      page: parseInt(intermediate[this.pageParam]) || 1,
      sort: intermediate.sort,
      pageSize: intermediate.size ? parseInt(intermediate.size) : undefined,
      filters: intermediate.mz,
      selections: intermediate.selections
    }

    return final
  }

  push(state) {
    const object = {
      [this.pageParam]: state.page,
      sort: state.sort,
      size: state.pageSize,
      mz: state.filters
    }
    // do not push keyword field if empty
    if (state.keyword && state.keyword !== '') {
      object[this.queryParam] = state.keyword
    }

    this.history.push({
      search: queryString.stringify(object, stringifyOptions)
    })
  }

  setQueryParam(queryParam) {
    this.queryParam = queryParam
  }

  setPageParam(pageParam) {
    this.pageParam = pageParam
  }

  destroy() {
    this.unlisten()
  }
}

const instance = new QueryState()

Vue.prototype.$queryState = instance

export default instance
