<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue'

export type FilterSection =
  | {
      key: string
      title: string
      type: 'range'
      unit?: string
      minPlaceholder?: string
      maxPlaceholder?: string
    }
  | {
      key: string
      title: string
      type: 'chips'
      options: string[]
    }

type SelectedFilters = Record<string, string[]>
type RangeFilters = Record<string, { min: string; max: string }>
type RangePresets = Record<string, Array<{ label: string; min: string; max: string }>>

const props = defineProps<{
  show: boolean
  selectedFilters: SelectedFilters
  rangeFilters: RangeFilters
  sections: FilterSection[]
  rangePresets: RangePresets
}>()

const emit = defineEmits<{
  'update:show': [value: boolean]
  'update:selectedFilters': [value: SelectedFilters]
  'update:rangeFilters': [value: RangeFilters]
  reset: []
}>()

const activeCount = computed(() => {
  const chipCount = Object.values(props.selectedFilters).reduce(
    (sum, values) => sum + values.length,
    0
  )
  const rangeCount = Object.values(props.rangeFilters).filter(
    range => range.min || range.max
  ).length
  return chipCount + rangeCount
})

const visibleSections = computed(() =>
  props.sections.filter(section => section.type === 'range' || section.options.length)
)
const activeSectionKey = ref('')
const navRef = ref<HTMLElement | null>(null)
const contentRef = ref<HTMLElement | null>(null)

watch(
  [() => props.show, () => visibleSections.value.map(section => section.key).join(',')],
  async ([show]) => {
    if (!show) return
    activeSectionKey.value = visibleSections.value[0]?.key || ''
    await nextTick()
    if (contentRef.value) contentRef.value.scrollTop = 0
  },
  { immediate: true }
)

watch(activeSectionKey, async sectionKey => {
  if (!sectionKey) return
  await nextTick()
  centerActiveNavItem(sectionKey)
})

function sectionCount(section: FilterSection) {
  if (section.type === 'chips') return props.selectedFilters[section.key]?.length || 0
  const range = props.rangeFilters[section.key]
  return range?.min || range?.max ? 1 : 0
}

function toggleChip(sectionKey: string, value: string) {
  const selected = props.selectedFilters[sectionKey] || []
  const next = selected.includes(value)
    ? selected.filter(item => item !== value)
    : [...selected, value]
  emit('update:selectedFilters', { ...props.selectedFilters, [sectionKey]: next })
}

function updateRange(sectionKey: string, side: 'min' | 'max', value: string) {
  const current = props.rangeFilters[sectionKey] || { min: '', max: '' }
  emit('update:rangeFilters', {
    ...props.rangeFilters,
    [sectionKey]: { ...current, [side]: value },
  })
}

function applyRangePreset(sectionKey: string, min: string, max: string) {
  emit('update:rangeFilters', {
    ...props.rangeFilters,
    [sectionKey]: { min, max },
  })
}

function isRangePresetActive(sectionKey: string, min: string, max: string) {
  const range = props.rangeFilters[sectionKey]
  return range?.min === min && range?.max === max
}

function resetFilters() {
  emit('reset')
}

function scrollToSection(sectionKey: string) {
  activeSectionKey.value = sectionKey
  const body = contentRef.value
  if (!body) return
  const section = findSectionElement(sectionKey)
  if (!section) return
  body.scrollTo({ top: Math.max(sectionTopInBody(section, body) - 8, 0), behavior: 'auto' })
}

function syncActiveSection() {
  const body = contentRef.value
  if (!body) return
  const sections = Array.from(body.querySelectorAll<HTMLElement>('[data-filter-section]'))
  const currentTop = body.scrollTop + 28
  let current = sections[0]
  for (const section of sections) {
    if (sectionTopInBody(section, body) <= currentTop) {
      current = section
      continue
    }
    break
  }
  activeSectionKey.value = current?.dataset.filterSection || visibleSections.value[0]?.key || ''
}

function centerActiveNavItem(sectionKey: string) {
  const nav = navRef.value
  if (!nav) return
  const navItem =
    Array.from(nav.querySelectorAll<HTMLElement>('[data-filter-nav]')).find(
      item => item.dataset.filterNav === sectionKey
    ) || null
  if (!navItem) return
  const itemTop = navItem.getBoundingClientRect().top - nav.getBoundingClientRect().top
  const targetTop = nav.scrollTop + itemTop - nav.clientHeight / 2 + navItem.clientHeight / 2
  nav.scrollTo({ top: Math.max(targetTop, 0), behavior: 'auto' })
}

function findSectionElement(sectionKey: string) {
  const body = contentRef.value
  if (!body) return null
  return (
    Array.from(body.querySelectorAll<HTMLElement>('[data-filter-section]')).find(
      section => section.dataset.filterSection === sectionKey
    ) || null
  )
}

function sectionTopInBody(section: HTMLElement, body: HTMLElement) {
  return section.getBoundingClientRect().top - body.getBoundingClientRect().top + body.scrollTop
}
</script>

<template>
  <van-popup
    :show="show"
    position="bottom"
    round
    class="mobile-filter-sheet"
    @update:show="emit('update:show', $event)"
  >
    <header class="sheet-header">
      <span></span>
      <strong>筛选</strong>
      <button
        class="sheet-close"
        type="button"
        aria-label="关闭筛选"
        @click="emit('update:show', false)"
      >
        <van-icon name="cross" :size="24" />
      </button>
    </header>

    <div class="sheet-body">
      <nav ref="navRef" class="filter-nav" aria-label="筛选分类">
        <button
          v-for="section in visibleSections"
          :key="section.key"
          type="button"
          class="filter-nav-item"
          :data-filter-nav="section.key"
          :class="{ active: activeSectionKey === section.key }"
          @click="scrollToSection(section.key)"
        >
          <span>{{ section.title }}</span>
          <em v-if="sectionCount(section)">{{ sectionCount(section) }}</em>
        </button>
      </nav>

      <div ref="contentRef" class="filter-content" @scroll.passive="syncActiveSection">
        <section
          v-for="section in visibleSections"
          :key="section.key"
          class="filter-section"
          :data-filter-section="section.key"
        >
          <div class="filter-title">
            <h3>{{ section.title }}</h3>
            <span v-if="section.type === 'range' && section.unit">单位：{{ section.unit }}</span>
          </div>

          <template v-if="section.type === 'range'">
            <div class="range-row">
              <input
                :value="rangeFilters[section.key]?.min || ''"
                type="number"
                inputmode="decimal"
                :placeholder="section.minPlaceholder || '最低'"
                @input="updateRange(section.key, 'min', ($event.target as HTMLInputElement).value)"
              />
              <span></span>
              <input
                :value="rangeFilters[section.key]?.max || ''"
                type="number"
                inputmode="decimal"
                :placeholder="section.maxPlaceholder || '最高'"
                @input="updateRange(section.key, 'max', ($event.target as HTMLInputElement).value)"
              />
            </div>
            <div v-if="rangePresets[section.key]?.length" class="chip-grid">
              <button
                v-for="preset in rangePresets[section.key]"
                :key="`${section.key}-${preset.label}`"
                type="button"
                :class="{ active: isRangePresetActive(section.key, preset.min, preset.max) }"
                @click="applyRangePreset(section.key, preset.min, preset.max)"
              >
                {{ preset.label }}
              </button>
            </div>
          </template>

          <div v-else class="chip-grid">
            <button
              v-for="option in section.options"
              :key="`${section.key}-${option}`"
              type="button"
              :class="{ active: selectedFilters[section.key]?.includes(option) }"
              @click="toggleChip(section.key, option)"
            >
              {{ option }}
            </button>
          </div>
        </section>
      </div>
    </div>

    <footer class="sheet-footer">
      <button type="button" @click="resetFilters">重置</button>
      <button type="button" class="primary" @click="emit('update:show', false)">
        确定{{ activeCount ? ` (${activeCount})` : '' }}
      </button>
    </footer>
  </van-popup>
</template>

<style scoped>
.mobile-filter-sheet {
  display: grid;
  grid-template-rows: 50px minmax(0, 1fr) auto;
  height: 86vh;
  max-height: 86vh;
  overflow: hidden;
  border-radius: 16px 16px 0 0;
  background: #fff;
}

.sheet-header,
.sheet-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 16px;
  background: #fff;
}

.sheet-header {
  display: grid;
  grid-template-columns: 36px minmax(0, 1fr) 36px;
  border-bottom: 1px solid #edf2f7;
  padding: 0 12px;
  text-align: center;
}

.sheet-header strong {
  color: #17233d;
  font-size: 17px;
  font-weight: 900;
}

.sheet-header button,
.sheet-footer button {
  border: none;
  background: transparent;
  color: #ff6a00;
  font-weight: 700;
}

.sheet-close {
  display: grid;
  width: 36px;
  height: 36px;
  place-items: center;
  border-radius: 999px;
  color: #222;
}

.sheet-body {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  min-height: 0;
  overflow: hidden;
  background: #fff;
}

.filter-nav {
  min-height: 0;
  overflow-y: auto;
  background: #f6f6f6;
  scrollbar-width: none;
}

.filter-nav::-webkit-scrollbar,
.filter-content::-webkit-scrollbar {
  display: none;
}

.filter-nav-item {
  position: relative;
  display: grid;
  width: 100%;
  min-height: 48px;
  place-items: center;
  gap: 3px;
  padding: 6px 6px;
  border: none;
  border-bottom: 1px solid #eeeeee;
  background: transparent;
  color: #222;
  font-size: 13px;
  line-height: 1.25;
}

.filter-nav-item.active {
  background: #fff;
  color: #ff6a00;
  font-weight: 800;
}

.filter-nav-item.active::before {
  position: absolute;
  left: 0;
  top: 50%;
  width: 3px;
  height: 20px;
  border-radius: 999px;
  background: #ff6a00;
  content: '';
  transform: translateY(-50%);
}

.filter-nav-item span {
  display: block;
  max-width: 100%;
  overflow-wrap: anywhere;
}

.filter-nav-item em {
  display: grid;
  min-width: 15px;
  height: 15px;
  place-items: center;
  border-radius: 999px;
  background: #ff6a00;
  color: #fff;
  font-size: 10px;
  font-style: normal;
  line-height: 1;
}

.filter-content {
  min-height: 0;
  overflow-y: auto;
  background: #fff;
}

.filter-section {
  padding: 14px 14px 16px;
  border-bottom: 1px solid #f0f0f0;
}

.filter-title {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 10px;
}

.filter-title h3 {
  margin: 0;
  color: #2b2b2b;
  font-size: 15px;
  font-weight: 900;
}

.filter-title span {
  color: #8b9cb5;
  font-size: 11px;
}

.chip-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 7px;
}

.chip-grid button {
  min-height: 34px;
  padding: 0 7px;
  border: 1px solid transparent;
  border-radius: 8px;
  background: #f5f5f5;
  color: #666;
  font-size: 13px;
}

.chip-grid button.active {
  border-color: #ff6a00;
  background: #fff7ed;
  color: #c2410c;
  font-weight: 700;
}

.range-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 20px minmax(0, 1fr);
  align-items: center;
  margin-bottom: 10px;
}

.range-row span {
  height: 1px;
  background: #cbd5e1;
}

.range-row input {
  min-width: 0;
  height: 36px;
  padding: 0 10px;
  border: 1px solid #eeeeee;
  border-radius: 8px;
  background: #fff;
  color: #333;
  font-size: 13px;
  text-align: center;
}

.sheet-footer {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  border-top: 1px solid #edf2f7;
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
}

.sheet-footer button {
  height: 38px;
  border: 1px solid #eeeeee;
  border-radius: 10px;
  background: #fff;
  color: #222;
  font-size: 15px;
  font-weight: 800;
}

.sheet-footer .primary {
  min-width: 0;
  height: 38px;
  border-color: #ffcc00;
  border-radius: 10px;
  background: #ff6a00;
  color: #fff;
}

@media (max-width: 360px) {
  .sheet-body {
    grid-template-columns: 88px minmax(0, 1fr);
  }

  .filter-nav-item {
    font-size: 12px;
  }

  .filter-section {
    padding-right: 12px;
    padding-left: 12px;
  }
}
</style>
