<template>
  <loading-view
    :loading="initialLoading"
    :dusk="resourceName + '-index-component'"
    :data-relationship="viaRelationship"
  >
    <custom-index-header
      v-if="!viaResource"
      class="mb-3"
      :resource-name="resourceName"
    />

    <div v-if="shouldShowCards">
      <cards
        v-if="smallCards.length > 0"
        :cards="smallCards"
        class="mb-3"
        :resource-name="resourceName"
      />

      <cards
        v-if="largeCards.length > 0"
        :cards="largeCards"
        size="large"
        :resource-name="resourceName"
      />
    </div>

    <heading :level="1" class="mb-3" v-html="headingTitle" />

    <div class="flex">
      <!-- Search -->
      <div
        v-if="resourceInformation.searchable && !viaHasOne"
        class="relative h-9 flex-no-shrink"
        :class="{
          'mb-6': resourceInformation.searchable && !viaHasOne,
        }"
      >
        <icon type="search" class="absolute search-icon-center ml-3 text-70" />

        <input
          data-testid="search-input"
          dusk="search"
          class="appearance-none form-search w-search pl-search shadow"
          :placeholder="__('Search')"
          type="search"
          v-model="search"
          @keydown.stop="performSearch"
          @search="performSearch"
          spellcheck="false"
        />
      </div>

      <div class="w-full flex items-center" :class="{ 'mb-6': !viaResource }">
        <custom-index-toolbar
          v-if="!viaResource"
          :resource-name="resourceName"
        />

        <!-- Create / Attach Button -->
        <create-resource-button
          :label="createButtonLabel"
          :singular-name="singularName"
          :resource-name="resourceName"
          :via-resource="viaResource"
          :via-resource-id="viaResourceId"
          :via-relationship="viaRelationship"
          :relationship-type="relationshipType"
          :authorized-to-create="authorizedToCreate && !resourceIsFull"
          :authorized-to-relate="authorizedToRelate"
          class="flex-no-shrink ml-auto"
          :class="{ 'mb-6': viaResource }"
        />
      </div>
    </div>

    <card>
      <div
        class="flex items-center"
        :class="{
          'py-3 border-b border-50':
            shouldShowCheckBoxes ||
            shouldShowDeleteMenu ||
            softDeletes ||
            !viaResource ||
            hasFilters ||
            haveStandaloneActions,
        }"
      >
        <div class="flex items-center">
          <div class="px-3" v-if="shouldShowCheckBoxes">
            <!-- Select All -->
            <dropdown
              dusk="select-all-dropdown"
              placement="bottom-end"
              class="-mx-2"
            >
              <dropdown-trigger class="px-2">
                <fake-checkbox :checked="selectAllChecked" />
              </dropdown-trigger>

              <dropdown-menu slot="menu" direction="ltr" width="250">
                <div class="p-4">
                  <ul class="list-reset">
                    <li class="flex items-center mb-4">
                      <checkbox-with-label
                        :checked="selectAllChecked"
                        @input="toggleSelectAll"
                        dusk="select-all-button"
                      >
                        {{ __('Select All') }}
                      </checkbox-with-label>
                    </li>
                    <li class="flex items-center">
                      <checkbox-with-label
                        dusk="select-all-matching-button"
                        :checked="selectAllMatchingChecked"
                        @input="toggleSelectAllMatching"
                      >
                        <template>
                          <span class="mr-1">
                            {{ __('Select All Matching') }} ({{
                              allMatchingResourceCount
                            }})
                          </span>
                        </template>
                      </checkbox-with-label>
                    </li>
                  </ul>
                </div>
              </dropdown-menu>
            </dropdown>
          </div>
        </div>

        <div class="flex items-center ml-auto px-3">
          <resource-polling-button
            v-if="shouldShowPollingToggle"
            :currently-polling="currentlyPolling"
            @start-polling="startPolling"
            @stop-polling="stopPolling"
            class="mr-1"
          />

          <!-- Action Selector -->
          <action-selector
            v-if="selectedResources.length > 0 || haveStandaloneActions"
            :resource-name="resourceName"
            :actions="availableActions"
            :pivot-actions="pivotActions"
            :pivot-name="pivotName"
            :query-string="{
              currentSearch,
              encodedFilters,
              currentTrashed,
              viaResource,
              viaResourceId,
              viaRelationship,
            }"
            :selected-resources="selectedResourcesForActionSelector"
            @actionExecuted="getResources"
          />

          <!-- Lenses -->
          <dropdown
            class="bg-30 hover:bg-40 mr-3 rounded"
            v-if="lenses.length > 0"
          >
            <dropdown-trigger class="px-3">
              <h3
                slot="default"
                class="flex items-center font-normal text-base text-90 h-9"
              >
                {{ __('Lens') }}
              </h3>
            </dropdown-trigger>

            <dropdown-menu slot="menu" width="240" direction="rtl">
              <lens-selector :resource-name="resourceName" :lenses="lenses" />
            </dropdown-menu>
          </dropdown>

          <!-- Filters -->
          <filter-menu
            :resource-name="resourceName"
            :soft-deletes="softDeletes"
            :via-resource="viaResource"
            :via-has-one="viaHasOne"
            :trashed="trashed"
            :per-page="perPage"
            :per-page-options="
              perPageOptions || resourceInformation.perPageOptions
            "
            @clear-selected-filters="clearSelectedFilters"
            @filter-changed="filterChanged"
            @trashed-changed="trashedChanged"
            @per-page-changed="updatePerPageChanged"
          />

          <delete-menu
            v-if="shouldShowDeleteMenu"
            dusk="delete-menu"
            :soft-deletes="softDeletes"
            :resources="resources"
            :selected-resources="selectedResources"
            :via-many-to-many="viaManyToMany"
            :all-matching-resource-count="allMatchingResourceCount"
            :all-matching-selected="selectAllMatchingChecked"
            :authorized-to-delete-selected-resources="
              authorizedToDeleteSelectedResources
            "
            :authorized-to-force-delete-selected-resources="
              authorizedToForceDeleteSelectedResources
            "
            :authorized-to-delete-any-resources="authorizedToDeleteAnyResources"
            :authorized-to-force-delete-any-resources="
              authorizedToForceDeleteAnyResources
            "
            :authorized-to-restore-selected-resources="
              authorizedToRestoreSelectedResources
            "
            :authorized-to-restore-any-resources="
              authorizedToRestoreAnyResources
            "
            @deleteSelected="deleteSelectedResources"
            @deleteAllMatching="deleteAllMatchingResources"
            @forceDeleteSelected="forceDeleteSelectedResources"
            @forceDeleteAllMatching="forceDeleteAllMatchingResources"
            @restoreSelected="restoreSelectedResources"
            @restoreAllMatching="restoreAllMatchingResources"
            @close="deleteModalOpen = false"
          />
        </div>
      </div>

      <loading-view :loading="loading">
        <div
          v-if="!resources.length"
          class="flex justify-center items-center px-6 py-8"
        >
          <div class="text-center">
            <svg
              class="mb-3"
              xmlns="http://www.w3.org/2000/svg"
              width="65"
              height="51"
              viewBox="0 0 65 51"
            >
              <path
                fill="#A8B9C5"
                d="M56 40h2c.552285 0 1 .447715 1 1s-.447715 1-1 1h-2v2c0 .552285-.447715 1-1 1s-1-.447715-1-1v-2h-2c-.552285 0-1-.447715-1-1s.447715-1 1-1h2v-2c0-.552285.447715-1 1-1s1 .447715 1 1v2zm-5.364125-8H38v8h7.049375c.350333-3.528515 2.534789-6.517471 5.5865-8zm-5.5865 10H6c-3.313708 0-6-2.686292-6-6V6c0-3.313708 2.686292-6 6-6h44c3.313708 0 6 2.686292 6 6v25.049375C61.053323 31.5511 65 35.814652 65 41c0 5.522847-4.477153 10-10 10-5.185348 0-9.4489-3.946677-9.950625-9zM20 30h16v-8H20v8zm0 2v8h16v-8H20zm34-2v-8H38v8h16zM2 30h16v-8H2v8zm0 2v4c0 2.209139 1.790861 4 4 4h12v-8H2zm18-12h16v-8H20v8zm34 0v-8H38v8h16zM2 20h16v-8H2v8zm52-10V6c0-2.209139-1.790861-4-4-4H6C3.790861 2 2 3.790861 2 6v4h52zm1 39c4.418278 0 8-3.581722 8-8s-3.581722-8-8-8-8 3.581722-8 8 3.581722 8 8 8z"
              />
            </svg>

            <h3
              class="text-base text-80 font-normal"
              :class="{ 'mb-6': authorizedToCreate && !resourceIsFull }"
            >
              {{
                __('No :resource matched the given criteria.', {
                  resource: singularName.toLowerCase(),
                })
              }}
            </h3>

            <create-resource-button
              classes="btn btn-sm btn-outline inline-flex items-center focus:outline-none focus:shadow-outline active:outline-none active:shadow-outline"
              :label="createButtonLabel"
              :singular-name="singularName"
              :resource-name="resourceName"
              :via-resource="viaResource"
              :via-resource-id="viaResourceId"
              :via-relationship="viaRelationship"
              :relationship-type="relationshipType"
              :authorized-to-create="authorizedToCreate && !resourceIsFull"
              :authorized-to-relate="authorizedToRelate"
            >
            </create-resource-button>
          </div>
        </div>

        <div class="overflow-hidden overflow-x-auto relative">
          <!-- Resource Table -->
          <resource-table
            :authorized-to-relate="authorizedToRelate"
            :resource-name="resourceName"
            :resources="resources"
            :singular-name="singularName"
            :selected-resources="selectedResources"
            :selected-resource-ids="selectedResourceIds"
            :actions-are-available="allActions.length > 0"
            :should-show-checkboxes="shouldShowCheckBoxes"
            :via-resource="viaResource"
            :via-resource-id="viaResourceId"
            :via-relationship="viaRelationship"
            :relationship-type="relationshipType"
            :update-selection-status="updateSelectionStatus"
            @order="orderByField"
            @reset-order-by="resetOrderBy"
            @delete="deleteResources"
            @restore="restoreResources"
            @actionExecuted="getResources"
            ref="resourceTable"
          />
        </div>

        <!-- Pagination -->
        <component
          :is="paginationComponent"
          v-if="shouldShowPagination"
          :next="hasNextPage"
          :previous="hasPreviousPage"
          @load-more="loadMore"
          @page="selectPage"
          :pages="totalPages"
          :page="currentPage"
          :per-page="perPage"
          :resource-count-label="resourceCountLabel"
          :current-resource-count="resources.length"
          :all-matching-resource-count="allMatchingResourceCount"
        >
          <span
            v-if="resourceCountLabel"
            class="text-sm text-80 px-4"
            :class="{
              'ml-auto': paginationComponent == 'pagination-links',
            }"
          >
            {{ resourceCountLabel }}
          </span>
        </component>
      </loading-view>
    </card>
  </loading-view>
</template>

<script>
import {
  Capitalize,
  Deletable,
  Filterable,
  HasCards,
  InteractsWithQueryString,
  InteractsWithResourceInformation,
  Minimum,
  Paginatable,
  PerPageable,
  mapProps,
} from 'laravel-nova'
import HasActions from '@/mixins/HasActions'
import { CancelToken, Cancel } from 'axios'

export default {
  mixins: [
    HasActions,
    Deletable,
    Filterable,
    HasCards,
    Paginatable,
    PerPageable,
    InteractsWithResourceInformation,
    InteractsWithQueryString,
  ],

  metaInfo() {
    if (this.shouldOverrideMeta) {
      return {
        title: this.__(`${this.resourceInformation.label}`),
      }
    }
  },

  props: {
    shouldOverrideMeta: {
      type: Boolean,
      default: true,
    },

    field: {
      type: Object,
    },

    ...mapProps([
      'resourceName',
      'viaResource',
      'viaResourceId',
      'viaRelationship',
    ]),

    relationshipType: {
      type: String,
      default: '',
    },

    disablePagination: {
      type: Boolean,
      default: false,
    },

    initialPerPage: {
      type: Number,
      required: false,
    },
  },

  data: () => ({
    debouncer: null,
    canceller: null,
    pollingListener: null,
    initialLoading: true,
    loading: true,

    resourceResponse: null,
    resources: [],
    softDeletes: false,
    selectedResources: [],
    selectAllMatchingResources: false,
    allMatchingResourceCount: 0,

    deleteModalOpen: false,

    search: '',
    lenses: [],

    authorizedToRelate: false,

    orderBy: '',
    orderByDirection: '',
    trashed: '',

    // Load More Pagination
    currentPageLoadMore: null,

    currentlyPolling: false,
  }),

  /**
   * Mount the component and retrieve its initial data.
   */
  async created() {
    if (Nova.missingResource(this.resourceName))
      return this.$router.push({ name: '404' })

    this.debouncer = _.debounce(
      callback => callback(),
      this.resourceInformation.debounce
    )

    // Bind the keydown even listener when the router is visited if this
    // component is not a relation on a Detail page
    if (!this.viaResource && !this.viaResourceId) {
      Nova.addShortcut('c', this.handleKeydown)
    }

    this.initializeSearchFromQueryString()
    this.initializePerPageFromQueryString()
    this.initializeTrashedFromQueryString()
    this.initializeOrderingFromQueryString()

    this.currentlyPolling = this.resourceInformation.polling

    await this.initializeFilters()
    await this.getResources()
    await this.getAuthorizationToRelate()

    this.getLenses()
    this.getActions()

    this.initialLoading = false

    this.$watch(
      () => {
        return (
          this.resourceName +
          this.encodedFilters +
          this.currentSearch +
          this.currentPage +
          this.perPage +
          this.currentOrderBy +
          this.currentOrderByDirection +
          this.currentTrashed
        )
      },
      () => {
        if (this.canceller !== null) this.canceller()

        this.getResources()
      }
    )

    Nova.$on('refresh-resources', () => {
      this.getResources()
    })

    if (this.resourceInformation.polling) {
      this.startPolling()
    }
  },

  /**
   * Unbind the keydown even listener when the before component is destroyed
   */
  beforeDestroy() {
    if (this.pollingListener) {
      clearInterval(this.pollingListener)
    }

    if (!this.viaResource && !this.viaResourceId) {
      Nova.disableShortcut('c')
    }
  },

  watch: {
    $route(to, from) {
      this.initializeSearchFromQueryString()
      this.initializeState(false)
    },
  },

  methods: {
    /**
     * Handle the keydown event
     */
    handleKeydown(e) {
      // `c`
      if (
        this.authorizedToCreate &&
        e.target.tagName != 'INPUT' &&
        e.target.tagName != 'TEXTAREA' &&
        e.target.contentEditable != 'true'
      ) {
        this.$router.push({
          name: 'create',
          params: { resourceName: this.resourceName },
        })
      }
    },

    /**
     * Select all of the available resources
     */
    selectAllResources() {
      this.selectedResources = this.resources.slice(0)
    },

    /**
     * Toggle the selection of all resources
     */
    toggleSelectAll(event) {
      if (this.selectAllChecked) return this.clearResourceSelections()
      this.selectAllResources()
    },

    /**
     * Toggle the selection of all matching resources in the database
     */
    toggleSelectAllMatching() {
      if (!this.selectAllMatchingResources) {
        this.selectAllResources()
        this.selectAllMatchingResources = true

        return
      }

      this.selectAllMatchingResources = false
    },

    /*
     * Update the resource selection status
     */
    updateSelectionStatus(resource) {
      if (!_(this.selectedResources).includes(resource))
        return this.selectedResources.push(resource)
      const index = this.selectedResources.indexOf(resource)
      if (index > -1) return this.selectedResources.splice(index, 1)
    },

    /**
     * Get the resources based on the current page, search, filters, etc.
     */
    getResources() {
      this.loading = true

      this.$nextTick(() => {
        this.clearResourceSelections()

        return Minimum(
          Nova.request().get('/nova-api/' + this.resourceName, {
            params: this.resourceRequestQueryString,
            cancelToken: new CancelToken(canceller => {
              this.canceller = canceller
            }),
          }),
          300
        )
          .then(({ data }) => {
            this.resources = []

            this.resourceResponse = data
            this.resources = data.resources
            this.softDeletes = data.softDeletes
            this.perPage = data.per_page
            this.allMatchingResourceCount = data.total

            this.loading = false

            Nova.$emit('resources-loaded')
          })
          .catch(e => {
            if (e instanceof Cancel) {
              return
            }

            throw e
          })
      })
    },

    /**
     * Get the relatable authorization status for the resource.
     */
    getAuthorizationToRelate() {
      if (
        !this.authorizedToCreate &&
        this.relationshipType != 'belongsToMany' &&
        this.relationshipType != 'morphToMany'
      ) {
        return
      }

      if (!this.viaResource) {
        return (this.authorizedToRelate = true)
      }

      return Nova.request()
        .get(
          '/nova-api/' +
            this.resourceName +
            '/relate-authorization' +
            '?viaResource=' +
            this.viaResource +
            '&viaResourceId=' +
            this.viaResourceId +
            '&viaRelationship=' +
            this.viaRelationship +
            '&relationshipType=' +
            this.relationshipType
        )
        .then(response => {
          this.authorizedToRelate = response.data.authorized
        })
    },

    /**
     * Get the lenses available for the current resource.
     */
    getLenses() {
      this.lenses = []

      if (this.viaResource) {
        return
      }

      return Nova.request()
        .get('/nova-api/' + this.resourceName + '/lenses')
        .then(response => {
          this.lenses = response.data
        })
    },

    /**
     * Get the actions available for the current resource.
     */
    getActions() {
      this.actions = []
      this.pivotActions = null

      return Nova.request()
        .get(`/nova-api/${this.resourceName}/actions`, {
          params: {
            viaResource: this.viaResource,
            viaResourceId: this.viaResourceId,
            viaRelationship: this.viaRelationship,
            relationshipType: this.relationshipType,
            display: 'index',
          },
        })
        .then(response => {
          this.actions = response.data.actions
          this.pivotActions = response.data.pivotActions
        })
    },

    /**
     * Execute a search against the resource.
     */
    performSearch(event) {
      this.debouncer(() => {
        // Only search if we're not tabbing into the field
        if (event.which != 9) {
          this.updateQueryString({
            [this.pageParameter]: 1,
            [this.searchParameter]: this.search,
          })
        }
      })
    },

    /**
     * Clear the selected resouces and the "select all" states.
     */
    clearResourceSelections() {
      this.selectAllMatchingResources = false
      this.selectedResources = []
    },

    /**
     * Get the count of all of the matching resources.
     */
    getAllMatchingResourceCount() {
      Nova.request()
        .get('/nova-api/' + this.resourceName + '/count', {
          params: this.resourceRequestQueryString,
        })
        .then(response => {
          this.allMatchingResourceCount = response.data.count
        })
    },

    /**
     * Sort the resources by the given field.
     */
    orderByField(field) {
      let direction = this.currentOrderByDirection == 'asc' ? 'desc' : 'asc'

      if (this.currentOrderBy != field.sortableUriKey) {
        direction = 'asc'
      }

      this.updateQueryString({
        [this.orderByParameter]: field.sortableUriKey,
        [this.orderByDirectionParameter]: direction,
      })
    },

    /**
     * Reset the order by to its default state
     */
    resetOrderBy(field) {
      this.updateQueryString({
        [this.orderByParameter]: field.sortableUriKey,
        [this.orderByDirectionParameter]: null,
      })
    },

    /**
     * Sync the current search value from the query string.
     */
    initializeSearchFromQueryString() {
      this.search = this.currentSearch
    },

    /**
     * Sync the current order by values from the query string.
     */
    initializeOrderingFromQueryString() {
      this.orderBy = this.currentOrderBy
      this.orderByDirection = this.currentOrderByDirection
    },

    /**
     * Sync the trashed state values from the query string.
     */
    initializeTrashedFromQueryString() {
      this.trashed = this.currentTrashed
    },

    /**
     * Update the trashed constraint for the resource listing.
     */
    trashedChanged(trashedStatus) {
      this.trashed = trashedStatus
      this.updateQueryString({ [this.trashedParameter]: this.trashed })
    },

    /**
     * Update the per page parameter in the query string
     */
    updatePerPageChanged(perPage) {
      this.perPage = perPage
      this.perPageChanged()
    },

    /**
     * Load more resources.
     */
    loadMore() {
      if (this.currentPageLoadMore === null) {
        this.currentPageLoadMore = this.currentPage
      }

      this.currentPageLoadMore = this.currentPageLoadMore + 1

      return Minimum(
        Nova.request().get('/nova-api/' + this.resourceName, {
          params: {
            ...this.resourceRequestQueryString,
            page: this.currentPageLoadMore, // We do this to override whatever page number is in the URL
          },
        }),
        300
      ).then(({ data }) => {
        this.resourceResponse = data
        this.resources = [...this.resources, ...data.resources]

        this.allMatchingResourceCount = data.total

        Nova.$emit('resources-loaded')
      })
    },

    /**
     * Select the next page.
     */
    selectPage(page) {
      this.updateQueryString({ [this.pageParameter]: page })
    },

    /**
     * Sync the per page values from the query string.
     */
    initializePerPageFromQueryString() {
      this.perPage =
        this.$route.query[this.perPageParameter] ||
        this.initialPerPage ||
        this.resourceInformation.perPageOptions[0]
    },

    /**
     * Pause polling for new resources.
     */
    stopPolling() {
      clearInterval(this.pollingListener)

      this.$nextTick(() => (this.currentlyPolling = false))
    },

    /**
     * Start polling for new resources.
     */
    startPolling() {
      this.pollingListener = setInterval(() => {
        if (
          document.hasFocus() &&
          document.querySelectorAll('div.modal').length < 1
        ) {
          this.getResources()
        }
      }, this.resourceInformation.pollingInterval)

      this.$nextTick(() => (this.currentlyPolling = true))
    },
  },

  computed: {
    /**
     * Determine if the resource has any filters
     */
    hasFilters() {
      return this.$store.getters[`${this.resourceName}/hasFilters`]
    },

    /**
     * Determine if the resource should show any cards
     */
    shouldShowCards() {
      // Don't show cards if this resource is beings shown via a relations
      return (
        this.cards.length > 0 &&
        this.resourceName == this.$route.params.resourceName
      )
    },

    /**
     * Get the endpoint for this resource's metrics.
     */
    cardsEndpoint() {
      return `/nova-api/${this.resourceName}/cards`
    },

    /**
     * Get the name of the search query string variable.
     */
    searchParameter() {
      return this.viaRelationship
        ? this.viaRelationship + '_search'
        : this.resourceName + '_search'
    },

    /**
     * Get the name of the order by query string variable.
     */
    orderByParameter() {
      return this.viaRelationship
        ? this.viaRelationship + '_order'
        : this.resourceName + '_order'
    },

    /**
     * Get the name of the order by direction query string variable.
     */
    orderByDirectionParameter() {
      return this.viaRelationship
        ? this.viaRelationship + '_direction'
        : this.resourceName + '_direction'
    },

    /**
     * Get the name of the trashed constraint query string variable.
     */
    trashedParameter() {
      return this.viaRelationship
        ? this.viaRelationship + '_trashed'
        : this.resourceName + '_trashed'
    },

    /**
     * Get the name of the per page query string variable.
     */
    perPageParameter() {
      return this.viaRelationship
        ? this.viaRelationship + '_per_page'
        : this.resourceName + '_per_page'
    },

    /**
     * Get the name of the page query string variable.
     */
    pageParameter() {
      return this.viaRelationship
        ? this.viaRelationship + '_page'
        : this.resourceName + '_page'
    },

    /**
     * Build the resource request query string.
     */
    resourceRequestQueryString() {
      return {
        search: this.currentSearch,
        filters: this.encodedFilters,
        orderBy: this.currentOrderBy,
        orderByDirection: this.currentOrderByDirection,
        perPage: this.currentPerPage,
        trashed: this.currentTrashed,
        page: this.currentPage,
        viaResource: this.viaResource,
        viaResourceId: this.viaResourceId,
        viaRelationship: this.viaRelationship,
        viaResourceRelationship: this.viaResourceRelationship,
        relationshipType: this.relationshipType,
      }
    },

    /**
     * Determine if all resources are selected.
     */
    selectAllChecked() {
      return this.selectedResources.length == this.resources.length
    },

    /**
     * Determine if all matching resources are selected.
     */
    selectAllMatchingChecked() {
      return (
        this.selectedResources.length == this.resources.length &&
        this.selectAllMatchingResources
      )
    },

    /**
     * Get the IDs for the selected resources.
     */
    selectedResourceIds() {
      return _.map(this.selectedResources, resource => resource.id.value)
    },

    /**
     * Get the current search value from the query string.
     */
    currentSearch() {
      return this.$route.query[this.searchParameter] || ''
    },

    /**
     * Get the current order by value from the query string.
     */
    currentOrderBy() {
      return this.$route.query[this.orderByParameter] || ''
    },

    /**
     * Get the current order by direction from the query string.
     */
    currentOrderByDirection() {
      return this.$route.query[this.orderByDirectionParameter] || null
    },

    /**
     * Get the current trashed constraint value from the query string.
     */
    currentTrashed() {
      return this.$route.query[this.trashedParameter] || ''
    },

    /**
     * Determine if the current resource listing is via a many-to-many relationship.
     */
    viaManyToMany() {
      return (
        this.relationshipType == 'belongsToMany' ||
        this.relationshipType == 'morphToMany'
      )
    },

    /**
     * Determine if the resource / relationship is "full".
     */
    resourceIsFull() {
      return (
        (Boolean(this.viaHasOne) && this.resources.length > 0) ||
        Boolean(this.viaHasOneThrough && this.resources.length > 0)
      )
    },

    /**
     * Determine if the current resource listing is via a has-one relationship.
     */
    viaHasOne() {
      return (
        this.relationshipType == 'hasOne' || this.relationshipType == 'morphOne'
      )
    },

    viaHasOneThrough() {
      return this.relationshipType == 'hasOneThrough'
    },

    /**
     * Get the singular name for the resource
     */
    singularName() {
      if (this.isRelation && this.field) {
        return Capitalize(this.field.singularLabel)
      }

      return Capitalize(this.resourceInformation.singularLabel)
    },

    /**
     * Get the default label for the create button
     */
    createButtonLabel() {
      return this.resourceInformation.createButtonLabel
    },

    /**
     * Determine if there are any resources for the view
     */
    hasResources() {
      return Boolean(this.resources.length > 0)
    },

    /**
     * Determine if there any lenses for this resource
     */
    hasLenses() {
      return Boolean(this.lenses.length > 0)
    },

    /**
     * Determine whether to show the selection checkboxes for resources
     */
    shouldShowCheckBoxes() {
      return (
        Boolean(this.hasResources && !this.viaHasOne) &&
        Boolean(
          this.actionsAreAvailable ||
            this.authorizedToDeleteAnyResources ||
            this.canShowDeleteMenu
        )
      )
    },

    /**
     * Determine if any selected resources may be deleted.
     */
    authorizedToDeleteSelectedResources() {
      return Boolean(
        _.find(this.selectedResources, resource => resource.authorizedToDelete)
      )
    },

    /**
     * Determine if any selected resources may be force deleted.
     */
    authorizedToForceDeleteSelectedResources() {
      return Boolean(
        _.find(
          this.selectedResources,
          resource => resource.authorizedToForceDelete
        )
      )
    },

    /**
     * Determine if the user is authorized to delete any listed resource.
     */
    authorizedToDeleteAnyResources() {
      return (
        this.resources.length > 0 &&
        Boolean(_.find(this.resources, resource => resource.authorizedToDelete))
      )
    },

    /**
     * Determine if the user is authorized to force delete any listed resource.
     */
    authorizedToForceDeleteAnyResources() {
      return (
        this.resources.length > 0 &&
        Boolean(
          _.find(this.resources, resource => resource.authorizedToForceDelete)
        )
      )
    },

    /**
     * Determine if any selected resources may be restored.
     */
    authorizedToRestoreSelectedResources() {
      return Boolean(
        _.find(this.selectedResources, resource => resource.authorizedToRestore)
      )
    },

    /**
     * Determine if the user is authorized to restore any listed resource.
     */
    authorizedToRestoreAnyResources() {
      return (
        this.resources.length > 0 &&
        Boolean(
          _.find(this.resources, resource => resource.authorizedToRestore)
        )
      )
    },

    /**
     * Determine whether the delete menu should be shown to the user
     */
    shouldShowDeleteMenu() {
      return (
        Boolean(this.selectedResources.length > 0) && this.canShowDeleteMenu
      )
    },

    /**
     * Determine whether the user is authorized to perform actions on the delete menu
     */
    canShowDeleteMenu() {
      return Boolean(
        this.authorizedToDeleteSelectedResources ||
          this.authorizedToForceDeleteSelectedResources ||
          this.authorizedToRestoreSelectedResources ||
          this.selectAllMatchingChecked
      )
    },

    /**
     * Determine if the index is a relation field
     */
    isRelation() {
      return Boolean(this.viaResourceId && this.viaRelationship)
    },

    /**
     * Return the heading for the view
     */
    headingTitle() {
      return this.loading
        ? '&nbsp;'
        : this.isRelation && this.field
        ? this.field.name
        : this.resourceResponse.label
    },

    /**
     * Return the resource count label
     */
    resourceCountLabel() {
      const first = this.perPage * (this.currentPage - 1)

      return (
        this.resources.length &&
        `${Nova.formatNumber(first + 1)}-${Nova.formatNumber(
          first + this.resources.length
        )} ${this.__('of')} ${Nova.formatNumber(this.allMatchingResourceCount)}`
      )
    },

    /**
     * Return the currently encoded filter string from the store
     */
    encodedFilters() {
      return this.$store.getters[`${this.resourceName}/currentEncodedFilters`]
    },

    /**
     * Return the initial encoded filters from the query string
     */
    initialEncodedFilters() {
      return this.$route.query[this.filterParameter] || ''
    },

    paginationComponent() {
      return `pagination-${Nova.config['pagination'] || 'links'}`
    },

    hasNextPage() {
      return Boolean(
        this.resourceResponse && this.resourceResponse.next_page_url
      )
    },

    hasPreviousPage() {
      return Boolean(
        this.resourceResponse && this.resourceResponse.prev_page_url
      )
    },

    totalPages() {
      return Math.ceil(this.allMatchingResourceCount / this.currentPerPage)
    },

    /**
     * Get the current per page value from the query string.
     */
    currentPerPage() {
      return this.perPage
    },

    /**
     * The per-page options configured for this resource.
     */
    perPageOptions() {
      if (this.resourceResponse) {
        return this.resourceResponse.per_page_options
      }
    },

    /**
     * Determine whether the pagination component should be shown.
     */
    shouldShowPagination() {
      return (
        this.disablePagination !== true &&
        this.resourceResponse &&
        this.resources.length > 0
      )
    },

    /**
     * Determine if the polling toggle button should be shown.
     */
    shouldShowPollingToggle() {
      return this.resourceInformation.showPollingToggle
    },
  },
}
</script>
