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

defineOptions({ name: 'CustomIconSelect' });

interface IconGroup {
  label: string;
  value: string;
  icons: string[];
}

interface Props {
  value: string;
  icons?: string[];
  iconGroups?: IconGroup[];
  emptyIcon?: string;
  title?: string;
  placeholder?: string;
  emptyDescription?: string;
  pageSize?: number;
}

const props = withDefaults(defineProps<Props>(), {
  icons: () => [],
  iconGroups: () => [],
  emptyIcon: 'mdi:apps',
  title: '选择图标',
  placeholder: '点击选择图标',
  emptyDescription: '没有找到可选图标',
  pageSize: 48
});

interface Emits {
  (e: 'update:value', val: string): void;
}

const emit = defineEmits<Emits>();

const dialogVisible = ref(false);
const activeGroup = ref('all');
const currentPage = ref(1);

const modelValue = computed({
  get() {
    return props.value;
  },
  set(val: string) {
    emit('update:value', val);
  }
});

const selectedIcon = computed(() => modelValue.value || props.emptyIcon);

const normalizedGroups = computed<IconGroup[]>(() => {
  if (props.iconGroups.length) {
    const groups = props.iconGroups.map(group => ({
      ...group,
      icons: Array.from(new Set(group.icons))
    }));

    return [
      {
        label: '全部',
        value: 'all',
        icons: Array.from(new Set(groups.flatMap(group => group.icons)))
      },
      ...groups
    ];
  }

  return [
    {
      label: '全部',
      value: 'all',
      icons: Array.from(new Set(props.icons))
    }
  ];
});

const currentGroup = computed(
  () => normalizedGroups.value.find(group => group.value === activeGroup.value) ?? normalizedGroups.value[0]
);

const groupIcons = computed(() => currentGroup.value?.icons ?? []);

const pagedIcons = computed(() => {
  const startIndex = (currentPage.value - 1) * props.pageSize;

  return groupIcons.value.slice(startIndex, startIndex + props.pageSize);
});

watch(
  normalizedGroups,
  groups => {
    if (!groups.some(group => group.value === activeGroup.value)) {
      activeGroup.value = groups[0]?.value ?? 'all';
    }
  },
  { immediate: true }
);

watch(activeGroup, () => {
  currentPage.value = 1;
});

function openDialog() {
  activeGroup.value = normalizedGroups.value[0]?.value ?? 'all';
  currentPage.value = 1;
  dialogVisible.value = true;
}

function handleChange(iconItem: string) {
  modelValue.value = iconItem;
  dialogVisible.value = false;
}
</script>

<template>
  <div class="custom-icon-select">
    <div class="custom-icon-select__trigger" @click="openDialog">
      <ElInput :model-value="modelValue" readonly :placeholder="placeholder">
        <template #suffix>
          <SvgIcon :icon="selectedIcon" class="text-22px" />
        </template>
      </ElInput>
    </div>

    <BusinessFormDialog
      v-model="dialogVisible"
      :title="title"
      preset="lg"
      width="720px"
      :show-footer="false"
      :scrollbar="false"
    >
      <div class="custom-icon-select__content">
        <ElTabs v-model="activeGroup" class="custom-icon-select__tabs">
          <ElTabPane
            v-for="group in normalizedGroups"
            :key="group.value"
            :label="`${group.label} (${group.icons.length})`"
            :name="group.value"
          />
        </ElTabs>

        <div v-if="pagedIcons.length" class="custom-icon-select__grid">
          <button
            v-for="iconItem in pagedIcons"
            :key="iconItem"
            type="button"
            class="custom-icon-select__item"
            :class="{ 'custom-icon-select__item--active': modelValue === iconItem }"
            :title="iconItem"
            @click="handleChange(iconItem)"
          >
            <SvgIcon :icon="iconItem" class="text-24px" />
            <span class="custom-icon-select__name">{{ iconItem }}</span>
          </button>
        </div>
        <ElEmpty v-else :description="emptyDescription" class="py-24px" />

        <div v-if="groupIcons.length > props.pageSize" class="custom-icon-select__pagination">
          <ElPagination
            v-model:current-page="currentPage"
            background
            layout="prev, pager, next"
            :page-size="props.pageSize"
            :total="groupIcons.length"
          />
        </div>
      </div>
    </BusinessFormDialog>
  </div>
</template>

<style lang="scss" scoped>
.custom-icon-select__trigger {
  cursor: pointer;
}

.custom-icon-select__content {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.custom-icon-select__grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 12px;
  min-height: 360px;
  max-height: 420px;
  overflow-y: auto;
  padding-right: 4px;
}

.custom-icon-select__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  border: 1px solid var(--el-border-color);
  border-radius: 12px;
  background: var(--el-fill-color-blank);
  padding: 12px 8px;
  transition:
    border-color 0.2s ease,
    background-color 0.2s ease,
    transform 0.2s ease;
}

.custom-icon-select__item:hover {
  border-color: var(--el-color-primary);
  background: var(--el-color-primary-light-9);
  transform: translateY(-1px);
}

.custom-icon-select__item--active {
  border-color: var(--el-color-primary);
  background: var(--el-color-primary-light-9);
  box-shadow: 0 0 0 1px var(--el-color-primary-light-5) inset;
}

.custom-icon-select__name {
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
  font-size: 12px;
  color: var(--el-text-color-secondary);
}

.custom-icon-select__pagination {
  display: flex;
  justify-content: flex-end;
  padding-top: 4px;
}
</style>
