import { mapActions, mapGetters } from 'vuex'
import { DataProviderType } from '@baserow/modules/core/dataProviderTypes'
import { notifyIf } from '@baserow/modules/core/utils/error'
import _ from 'lodash'

export default {
  data() {
    return {
      currentOffset: 0,
      errorNotified: false,
      resetTimeout: null,
      contentFetchEnabled: true,
    }
  },
  computed: {
    ...mapGetters({
      getLoading: 'elementContent/getLoading',
      getHasMorePage: 'elementContent/getHasMorePage',
      getElementContent: 'elementContent/getElementContent',
      getReset: 'elementContent/getReset',
      getPagesDataSourceById: 'dataSource/getPagesDataSourceById',
      getSharedPage: 'page/getSharedPage',
    }),
    reset() {
      return this.getReset(this.element)
    },
    sharedPage() {
      return this.getSharedPage(this.builder)
    },
    dataSource() {
      if (!this.element.data_source_id) {
        return null
      }
      const pages = [this.page, this.sharedPage]
      return this.getPagesDataSourceById(pages, this.element.data_source_id)
    },
    dataSourceType() {
      if (!this.dataSource) {
        return null
      }
      return this.$registry.get('service', this.dataSource.type)
    },
    elementContent() {
      return this.getElementContent(this.element, this.applicationContext)
    },
    hasMorePage() {
      return this.getHasMorePage(this.element)
    },
    contentLoading() {
      return (
        this.$fetchState.pending ||
        (this.getLoading(this.element) && !this.elementIsInError)
      )
    },
    dispatchContext() {
      return DataProviderType.getAllDataSourceDispatchContext(
        this.$registry.getAll('builderDataProvider'),
        this.applicationContext
      )
    },
    elementIsInError() {
      return this.elementType.isInError({
        page: this.page,
        element: this.element,
        builder: this.builder,
      })
    },
  },
  watch: {
    reset() {
      this.debouncedReset()
    },
    'element.schema_property'(newValue, oldValue) {
      if (newValue) {
        this.debouncedReset()
      }
    },
    'element.data_source_id'() {
      this.debouncedReset()
    },
    'element.items_per_page'() {
      this.debouncedReset()
    },
    dispatchContext: {
      handler(newValue, prevValue) {
        if (!_.isEqual(newValue, prevValue)) {
          this.debouncedReset()
        }
      },
      deep: true,
    },
  },
  async fetch() {
    if (!this.elementIsInError && this.elementType.fetchAtLoad) {
      await this.fetchContent([0, this.element.items_per_page], true)
    }
  },
  methods: {
    ...mapActions({
      fetchElementContent: 'elementContent/fetchElementContent',
    }),
    debouncedReset() {
      clearTimeout(this.resetTimeout)
      this.resetTimeout = setTimeout(() => {
        this.contentFetchEnabled = true
        this.errorNotified = false
        this.currentOffset = 0
        this.loadMore(true)
      }, 500)
    },
    async fetchContent(range, replace) {
      if (!this.canFetch()) {
        return
      }
      try {
        await this.fetchElementContent({
          page: this.page,
          element: this.element,
          dataSource: this.dataSource,
          data: this.dispatchContext,
          range,
          mode: this.applicationContext.mode,
          replace,
        })
        this.currentOffset += this.element.items_per_page
      } catch (error) {
        // Handle the HTTP error if needed
        this.onContentFetchError(error)

        // We need to only launch one toast error message per element,
        // not one per element fetch, or we can end up with many error
        // toasts per element sharing a datasource.
        if (!this.errorNotified) {
          this.errorNotified = true
          notifyIf(error)
        }
      }
    },
    async loadMore(replace = false) {
      await this.fetchContent(
        [this.currentOffset, this.element.items_per_page],
        replace
      )
    },
    /** Overrides this if you want to prevent data fetching */
    canFetch() {
      return this.contentFetchEnabled
    },

    /** Override this if you want to handle content fetch errors */
    onContentFetchError(error) {
      // If the request failed without reaching the server, `error.response`
      // will be `undefined`, so we need to check that before checking the
      // HTTP status code
      if (error.response && [400, 404].includes(error.response.status)) {
        this.contentFetchEnabled = false
      }
    },
  },
}