<template>
	<div class="search panel-item" @click="searchPanelOpen">
		<search-outlined />
	</div>
	<xn-form-container
		title="搜索"
		:visible="searchActive"
		:closable="false"
		:footer="null"
		:width="600"
		destroyOnClose
		dialogClass="searchModal"
		:bodyStyle="{ maxHeight: '520px', overflow: 'auto', padding: '14px' }"
		@close="searchPanelClose"
	>
		<div
			@keyup.up="handleKeyUp"
			@keyup.down="handleKeyDown"
			@keyup.enter="handleKeyEnter"
			@click.self="handlePanelClick"
		>
			<a-input
				ref="inputRef"
				v-model="searchText"
				class="search-box"
				style="width: 100%"
				allowClear
				placeholder="搜索页面（支持拼音检索）"
				@change="querySearch"
			>
				<template #prefix>
					<search-outlined />
				</template>
			</a-input>
			<a-card
				:body-style="{ padding: '0 0' }"
				hoverable
				@mouseenter="onCardIn"
				@mouseleave="onCardOut"
				@keypress.up="handleKeyUp"
				@keypress.down="handleKeyDown"
				style="margin: 10px 0"
			>
				<div ref="cardListRef" class="search-card beauty-scroll">
					<a-list size="small" :data-source="resultsList">
						<template #renderItem="{ item, index }">
							<a-list-item
								@click="handleSelect(item.fullPath)"
								@mouseover="onCardItemHover(index)"
								:class="{ active: index === cardIndex }"
								style="padding-right: 10px"
							>
								<template #actions>
									<a>
										<enter-outlined />
									</a>
								</template>
								<a-list-item-meta :description="item.fullName">
									<template #title>
										<a>{{ item.name }}</a>
									</template>
									<template #avatar>
										<a-avatar style="color: var(--text-color); background-color: transparent" :type="item.icon">
											<template #icon>
												<component :is="item.icon" />
											</template>
										</a-avatar>
									</template>
								</a-list-item-meta>
							</a-list-item>
						</template>
					</a-list>
				</div>
			</a-card>
			<div class="search-tips">
				<span class="key">S</span>
				<span class="tips">打开搜索面板</span>

				<span class="key">
					<arrow-up-outlined />
				</span>
				<span class="key">
					<arrow-down-outlined />
				</span>
				<span class="tips">选择</span>

				<span class="key">
					<enter-outlined />
				</span>
				<span class="tips">确认</span>

				<span class="key left">Esc</span>
				<span class="tips">关闭</span>
			</div>
		</div>
	</xn-form-container>
</template>

<script setup>
	import Fuse from 'fuse.js'
	import { searchStore } from '@/store'
	import { useRouter, useRoute } from 'vue-router'
	import hotkeys from 'hotkeys-js'

	const route = useRoute()
	const router = useRouter()
	const searchText = ref('')
	const cardIndex = ref(0)
	const results = ref([])
	const search = searchStore()
	const inputRef = ref()
	const cardListRef = ref()
	const setActive = search.setActive
	const toggleActive = search.toggleActive
	const pool = computed(() => {
		return search.pool
	})
	const searchActive = computed(() => {
		return search.active
	})
	const searchHotkey = computed(() => {
		return search.hotkey
	})
	const mixinSearch = computed(() => {
		return mixinSearch
	})
	// 这份数据是展示在搜索面板下面的
	const resultsList = computed(() => {
		return results.value.length === 0 || searchText.value === '' ? pool.value : results.value
	})
	// 根据 pool 更新 fuse 实例
	const fuse = computed(() => {
		return new Fuse(pool.value, {
			shouldSort: true, // 按分数对结果列表进行排序
			threshold: 0.6, // 什么时候放弃
			location: 0, // 大致位置
			distance: 100, // 接近程度
			minMatchCharLength: 1, // 匹配长度
			keys: ['name', 'namePinyin', 'namePinyinFirst']
		})
	})
	// 过滤选项 这个方法在每次输入框的值发生变化时会触发
	const querySearch = (e) => {
		let queryString = e.target.value || ''
		const result = queryString && fuse.value.search(queryString).map((e) => e.item)
		searchText.value = queryString
		results.value = result
	}
	// 聚焦输入框
	const focus = () => {
		searchText.value = ''
		setTimeout(() => {
			if (inputRef.value) {
				inputRef.value.focus()
			}
			// 还原
			searchText.value = ''
			results.value = []
		}, 300)
	}
	const handleKeyEnter = () => {
		let idx = cardIndex.value
		if (resultsList.value[idx]) {
			handleSelect(resultsList.value[idx].fullPath)
		}
	}
	const handleKeyUp = () => {
		handleKeyUpOrDown(true)
	}
	const handleKeyDown = () => {
		handleKeyUpOrDown(false)
	}
	const handleKeyUpOrDown = (up) => {
		let len = resultsList.value.length - 1
		let idx = cardIndex.value
		if (up) {
			// 上
			if (idx > 0) {
				idx--
			} else {
				idx = len
			}
		} else {
			// 下
			if (idx < len) {
				idx++
			} else {
				idx = 0
			}
		}
		cardIndex.value = idx
		if (cardListRef.value.getElementsByClassName('ant-list-item')[idx]) {
			cardListRef.value.scrollTop = cardListRef.value.getElementsByClassName('ant-list-item')[idx].offsetTop
		} else {
			cardListRef.value.scrollTop = 0
		}
	}
	const onCardIn = () => {
		inputRef.value.activated = false
		inputRef.value.blur()
	}
	const onCardOut = () => {
		cardIndex.value = -1
	}
	const onCardItemHover = (index) => {
		cardIndex.value = index
	}
	// 接收用户在下拉菜单中选中事件
	const handleSelect = (path) => {
		// 如果用户选择的就是当前页面 就直接关闭搜索面板
		if (path === route.path) {
			searchPanelClose()
			return
		}
		router.push({ path })
		searchPanelClose()
	}
	// 接收用户点击空白区域的关闭
	const handlePanelClick = (e) => {
		if ('INPUT' !== e.target.tagName) {
			searchPanelClose()
		}
	}
	// 打开搜索面板
	const searchPanelOpen = () => {
		if (!searchActive.value) {
			setActive(true)
			setTimeout(() => {
				if (inputRef.value) {
					inputRef.value.focus()
				}
			}, 300)
		}
	}
	// 关闭搜索面板
	const searchPanelClose = () => {
		if (!searchActive.value) {
			return false
		}
		setActive(false)
		results.value = []
		if (inputRef.value.activated) {
			inputRef.value.activated = false
		}
	}
	onMounted(() => {
		// 绑定搜索功能快捷键 [ 打开 ]
		hotkeys(searchHotkey.value.open, (event) => {
			event.preventDefault()
			searchPanelOpen()
		})
		// 绑定搜索功能快捷键 [ 关闭 ]
		hotkeys(searchHotkey.value.close, (event) => {
			event.preventDefault()
			searchPanelClose()
		})
	})
	// 挂载
	hotkeys.unbind(searchHotkey.value.open)
	hotkeys.unbind(searchHotkey.value.close)
</script>

<style lang="less" scoped>
	:deep(.ant-input) {
		height: 35px;
	}
	:deep(.ant-input:not(:first-child)) {
		padding-left: 10px;
	}
	:deep(.ant-input-prefix) {
		font-size: 20px;
	}
	:deep(.ant-list-sm .ant-list-item) {
		padding: 4px 16px;
	}
	:deep(.ant-list-item-meta) {
		align-items: center;
	}
	:deep(.ant-list-item.active) {
		background-color: var(--primary-1);
	}
	.search-box {
		width: 100%;
	}
	.beauty-scroll {
		scrollbar-color: var(--primary-color) var(--primary-2);
		scrollbar-width: thin;
		-ms-overflow-style: none;
		position: relative;
		&::-webkit-scrollbar {
			width: 3px;
			height: 1px;
		}
		&::-webkit-scrollbar-thumb {
			border-radius: 3px;
			background: var(--primary-color);
		}
		&::-webkit-scrollbar-track {
			-webkit-box-shadow: inset 0 0 1px rgba(0, 0, 0, 0);
			border-radius: 3px;
			background: var(--primary-3);
		}
	}
	.search-card {
		height: 380px;
		overflow: hidden;
		overflow-y: scroll;
	}
	.search-tips {
		display: flex;
		border-top: 1px solid var(--component-background);
		padding-top: 10px;
		.tips {
			margin-right: 10px;
		}
		.key {
			width: 30px;
			height: 20px;
			line-height: 20px;
			text-align: center;
			padding-bottom: 2px;
			margin: 0px 4px;
			border-radius: 2px;
			box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff, 0 1px 2px 1px #1e235a66;
			font-weight: bold;
		}
	}
</style>
