<template>
  <!--suppress JSValidateTypes -->
  <DataTable
    :value="characters"
    dataKey="id"
    :loading="isLoadingCharacters"
    sortField="createdAt"
    :sortOrder="1"
    removableSort
    v-model:filters="filters"
    :filterDisplay="showFilters && !isLoadingOptions ? 'row' : null"
    :globalFilterFields="globalFilterFieldNames"
    selectionMode="single"
    resizableColumns
    stateStorage="session"
    stateKey="datatable-state"
    scrollable
    :scrollHeight="`calc(100vh - ${elementHeights}px)`"
    size="small"
    :pt="{
      header: { id: 'datatable_header' },
      footer: { id: 'datatable_footer' },
      column: {
        headerCell: 'max-w-[8rem]',
        bodyCell: 'max-w-[8rem]'
      }
    }"
    @filter="(event) => updateFilteredCharacters(event.filteredValue)"
    @rowSelect="(event) => showDetail(event.data)"
  >
    <Column
      v-for="field in _keys(columnFields)"
      :key="field"
      :field="field"
      :header="_upperCase(field)"
      :showFilterMenu="false"
      :sortable="true"
    >
      <template #filter="{ filterModel, filterCallback }">
        <OptionsFilter
          v-if="_isMatch(characterFields[field], { type: 'autocomplete' })"
          v-model="filterModel.value"
          :field="field"
          :options="options[field]"
          :filteredOptions="filteredOptions[field]"
          @filterCallback="filterCallback"
        />

        <TextFilter
          v-else
          v-model="filters[field]"
          :field="field"
        />
      </template>

      <template #sorticon="{ sorted, sortOrder }">
        <Icon
          v-if="sorted && sortOrder === 1"
          name="ph:sort-ascending-bold"
          size="1.25rem"
        />

        <Icon
          v-else-if="sorted && sortOrder === -1"
          name="ph:sort-descending-bold"
          size="1.25rem"
        />

        <Icon
          v-else
          name="ph:arrows-down-up-bold"
          size="1.25rem"
        />
      </template>
    </Column>

    <template #header>
      <div :class="isLoadingOptions && 'hidden'">
        <div class="flex justify-between gap-4 pb-2">
          <SearchField
            v-model="filters['global'].value"
            placeholder="Search&hellip;"
            type="search"
            id="global_filter"
          />

          <Button
            variant="outlined"
            :disabled="!hasAnyFilters"
            class="px-4"
            @click="resetFilters"
          >
            <Icon
              name="ph:funnel-simple-x-bold"
              size="1.25rem"
            />

            Reset
          </Button>

          <Button
            variant="outlined"
            class="px-4"
            @click="toggleShowFilters"
          >
            <Icon
              name="ph:funnel-simple-bold"
              size="1.25rem"
            />

            <div class="flex flex-col">
              <div
                class="overflow-hidden"
                :class="showFilters && 'h-0!'"
              >
                Show
              </div>

              <div
                class="overflow-hidden"
                :class="!showFilters && 'h-0!'"
              >
                Hide
              </div>
            </div>
          </Button>
        </div>

        <CharacterFilterChips
          v-if="hasAnyFilters() && !showFilters"
          class="pb-2"
        />
      </div>
    </template>

    <template #empty>
      <div
        class="text-center text-2xl"
        :class="isLoadingCharacters && 'hidden'"
      >
        <template v-if="hasGlobalFilter()">
          <template v-if="hasAnyColumnFilters()">
            No characters matching
            <!--suppress JSUnresolvedReference -->
            <span class="italic">&ldquo;{{ filters.global.value }}&rdquo;</span>
            with the current filters.
          </template>

          <template v-else>
            No characters matching
            <!--suppress JSUnresolvedReference -->
            <span class="italic">&ldquo;{{ filters.global.value }}&rdquo;</span
            >.
          </template>
        </template>

        <template v-else>No characters match the current filters.</template>
      </div>
    </template>

    <template #loading>
      <SpinnerModal
        :visible="true"
        maskClass="bg-surface!"
      />
    </template>

    <template #footer>
      <CharacterToolbar
        :class="isLoadingCharacters && 'hidden'"
        :filteredCount="filteredCount"
        :count="count"
      />
    </template>
  </DataTable>
</template>

<script setup>
definePageMeta({ name: "characters" })

const { isSignedIn } = useAuthClient()
const filtersStore = useFiltersStore()

const {
  hasAnyColumnFilters,
  hasAnyFilters,
  hasFilterFor,
  hasGlobalFilter,
  resetFilterFor,
  resetFilters,
  toggleShowFilters
} = filtersStore

const { filters, showFilters } = storeToRefs(filtersStore)

const {
  data: characters,
  isLoading: isLoadingCharacters,
  refetch: refetchCharacters
} = useQuery(characterListQuery)

const { data: options, isLoading: isLoadingOptions } = useQuery(
  characterOptionsQuery
)

const count = computed(() => _size(characters.value))
const filteredCount = computed(() => _size(filteredCharacters.value))
const filteredCharacters = ref(_cloneDeep(characters.value))

const filteredOptions = computed(() =>
  findUniqueOptions(filteredCharacters.value)
)

const DEFAULT_ELEMENT_HEIGHTS = 160
const elementHeights = ref(DEFAULT_ELEMENT_HEIGHTS)

onMounted(() => {
  refetchCharacters()
  updateElementHeights()
})

onUpdated(() => updateElementHeights())

function updateFilteredCharacters(filteredValue) {
  filteredCharacters.value = filteredValue
}

async function showDetail({ id: characterId }) {
  if (isSignedIn.value) {
    await navigateTo({ name: "characterEdit", params: { characterId } })
  } else {
    await navigateTo({ name: "characterView", params: { characterId } })
  }
}

function updateElementHeights() {
  elementHeights.value = totalElementHeights()
}

function totalElementHeights() {
  // total height of non-datatable elements (in pixels)
  const elements = ["navbar", "datatable_header", "datatable_footer"]

  // noinspection JSUnresolvedReference
  let totalHeights = _reduce(
    elements,
    (acc, element) => {
      // noinspection JSUnresolvedReference
      return acc + document?.getElementById(element)?.offsetHeight
    },
    0
  )

  // plus 16px [--spacing(4)] navbar bottom margin
  totalHeights += 16

  return _isNaN(totalHeights) ? DEFAULT_ELEMENT_HEIGHTS : totalHeights
}
</script>

<style scoped></style>
