<script setup lang="ts">
const props = withDefaults(
	defineProps<{
		mode?: "h" | "v" | "horizontal" | "vertical" // 布局方向
		isDrag?: boolean // 是否可以拖拽
		size?: number | string // 初始尺寸，支持像素或百分比(如 '50%')
		minSize?: number | string // 最小尺寸，支持像素或百分比(如 '10%')
		maxSize?: number | string // 最大尺寸，支持像素或百分比(如 '80%')
		containerSize?: string | number // 容器尺寸
		panelStyle?: object // 面板样式，支持内联样式
		panelItemStyle?: object // 面板项样式，支持内联样式
		firstPanelItemStyle?: object // 第1个面板项样式，支持内联样式
		secondPanelItemStyle?: object // 第2个面板项样式，支持内联样式
		handlerBg?: string // 拖拽条背景色
		handlerHoverBg?: string // 拖拽条悬停背景色
		handlerWidth?: number // 拖拽条宽度
		handlerMargin?: number // 拖拽条边距
	}>(),
	{
		mode: "h",
		isDrag: true,
		size: 200,
		minSize: 100,
		maxSize: 2000,
		containerSize: "100%",
		handlerBg: "#e5e7eb",
		handlerHoverBg: "#409eff",
		handlerWidth: 6,
		handlerMargin: 2
	}
)
const emits = defineEmits<{
	(e: "dragComplete", n1: number, n2: number): void
}>()

const panelRef = ref(null)
const isDragging = ref(false)
const parentSize = ref(0)
const firstPanelSize = ref<number>(
	typeof props.size === "number" ? props.size : parseFloat(props.size as string) || 200
)

const secondPanelSize = computed(() =>
	Math.floor(
		parentSize.value - firstPanelSize.value - (props.handlerWidth || 6) - (props.handlerMargin || 2)
	)
)

const validatePositive = (value, name) => {
	const num = Number(value)
	if (!isNaN(num) && num < 0) {
		throw new Error(`VbSplitPanel：${name} 不能小于 0，当前值：${value}`)
	}
}

const realMode = computed(() => {
	const m = props.mode
	return m === "h" || m === "horizontal" ? "horizontal" : "vertical"
})

// 解析尺寸值，将百分比转换为像素值
const parseSizeValue = (value: number | string, parentSize: number): number => {
	if (typeof value === "string" && value.endsWith("%")) {
		const percentage = parseFloat(value)

		if (!isNaN(percentage)) {
			if (percentage > 100) {
				console.warn(`VbSplitPanel：${value} 超出范围，已设置为 90%`)
				return Math.floor((parentSize * 90) / 100)
			} else if (percentage < 0) {
				console.warn(`VbSplitPanel：${value} 不能小于 0，已设置为 10%`)
				return Math.floor((parentSize * 10) / 100)
			} else {
				return Math.floor((parentSize * percentage) / 100)
			}
		}
	}
	return typeof value === "number" ? value : parseFloat(value) || 0
}

// 获取父元素拖拽方向尺寸
const getParentSize = () => {
	if (!panelRef.value?.parentElement) return
	const parent = panelRef.value.parentElement
	parent.style.userSelect = "none"
	const size = realMode.value === "horizontal" ? parent.offsetWidth : parent.offsetHeight

	validatePositive(size, `拖拽父元素${realMode.value === "horizontal" ? "水平" : "垂直"}方向尺寸`)
	parentSize.value = Math.floor(size)
}

// 容器样式
const containerStyle = computed(() => {
	const isH = realMode.value === "horizontal"
	const sizeProp = isH ? "width" : "height"
	const crossProp = isH ? "height" : "width"
	const val = props.containerSize

	validatePositive(val, "size")

	let finalSize
	if (val === "auto") {
		if (parentSize.value < 0) {
			throw new Error(`SplitPanel：size=auto，但父元素${sizeProp}为 0，无法渲染`)
		}
		finalSize = `${parentSize.value}px`
	} else if (!isNaN(Number(val))) {
		finalSize = `${val}px`
	} else {
		finalSize = val
	}

	return {
		...(props.panelStyle || {}),
		"--split-handler-bg": props.handlerBg || "#e5e7eb",
		"--split-handler-hover-bg": props.isDrag
			? props.handlerHoverBg || "#409eff"
			: "var(--split-handler-bg)",
		"--split-handler-width": (props.handlerWidth || 6) + "px",
		"--split-handler-margin": (props.handlerMargin || 2) + "px",
		"--split-handler-cursor": props.isDrag
			? realMode.value === "horizontal"
				? "col-resize"
				: "row-resize"
			: "default",
		[sizeProp]: finalSize,
		[crossProp]: "100%",
		display: "flex",
		overflow: "hidden"
	}
})

const modeClass = computed(() => realMode.value)
const handlerClass = computed(() => (realMode.value === "horizontal" ? "h-handler" : "v-handler"))

const firstPanelStyle = computed(() => {
	const pixelSize = parseSizeValue(firstPanelSize.value, parentSize.value)
	return {
		...(props.panelItemStyle || {}),
		...(props.firstPanelItemStyle || {}),
		...(realMode.value === "horizontal"
			? { width: `${pixelSize}px` }
			: { height: `${pixelSize}px` }),
		flex: "none"
	}
})
const secondPanelStyle = computed(() => {
	return {
		...(props.panelItemStyle || {}),
		...(props.secondPanelItemStyle || {}),
		...(realMode.value === "horizontal"
			? { width: `${secondPanelSize.value}px` }
			: { height: `${secondPanelSize.value}px` }),
		flex: 1
	}
})

// 拖拽逻辑
const startDrag = (e) => {
	if (!props.isDrag) return
	e.preventDefault()
	isDragging.value = true
	const startPos = realMode.value === "horizontal" ? e.clientX : e.clientY
	// 确保 start 是数值类型
	const start =
		typeof firstPanelSize.value === "number"
			? firstPanelSize.value
			: parseFloat(firstPanelSize.value as string) || 200
	const onMove = (e) => {
		if (!isDragging.value) return
		const pos = realMode.value === "horizontal" ? e.clientX : e.clientY
		let size = start + (pos - startPos)

		// 解析 minSize 和 maxSize 为像素值
		const minPixelSize = parseSizeValue(props.minSize, parentSize.value)
		const maxPixelSize = parseSizeValue(props.maxSize, parentSize.value)

		size = Math.max(minPixelSize, Math.min(maxPixelSize, size))
		size = Math.floor(Math.min(size, parentSize.value - minPixelSize))
		firstPanelSize.value = size
	}

	const stop = () => {
		isDragging.value = false
		document.removeEventListener("mousemove", onMove)
		document.removeEventListener("mouseup", stop)

		nextTick(() => {
			emits("dragComplete", firstPanelSize.value, secondPanelSize.value)
		})
	}

	document.addEventListener("mousemove", onMove)
	document.addEventListener("mouseup", stop)
}

onMounted(() => {
	getParentSize()
	// 在父元素尺寸获取后，重新计算初始尺寸
	if (parentSize.value > 0) {
		firstPanelSize.value = parseSizeValue(props.size, parentSize.value)
	}
})

watch(() => [props.mode, props.containerSize], getParentSize)

// 监听 size 属性变化
watch(
	() => props.size,
	(newSize) => {
		if (parentSize.value > 0) {
			firstPanelSize.value = parseSizeValue(newSize, parentSize.value)
		}
	}
)
</script>

<template>
	<div class="split-panel" ref="panelRef" :class="modeClass" :style="containerStyle">
		<div class="panel-item" :style="firstPanelStyle">
			<slot name="first"></slot>
		</div>

		<div class="split-handler" :class="handlerClass" @mousedown="startDrag"></div>

		<div class="panel-item" :style="secondPanelStyle">
			<slot name="second"></slot>
		</div>
	</div>
</template>

<style scoped lang="scss">
.split-panel {
	position: relative;
	background: transparent;
	--split-handler-bg: #e5e7eb;
	--split-handler-hover-bg: #409eff;
	--split-handler-width: 6px;
	--split-handler-margin: 2px;
	--split-handler-cursor: row-resize;

	&.horizontal {
		flex-direction: row;
	}
	&.vertical {
		flex-direction: column;
	}
	.panel-item {
		background: transparent;
		overflow: auto;
	}
	.split-handler {
		background: var(--split-handler-bg);
		transition: background 0.2s;
		z-index: 10;
		flex-shrink: 0;

		&.h-handler {
			width: var(--split-handler-width);
			height: 100%;
			cursor: var(--split-handler-cursor);
			margin: 0 var(--split-handler-margin);
		}
		&.v-handler {
			height: var(--split-handler-width);
			width: 100%;
			cursor: var(--split-handler-cursor);
			margin: var(--split-handler-margin) 0;
		}
		&:hover {
			background: var(--split-handler-hover-bg);
		}
	}
}
</style>
