<template>
  <div
    v-infinite-scroll="loadMore"
    :data-infinite-scroll-disabled="disableInfinite"
    class="custom-solr-searches__container"
  >
    <div class="custom-solr-searches__field df-center-y">
      <input-text
        model-name="search"
        :default-value="searchModel"
        :standalone="true"
        :placeholder="$_tr('placeholder')"
        @onDataChange="onChange"
      />

      <transition name="fadeIn" mode="out-in">
        <loader-ui v-if="searchLoading" :key="searchLoading" />

        <Icon
          v-else
          :key="searchLoading"
          :color="$_theme('colorGrey400')"
          icon="search"
          size="m"
          class="search-icon"
        />
      </transition>
    </div>

    <template v-if="searchIsInit">
      <div class="custom-solr-searches__filters df-center-x">
        <button
          v-for="(value, filter) in searchFilters"
          :key="filter"
          class="btn-reset"
          :class="{ active: searchType === filter }"
          @click="setContentFilter(filter)"
        >
          {{ getCheckedData(contentTypeTitle(filter), 'title') }}
          <template v-if="showCounter">({{ value }})</template>
        </button>
      </div>

      <div class="custom-solr-searches__header df-center-y">
        <div class="df-center-y">
          <BaseText>
            <template v-if="showCounter">
              {{ totalResults }} {{ $_tr('results', totalResults) }}
            </template>
          </BaseText>

          <input-selectbox
            model-name="sortBy"
            :standalone="true"
            :clearable="false"
            :default-value="currentSort.value"
            :input-data="sortOptions"
            @onDataChange="onSortChange"
          />
        </div>
      </div>

      <div class="custom-solr-searches__results">
        <template v-if="Object.keys(searchResults).length > 0">
          <SolrSearchItem
            v-for="(result, uuid) in searchResults"
            :key="uuid"
            :result-uuid="uuid"
            :result-data="result"
          />
        </template>

        <div v-else class="custom-solr-searches__suggests">
          <BaseText v-if="searchSuggest" :html="true">
            {{ $_tc('suggest', [searchSuggest]) }}
          </BaseText>

          <BaseText v-else>{{ $_tr('noResults') }}</BaseText>
        </div>

        <transition name="fadeIn">
          <div
            v-if="currentPage > 1 && searchLoading"
            class="custom-solr-searches__infinite"
          >
            <loader-ui />
          </div>
        </transition>
      </div>

      <transition name="fadeIn">
        <button
          v-if="showTopButton"
          class="custom-solr-searches__top df-center-xy"
          @click="goToTop"
        >
          <Icon icon="arrow-up" size="l" :color="$_theme('colorTextLight')" />
        </button>
      </transition>
    </template>
  </div>
</template>

<script>
import throttle from 'lodash/throttle'

import { BaseText, Icon } from 'inside-collection/atoms'

import SolrSearchItem from './sub-components/SolrSearchItem'

export default {
  name: 'SolrSearches',
  $_i18nRootPath: 'customs.SEARCHES.solr',
  components: { SolrSearchItem, BaseText, Icon },
  data() {
    return {
      searchableContents: [],
      searchIsInit: false,
      searchModel: this.$route.query.search || null,
      searchFilters: {},
      searchType: '',
      currentSort: {
        value: 'score:desc',
        label: this.$_tr('sort.relevance'),
        type: 'relevance'
      },
      searchResults: {},
      searchSuggest: '',
      searchLoading: false,
      totalResults: 0,
      currentPage: 1,
      disableInfinite: true,
      showTopButton: false
    }
  },
  computed: {
    itemPerPage() {
      return 5
    },
    showCounter() {
      return false
    },
    contentTypeTitle() {
      return type => this.searchableContents.find(item => item.type === type)
    },
    sortOptions() {
      return [
        {
          value: 'score:desc',
          label: this.$_tr('sort.relevance'),
          type: 'relevance'
        },
        {
          value: 'timestamp:desc',
          label: this.$_tr('sort.recently'),
          type: 'recently'
        },
        {
          value: 'timestamp:asc',
          label: this.$_tr('sort.oldest'),
          type: 'oldest'
        }
      ]
    }
  },
  watch: {
    /**
     * Watch searchType array
     */
    searchType() {
      this.resetCurrentPage()
      this.launchSearch(this.searchModel)
    }
  },
  async beforeMount() {
    this.searchableContents = await this.fetchSearchableContents()
    this.baseURL = [
      location.protocol,
      '//',
      location.host,
      location.pathname
    ].join('')

    if (this.$route.query.search) {
      this.launchSearch(this.$route.query.search)
    }

    document.addEventListener('scroll', this.onScroll)
  },
  mounted() {
    const sortIndex = this.sortOptions.findIndex(
      data =>
        data.type ==
        this.$store.getters.getInsideSettings('general.search_type')
    )

    this.currentSort = this.sortOptions[sortIndex]
  },
  beforeUnmount() {
    document.removeEventListener('scroll', this.onScroll)
  },
  methods: {
    /**
     * Methods to add and remove content_type filter
     * @params {String} type
     */
    setContentFilter(type) {
      this.searchType = this.searchType === type ? '' : type
    },
    /**
     * Launch search and dispatch response in component data
     * @params {String} searchValue
     */
    async launchSearch(searchValue) {
      const search = await this.fetchSearch(searchValue)

      this.disableInfinite = search.data.length === 0

      if (!Object.keys(this.searchFilters).length) {
        this.searchFilters = search.types
      }

      this.totalResults = search.total
      this.searchSuggest = this.findBestSuggest(
        !search.total ? search.spellcheck : {}
      )

      if (this.currentPage > 1) {
        this.searchResults = {
          ...this.searchResults,
          ...search.data
        }
      } else {
        this.searchResults = search.total ? search.data : {}
      }

      if (!this.searchIsInit) this.searchIsInit = true
    },
    /**
     * Fetch search API
     * @params {String} searchValue
     * @return {Promise}
     */
    fetchSearch(searchValue = '') {
      this.searchLoading = true

      return new Promise((resolve, reject) => {
        this.get('special/search', {
          params: {
            content_types: this.searchType,
            page: this.currentPage,
            perPage: this.itemPerPage,
            terms: searchValue,
            ...(this.currentSort && { sort: this.currentSort.value })
          }
        })
          .then(res => {
            this.searchLoading = false

            if (res.status === 200) {
              resolve(res.data)
            }
          })
          .catch(err => {
            reject(err)
          })
      })
    },
    /**
     * Event on main search input
     * @params {Object} searchModel
     */
    onChange: throttle(function (searchModel) {
      window.history.replaceState(
        {},
        '',
        `${this.baseURL}?search=${searchModel.value}`
      )

      if (searchModel.value.length >= 3) {
        this.resetCurrentPage()
        this.launchSearch(searchModel.value)
        this.searchModel = searchModel.value
      }
    }, 500),
    /**
     * Event on sort change
     * @params {Object} searchSort
     */
    onSortChange(searchSort) {
      this.resetCurrentPage()
      this.currentSort = searchSort || this.sortOptions[0]
      this.launchSearch(this.searchModel)
    },
    /**
     * Event on scroll in page
     */
    onScroll: throttle(function () {
      this.showTopButton = window.scrollY > 400
    }, 500),
    /**
     * Get all searchable contents
     * @return {Promise}
     */
    fetchSearchableContents() {
      return new Promise((resolve, reject) => {
        this.get('searchable', {
          params: {
            langcode: this.$_currentLang
          }
        }).then(res => {
          resolve(res.data)
        })
      })
    },
    /**
     * Reset current page
     */
    resetCurrentPage() {
      this.currentPage = 1
    },
    /**
     * Infinite scroll trigger, launch new request
     */
    loadMore() {
      this.currentPage++
      this.launchSearch(this.searchModel)
      this.disableInfinite = true
    },
    /**
     * Allow to scroll top automatically
     */
    goToTop() {
      window.scrollTo(0, 0)
    },
    /**
     * Find best suggestion in search response
     * @params {Object} suggestList
     * @return {String}
     */
    findBestSuggest(suggestList) {
      const findBestSuggest = Math.max.apply(
        Math,
        Object.entries(suggestList).map(o => o[1])
      )
      const findBestSuggestWord = Object.entries(suggestList).find(
        o => o[1] === findBestSuggest
      )

      return findBestSuggestWord ? findBestSuggestWord[0] : ''
    }
  }
}
</script>

<style scoped lang="scss">
@use '../../styles/modules/inside-customs/searches/solr.scss' as *;
</style>
