<template>
  <Context
    :class="{ 'context--loading-overlay': view._.loading }"
    :overflow-scroll="true"
    :max-height-if-outside-viewport="true"
    @shown="focus"
  >
    <form class="context__form" @submit.prevent="searchIfChanged">
      <div class="control margin-bottom-1">
        <div class="control__elements">
          <div
            class="input__with-icon input__with-icon--left"
            :class="{ 'input__with-icon--loading': loading }"
          >
            <input
              ref="activeSearchTermInput"
              v-model="activeSearchTerm"
              type="text"
              :placeholder="$t('viewSearchContext.searchInRows')"
              class="input"
              @keyup="searchIfChanged"
            />
            <i class="iconoir-search"></i>
          </div>
        </div>
      </div>
      <div
        v-if="!alwaysHideRowsNotMatchingSearch"
        class="control control--align-right margin-bottom-0"
      >
        <SwitchInput
          v-model="hideRowsNotMatchingSearch"
          @input="searchIfChanged"
        >
          {{ $t('viewSearchContext.hideNotMatching') }}
        </SwitchInput>
      </div>
    </form>
  </Context>
</template>

<script>
import debounce from 'lodash/debounce'

import context from '@baserow/modules/core/mixins/context'

export default {
  name: 'ViewSearchContext',
  mixins: [context],
  props: {
    view: {
      type: Object,
      required: true,
    },
    fields: {
      type: Array,
      required: true,
    },
    storePrefix: {
      type: String,
      required: true,
    },
    alwaysHideRowsNotMatchingSearch: {
      type: Boolean,
      required: false,
      default: false,
    },
  },
  data() {
    return {
      activeSearchTerm: '',
      lastSearch: '',
      hideRowsNotMatchingSearch: true,
      lastHide: true,
      loading: false,
    }
  },
  methods: {
    focus() {
      this.$nextTick(function () {
        this.$refs.activeSearchTermInput.focus()
      })
    },
    searchIfChanged() {
      this.$emit('search-changed', this.activeSearchTerm)
      if (
        this.lastSearch === this.activeSearchTerm &&
        this.lastHide === this.hideRowsNotMatchingSearch
      ) {
        return
      }

      this.search()

      this.lastSearch = this.activeSearchTerm
      this.lastHide = this.hideRowsNotMatchingSearch
    },
    search() {
      this.loading = true

      // When the user toggles from hiding rows to not hiding rows we still
      // need to refresh as we need to fetch the un-searched rows from the server first.
      if (this.hideRowsNotMatchingSearch || this.lastHide) {
        // noinspection JSValidateTypes
        this.debouncedServerSearchRefresh()
      } else {
        // noinspection JSValidateTypes
        this.debouncedClientSideSearchRefresh()
      }
    },
    debouncedServerSearchRefresh: debounce(async function () {
      await this.$store.dispatch(
        `${this.storePrefix}view/${this.view.type}/updateSearch`,
        {
          activeSearchTerm: this.activeSearchTerm,
          hideRowsNotMatchingSearch: this.hideRowsNotMatchingSearch,
          // The refresh event we fire below will cause the table to refresh it state from
          // the server using the newly set search terms.
          refreshMatchesOnClient: false,
          fields: this.fields,
        }
      )
      this.$emit('refresh', {
        callback: this.finishedLoading,
      })
    }, 400),
    // Debounce even the client side only refreshes as otherwise spamming the keyboard
    // can cause many refreshes to queue up quickly bogging down the UI.
    debouncedClientSideSearchRefresh: debounce(async function () {
      await this.$store.dispatch(
        `${this.storePrefix}view/${this.view.type}/updateSearch`,
        {
          activeSearchTerm: this.activeSearchTerm,
          hideRowsNotMatchingSearch: this.hideRowsNotMatchingSearch,
          refreshMatchesOnClient: true,
          fields: this.fields,
        }
      )
      this.finishedLoading()
    }, 10),
    finishedLoading() {
      this.loading = false
    },
  },
}
</script>