<template>
  <div class="menu_wrap">
    <ElScrollbar
      style="
        height: calc(100% - 10px);
        min-width: 170px;
        overflow-y: auto;
        overflow-x: hidden;
        margin-right: 10px;
        border: 1px solid #ddd;
        border-radius: 5px;
      "
    >
      <div
        v-for="(item, index) in menuList"
        :key="index"
        class="menu_title"
        :class="{ active: index === activeIndex }"
        @click="setActive(index)"
      >
        {{ formatFirstName(item.name) }}
      </div>
    </ElScrollbar>
    <div style="flex: 1; display: flex; flex-direction: column">
      <slot name="header"></slot>
      <ElScrollbar
        ref="scrollBarRef"
        style="flex: 1; background: #fff; border-radius: 10px"
      >
        <div v-for="(item, index) in menuList" :key="index" class="scrollbar">
          <slot v-bind="item" :index="index"></slot>
        </div>
      </ElScrollbar>
    </div>
  </div>
</template>

<script setup lang="ts">
import { capitalize, throttle } from 'lodash-es'
import { nextTick, onBeforeUnmount, onMounted, ref, watch, type Ref } from 'vue'
import type { MenuTreeNode } from '@/types/api/role'
const props = withDefaults(
  defineProps<{
    menuList?: MenuTreeNode[]
    isActive?: boolean
  }>(),
  {
    menuList: () => [],
    isActive: false,
  },
)

const emit = defineEmits<{
  (e: 'setActive', item: MenuTreeNode, index: number): void
}>()

defineSlots<{
  header(): unknown
  default(props: MenuTreeNode & { index: number }): unknown
}>()

const scrollBarRef = ref<{
  wrapRef: Ref<HTMLElement | undefined>
  setScrollTop?: (value: number) => void
} | null>(null)
const activeIndex = ref(0)
const itemHeight = ref<number[]>([])
const notScroll = ref(false)

function getWrap(): HTMLElement | undefined {
  return scrollBarRef.value?.wrapRef;
}
function formatFirstName(value: unknown) {
  const str = String(value ?? '')
  if (/^[a-zA-Z]+$/.test(str)) {
    return capitalize(str)
  }
  return str
}

function setHeight() {
  nextTick(() => {
    if (!props.isActive) return
    const wrap = getWrap()
    if (!wrap) return
    const list = wrap.querySelectorAll('.scrollbar')
    itemHeight.value = []
    let h = 0
    itemHeight.value.push(h)
    list.forEach((el) => {
      h += (el as HTMLElement).clientHeight
      itemHeight.value.push(h)
    })
  })
}

function setActive(index: number) {
  notScroll.value = true
  activeIndex.value = index
  const wrap = getWrap()
  if (wrap) {
    wrap.scrollTop = itemHeight.value[index] ?? 0
  }
  window.setTimeout(() => {
    notScroll.value = false
  }, 200)
  emit('setActive', props.menuList[index], index)
}

const scrollMenu = throttle((event: Event) => {
  if (notScroll.value) return
  const target = event.target as HTMLElement
  const newy = target.scrollTop + 100
  const heights = itemHeight.value
  for (let i = 0; i < heights.length - 1; i++) {
    const h1 = heights[i]
    const h2 = heights[i + 1]
    if (newy >= h1 && newy < h2) {
      if (activeIndex.value !== i) {
        activeIndex.value = i
      }
      return
    }
  }
}, 200)

function onScroll(event: Event) {
  scrollMenu(event)
}

onMounted(() => {
  const wrap = getWrap()
  if (wrap) {
    wrap.addEventListener('scroll', onScroll)
  }
  setHeight()
})

onBeforeUnmount(() => {
  const wrap = getWrap()
  if (wrap) {
    wrap.removeEventListener('scroll', onScroll)
  }
})

watch(
  () => props.menuList,
  (value, oldValue) => {
    if (value !== oldValue) setHeight()
  },
)

watch(
  () => props.isActive,
  (v) => {
    if (v && itemHeight.value.length === 0) {
      setHeight()
    }
  },
)
</script>

<style scoped>
.menu_wrap {
  height: 100%;
  display: flex;
}
.menu_title {
  padding-left: 10px;
  height: 36px;
  line-height: 36px;
  font-weight: bold;
  font-size: 14px;
  cursor: pointer;
}
.menu_title:hover,
.menu_title.active {
  color: #333333;
}
.menu_title:hover {
  background: #f5f7fa;
}
.menu_title.active {
  background: #409eff;
}
.scrollbar {
  margin-bottom: 10px;
  border-radius: 10px;
  border: 1px solid #ddd;
}
</style>
