<template>
  <el-popover
    ref="popoverRef"
    :teleported="true"
    append-to="body"
    trigger="click"
    placement="top"
    popper-class="event-edit-select-popover"
    popper-style="width: 440px; z-index: 12000;"
    :popper-options="popperOptions"
    @hide="handleCancel"
  >
    <template #reference>
      <slot></slot>
    </template>

    <div class="w-full">
      <div class="h-32px leading-32px pl-12px bg-bg-tertiary flex justify-between items-center">
        <span>{{ title }}</span>
        <el-input
          v-model="search"
          spellcheck="false"
          style="width: 200px"
          placeholder="search..."
          size="small"
        />
      </div>

      <el-scrollbar max-height="360px">
        <div class="w-full my-12px">
          <template v-for="group in groupedOptions" :key="group.key">
            <div class="w-full mb-8px text-12px text-text-secondary font-600">
              {{ group.label }}
            </div>

            <div class="w-full mb-12px flex flex-wrap gap-x-12px gap-y-8px">
              <div
                v-for="item in group.options"
                :key="item.value"
                style="width: calc(50% - 8px)"
                class="shrink-0"
              >
                <div
                  class="w-full min-h-20px border-dashed border-1px border-border grid place-items-center py-4px text-center"
                  :class="getItemClass(item)"
                  @click="handleSelect(item)"
                >
                  {{ item.label }}
                </div>
              </div>
            </div>
          </template>

          <div v-if="!groupedOptions.length" class="w-full h-120px grid place-items-center">
            暂无数据
          </div>
        </div>
      </el-scrollbar>

      <div class="w-full flex justify-end gap-8px">
        <el-button size="small" @click="handleCancel">取消</el-button>
        <el-button size="small" type="primary" @click="handleConfirm">确定</el-button>
      </div>
    </div>
  </el-popover>
</template>

<script setup lang="ts">
import type { PopoverInstance } from 'element-plus'
import type { OptionType } from './type'

import { computed, ref, watch } from 'vue'

const emit = defineEmits<{
  (e: 'confirm', value?: OptionType[]): void
}>()

const props = withDefaults(
  defineProps<{
    title?: string
    multiple?: boolean
    options?: OptionType[]
    selected?: string | string[]
  }>(),
  {
    title: '',
    multiple: false,
    options: () => []
  }
)

const selectedList = ref<string[]>([])
const search = ref('')
const popoverRef = ref<PopoverInstance>()

const popperOptions = {
  strategy: 'fixed',
  modifiers: [
    {
      name: 'preventOverflow',
      options: {
        boundary: document.body,
        padding: 8
      }
    },
    {
      name: 'flip',
      options: {
        fallbackPlacements: ['top', 'bottom', 'right', 'left']
      }
    }
  ]
}

const normalizeSelected = (value?: string | string[]) => {
  if (!value) return []
  return Array.isArray(value) ? [...value] : [value]
}

watch(
  () => props.selected,
  (value) => {
    selectedList.value = normalizeSelected(value)
  },
  {
    immediate: true
  }
)

const filteredOptions = computed(() => {
  const keyword = search.value.trim().toLowerCase()
  const options = props.options || []

  if (!keyword) return options

  return options.filter((item) => item.label.toLowerCase().includes(keyword))
})

const groupedOptions = computed(() => {
  const groups = new Map<
    string,
    {
      key: string
      label: string
      order: number
      options: OptionType[]
    }
  >()

  filteredOptions.value.forEach((item) => {
    const key = item.groupKey || 'default'
    const label = item.groupLabel || ''
    const order = item.groupOrder ?? Number.MAX_SAFE_INTEGER
    const current = groups.get(key)

    if (current) {
      current.options.push(item)
      return
    }

    groups.set(key, {
      key,
      label,
      order,
      options: [item]
    })
  })

  return [...groups.values()].sort((a, b) => a.order - b.order)
})

const getItemClass = (item: OptionType) => {
  if (item.disabled) {
    return 'cursor-not-allowed bg-bg-secondary text-text-secondary opacity-50'
  }

  if (selectedList.value.includes(item.value)) {
    return 'cursor-pointer bg-accent-blue text-white hover:bg-accent-blue!'
  }

  return 'cursor-pointer hover:bg-bg-primary'
}

const handleSelect = (item: OptionType) => {
  if (item.disabled) return

  if (selectedList.value.includes(item.value)) {
    selectedList.value = selectedList.value.filter((value) => value !== item.value)
    return
  }

  selectedList.value = props.multiple ? [...selectedList.value, item.value] : [item.value]
}

const handleCancel = () => {
  popoverRef.value?.hide()
  selectedList.value = normalizeSelected(props.selected)
}

const handleConfirm = () => {
  const result = (props.options || []).filter((item) => selectedList.value.includes(item.value))
  handleCancel()
  setTimeout(() => {
    emit('confirm', result)
  }, 200)
}
</script>

<style lang="less">
.event-edit-select-popover.el-popper {
  z-index: 12000 !important;
  max-height: calc(100vh - 24px);
  overflow: hidden;
}
</style>
