import cloneDeep from 'lodash/cloneDeep'
import each from 'lodash/each'
import find from 'lodash/find'
import filter from 'lodash/filter'
import reduce from 'lodash/reduce'
import { escapeUnicode } from '@/util/escapeUnicode'

export default {
  namespaced: true,

  state: () => ({
    filters: [],
    originalFilters: [],
  }),

  getters: {
    /**
     * The filters for the resource
     */
    filters: state => state.filters,

    /**
     * The original filters for the resource
     */
    originalFilters: state => state.originalFilters,

    /**
     * Determine if there are any filters for the resource.
     */
    hasFilters: state => Boolean(state.filters.length > 0),

    /**
     * The current unencoded filter value payload
     */
    currentFilters: (state, getters) => {
      return filter(state.filters).map(f => {
        return {
          [f.class]: f.currentValue,
        }
      })
    },

    /**
     * Return the current filters encoded to a string.
     */
    currentEncodedFilters: (state, getters) =>
      btoa(escapeUnicode(JSON.stringify(getters.currentFilters))),

    /**
     * Determine whether any filters are applied
     */
    filtersAreApplied: (state, getters) => getters.activeFilterCount > 0,

    /**
     * Return the number of filters that are non-default
     */
    activeFilterCount: (state, getters) => {
      return reduce(
        state.filters,
        (result, f) => {
          const originalFilter = getters.getOriginalFilter(f.class)
          const originalFilterCloneValue = JSON.stringify(
            originalFilter.currentValue
          )
          const currentFilterCloneValue = JSON.stringify(f.currentValue)
          return currentFilterCloneValue == originalFilterCloneValue
            ? result
            : result + 1
        },
        0
      )
    },

    /**
     * Get a single filter from the list of filters.
     */
    getFilter: state => filterKey => {
      return find(state.filters, filter => {
        return filter.class == filterKey
      })
    },

    getOriginalFilter: state => filterKey => {
      return find(state.originalFilters, filter => {
        return filter.class == filterKey
      })
    },

    /**
     * Get the options for a single filter.
     */
    getOptionsForFilter: (state, getters) => filterKey => {
      const filter = getters.getFilter(filterKey)
      return filter ? filter.options : []
    },

    /**
     * Get the current value for a given filter at the provided key.
     */
    filterOptionValue: (state, getters) => (filterKey, optionKey) => {
      const filter = getters.getFilter(filterKey)

      return find(filter.currentValue, (value, key) => key == optionKey)
    },
  },

  actions: {
    /**
     * Fetch the current filters for the given resource name.
     */
    async fetchFilters({ commit, state }, options) {
      let { resourceName, lens = false } = options
      let { viaResource, viaResourceId, viaRelationship, relationshipType } =
        options
      let params = {
        params: {
          viaResource,
          viaResourceId,
          viaRelationship,
          relationshipType,
        },
      }

      const { data } = lens
        ? await Nova.request().get(
            '/nova-api/' + resourceName + '/lens/' + lens + '/filters',
            params
          )
        : await Nova.request().get(
            '/nova-api/' + resourceName + '/filters',
            params
          )

      commit('storeFilters', data)
    },

    /**
     * Reset the default filter state to the original filter settings.
     */
    async resetFilterState({ commit, getters }) {
      each(getters.originalFilters, filter => {
        commit('updateFilterState', {
          filterClass: filter.class,
          value: filter.currentValue,
        })
      })
    },

    /**
     * Initialize the current filter values from the decoded query string.
     */
    async initializeCurrentFilterValuesFromQueryString(
      { commit, getters },
      encodedFilters
    ) {
      if (encodedFilters) {
        const initialFilters = JSON.parse(atob(encodedFilters))
        each(initialFilters, filter => {
          if (
            filter.hasOwnProperty('class') &&
            filter.hasOwnProperty('value')
          ) {
            commit('updateFilterState', {
              filterClass: filter.class,
              value: filter.value,
            })
          } else {
            for (let key in filter) {
              commit('updateFilterState', {
                filterClass: key,
                value: filter[key],
              })
            }
          }
        })
      }
    },
  },

  mutations: {
    updateFilterState(state, { filterClass, value }) {
      const filter = find(state.filters, f => f.class == filterClass)

      if (filter !== undefined && filter !== null) {
        filter.currentValue = value
      }
    },

    /**
     * Store the mutable filter settings
     */
    storeFilters(state, data) {
      state.filters = data
      state.originalFilters = cloneDeep(data)
    },

    /**
     * Clear the filters for this resource
     */
    clearFilters(state) {
      state.filters = []
      state.originalFilters = []
    },
  },
}
