<template>
  <div class="filters-wrapper">
    <div class="filters-row">
      <v-text-field
        v-model="pkgbase"
        aria-label="Search packages by name"
        class="filter-search"
        clearable
        density="compact"
        hide-details
        label="Search"
        prepend-inner-icon="mdi-magnify"
        variant="outlined" />

      <v-select
        v-model="repo"
        :items="REPO_ITEMS"
        aria-label="Filter by repository"
        class="filter-select"
        clearable
        density="compact"
        hide-details
        label="Repository"
        variant="outlined" />

      <v-select
        v-model="status"
        :items="STATUS_ITEMS"
        aria-label="Filter by package status"
        chips
        class="filter-select filter-select--status"
        closable-chips
        density="compact"
        hide-details
        item-title="title"
        item-value="value"
        label="Status"
        multiple
        return-object
        variant="outlined" />

      <v-switch
        v-model="exact"
        aria-label="Toggle exact search matching"
        class="filter-switch"
        color="primary"
        density="compact"
        hide-details
        label="Exact" />
    </div>

    <v-pagination
      v-model="page"
      :length="totalPages"
      :total-visible="mobile ? 3 : 5"
      active-color="primary"
      class="filter-pagination"
      density="compact"
      rounded
      start="1"
      variant="text" />
  </div>
</template>

<script lang="ts" setup>
import { useDisplay } from 'vuetify'
import { REPO_ITEMS, STATUS_ITEMS } from '@/config/constants'
import { usePackagesStore } from '@/stores'
import { computed, onMounted, ref, watch } from 'vue'
import { components } from '@/api'
import { useDebounce } from '@/composables/useDebounce'

const { mobile } = useDisplay()
const packagesStore = usePackagesStore()
const { debouncedFn: debouncedUpdateFilter, cancel: cancelDebounce } = useDebounce(
  () => updateFilter(),
  300
)

const page = ref<number>(1)
const pkgbase = ref<string>()
const repo = ref<string>()
const status = ref<{ title: string; value: string }[]>([])
const exact = ref<boolean>()

const totalPages = computed(() => Math.ceil(packagesStore.state.total / packagesStore.state.limit))

const updateFilter = (pageVal?: number) => {
  if (packagesStore.state.loading) return

  if (pageVal) {
    page.value = pageVal
  } else {
    page.value = 1
  }

  packagesStore.setFilters(
    {
      exact: exact.value,
      status: status.value.map(
        (state) => state.value as components['schemas']['Package']['status']
      ),
      pkgbase: pkgbase.value !== null ? pkgbase.value : undefined,
      repo: repo.value
    },
    page.value
  )
}

const initFilters = () => {
  page.value = packagesStore.state.offset / packagesStore.state.limit + 1
  pkgbase.value = packagesStore.state.filters.pkgbase
  repo.value = packagesStore.state.filters.repo
  exact.value = packagesStore.state.filters.exact
  if (packagesStore.state.filters.status) {
    for (const state of packagesStore.state.filters.status) {
      if (state) {
        status.value.push({ title: state.toUpperCase(), value: state.toLowerCase() })
      }
    }
  }
}

watch(
  () => page.value,
  (pageVal) => {
    packagesStore.goToPage(pageVal)
  }
)

// Watcher for pkgbase with debounce
watch(
  () => pkgbase.value,
  () => {
    debouncedUpdateFilter()
  }
)

// Watcher for other filters (repo, status, exact) without debounce
watch(
  [repo, status, exact],
  () => {
    // Cancel pending pkgbase debounce if any
    cancelDebounce()
    updateFilter()
  },
  { deep: true }
)

onMounted(() => {
  initFilters()
})
</script>

<style scoped>
.filters-wrapper {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

.filters-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  flex: 1;
}

.filter-search {
  width: 200px;
  flex-shrink: 0;
}

.filter-select {
  width: 140px;
  flex-shrink: 0;
}

.filter-select--status {
  width: 200px;
}

.filter-switch {
  flex-shrink: 0;
}

.filter-switch :deep(.v-label) {
  font-size: var(--font-size-sm);
  color: var(--color-text-secondary);
}

.filter-pagination {
  flex-shrink: 0;
}

.filter-pagination :deep(.v-pagination__item) {
  font-size: var(--font-size-sm);
}

.filter-pagination :deep(.v-btn--active) {
  background: var(--color-brand-primary) !important;
  color: white !important;
}

/* Vuetify field overrides */
.filters-row :deep(.v-field) {
  border-radius: var(--radius-md);
  background: var(--color-bg-secondary);
}

.filters-row :deep(.v-field__prepend-inner) {
  color: var(--color-text-muted);
}

.filters-row :deep(.v-chip) {
  height: 22px;
  font-size: var(--font-size-xs);
}

@media (max-width: 960px) {
  .filters-wrapper {
    flex-direction: column;
    align-items: stretch;
  }

  .filters-row {
    width: 100%;
  }

  .filter-search,
  .filter-select,
  .filter-select--status {
    flex: 1;
    min-width: 120px;
    width: auto;
  }

  .filter-pagination {
    justify-content: center;
  }
}

@media (max-width: 600px) {
  .filters-row {
    gap: var(--space-2);
  }

  .filter-search,
  .filter-select,
  .filter-select--status {
    width: 100%;
    flex: none;
  }
}
</style>
