<template>
	<view
		class="cl-list-item"
		:class="[
			{
				'cl-list-item--disabled': disabled
			},
			pt.className
		]"
		@touchstart="onTouchStart"
		@touchend="onTouchEnd"
		@touchmove="onTouchMove"
		@touchcancel="onTouchCancel"
	>
		<view
			class="cl-list-item__wrapper"
			:class="[
				{
					'is-transition': !isHover,
					[isDark ? 'bg-surface-800' : 'bg-white']: true,
					[isDark ? '!bg-surface-700' : '!bg-surface-50']: hoverable && isHover
				}
			]"
			:style="{
				transform: `translateX(${swipe.offsetX}px)`
			}"
			@tap="onTap"
		>
			<view class="cl-list-item__inner" :class="[pt.inner?.className]">
				<cl-icon
					:name="icon"
					:size="pt.icon?.size ?? 36"
					:color="pt.icon?.color"
					:pt="{
						className: `mr-3 ${pt.icon?.className}`
					}"
					v-if="icon"
				></cl-icon>

				<cl-text
					v-if="label"
					:pt="{
						className: parseClass([
							'cl-list-item__label whitespace-nowrap overflow-visible',
							[justify == 'start', 'w-24'],
							pt.label?.className
						])
					}"
				>
					{{ label }}
				</cl-text>

				<view
					class="cl-list-item__content"
					:class="[
						{
							'justify-start': justify == 'start',
							'justify-center': justify == 'center',
							'justify-end': justify == 'end'
						},
						pt.content?.className
					]"
				>
					<slot></slot>
				</view>

				<cl-icon
					name="arrow-right-s-line"
					:size="36"
					:pt="{
						className: parseClass([
							'!text-surface-400 ml-1 duration-200',
							{
								'rotate-90': isCollapse
							}
						])
					}"
					v-if="arrow"
				></cl-icon>
			</view>

			<view
				:class="['cl-list-item__swipe', `cl-list-item__swipe-${swipe.direction}`]"
				v-if="swipeable"
			>
				<slot name="swipe-left"></slot>
				<slot name="swipe-right"></slot>
			</view>
		</view>

		<cl-collapse
			v-model="isCollapse"
			:pt="{
				className: parseClass(['p-[24rpx]', pt.collapse?.className])
			}"
		>
			<slot name="collapse"></slot>
		</cl-collapse>
	</view>
</template>

<script lang="ts" setup>
import {
	computed,
	getCurrentInstance,
	onMounted,
	reactive,
	ref,
	useSlots,
	type PropType
} from "vue";
import { isDark, isHarmony, parseClass, parsePt } from "@/cool";
import type { Justify, PassThroughProps } from "../../types";
import type { ClIconProps } from "../cl-icon/props";

defineOptions({
	name: "cl-list-item"
});

// 定义组件属性
const props = defineProps({
	// 透传样式配置
	pt: {
		type: Object,
		default: () => ({})
	},
	// 图标名称
	icon: {
		type: String,
		default: ""
	},
	// 标签文本
	label: {
		type: String,
		default: ""
	},
	// 内容对齐方式
	justify: {
		type: String as PropType<Justify>,
		default: "end"
	},
	// 是否显示箭头
	arrow: {
		type: Boolean,
		default: false
	},
	// 是否可滑动
	swipeable: {
		type: Boolean,
		default: false
	},
	// 是否显示点击态
	hoverable: {
		type: Boolean,
		default: false
	},
	// 是否禁用
	disabled: {
		type: Boolean,
		default: false
	},
	// 是否显示折叠
	collapse: {
		type: Boolean,
		default: false
	}
});

const { proxy } = getCurrentInstance()!;
const slots = useSlots();

// 透传样式类型定义 - 用于定义可以传入的样式属性类型
type PassThrough = {
	className?: string; // 根元素类名
	inner?: PassThroughProps; // 内部容器样式
	label?: PassThroughProps; // 标签文本样式
	content?: PassThroughProps; // 内容区域样式
	icon?: ClIconProps; // 图标样式
	collapse?: PassThroughProps; // 折叠内容样式
};

// 计算透传样式 - 解析传入的样式配置
const pt = computed(() => parsePt<PassThrough>(props.pt));

// 滑动状态类型定义 - 用于管理滑动相关的状态数据
type Swipe = {
	width: number; // 滑动区域宽度
	maxX: number; // 最大滑动距离
	startX: number; // 开始触摸位置
	endX: number; // 结束触摸位置
	offsetX: number; // 当前偏移量
	direction: "right" | "left"; // 滑动方向 - right表示向右滑动显示左侧内容,left表示向左滑动显示右侧内容
	moveDirection: "right" | "left"; // 移动方向 - 实时记录手指滑动方向
};

// 滑动状态数据 - 初始化滑动状态
const swipe = reactive<Swipe>({
	width: 0, // 滑动区域宽度,通过查询获取
	maxX: 0, // 最大可滑动距离
	startX: 0, // 开始触摸的X坐标
	endX: 0, // 结束触摸的X坐标
	offsetX: 0, // X轴偏移量
	direction: "left", // 默认向左滑动
	moveDirection: "left" // 默认向左移动
});

/**
 * 初始化滑动状态
 * 根据插槽判断滑动方向并获取滑动区域宽度
 */
function initSwipe() {
	if (!props.swipeable) return;

	// 根据是否有左侧插槽判断滑动方向
	swipe.direction = slots["swipe-left"] != null ? "right" : "left";

	// 获取滑动区域宽度
	uni.createSelectorQuery()
		.in(proxy)
		.select(".cl-list-item__swipe")
		.boundingClientRect((node) => {
			// 获取滑动区域的宽度，如果未获取到则默认为0
			swipe.width = (node as NodeInfo).width ?? 0;
			// 根据滑动方向(left/right)设置最大可滑动距离，左滑为负，右滑为正
			swipe.maxX = swipe.width * (swipe.direction == "left" ? -1 : 1);
		})
		.exec();
}

/**
 * 重置滑动状态
 * 将开始和结束位置重置为0
 */
function resetSwipe() {
	swipe.startX = 0;
	swipe.endX = 0;
	swipe.offsetX = 0;
}

/**
 * 滑动到指定位置
 * @param num 目标位置
 * 使用requestAnimationFrame实现平滑滑动动画
 */
function swipeTo(num: number) {
	// #ifdef APP
	function next() {
		requestAnimationFrame(() => {
			if (swipe.offsetX != num) {
				// 计算每次移动的距离
				const step = 2;
				const direction = swipe.offsetX < num ? 1 : -1;

				// 更新偏移量
				swipe.offsetX += step * direction;

				// 防止过度滑动
				if (direction > 0 ? swipe.offsetX > num : swipe.offsetX < num) {
					swipe.offsetX = num;
				}

				next();
			} else {
				// 动画结束,更新结束位置
				swipe.endX = swipe.offsetX;
			}
		});
	}

	next();
	// #endif

	// #ifdef H5 || MP
	swipe.offsetX = num;
	swipe.endX = num;
	// #endif
}

// 点击态状态 - 用于显示点击效果
const isHover = ref(false);

/**
 * 触摸开始事件处理
 * @param e 触摸事件对象
 */
function onTouchStart(e: UniTouchEvent) {
	isHover.value = true;

	// 记录开始触摸位置
	if (props.swipeable) {
		swipe.startX = (e.touches[0] as UniTouch).pageX;
	}
}

/**
 * 触摸结束事件处理
 * 根据滑动距离判断是否触发完整滑动
 */
function onTouchEnd() {
	if (isHover.value) {
		// 计算滑动阈值 - 取滑动区域一半和50px中的较小值
		const threshold = swipe.width / 2 > 50 ? 50 : swipe.width / 2;
		// 计算实际滑动距离
		const offset = Math.abs(swipe.offsetX - swipe.endX);

		// 移除点击效果
		isHover.value = false;

		// 根据滑动距离判断是否触发滑动
		if (offset > threshold) {
			// 如果滑动方向与预设方向一致,滑动到最大位置
			if (swipe.direction == swipe.moveDirection) {
				swipeTo(swipe.maxX);
			} else {
				// 否则回到起始位置
				swipeTo(0);
			}
		} else {
			// 滑动距离不够,回到最近的位置
			swipeTo(swipe.endX == 0 ? 0 : swipe.maxX);
		}
	}
}

/**
 * 触摸取消事件处理
 */
function onTouchCancel() {
	isHover.value = false; // 移除点击效果
}

/**
 * 触摸移动事件处理
 * @param e 触摸事件对象
 */
function onTouchMove(e: UniTouchEvent) {
	if (isHover.value) {
		// 计算滑动偏移量
		const offsetX = (e.touches[0] as UniTouch).pageX - swipe.startX;

		// 根据偏移量判断滑动方向
		swipe.moveDirection = offsetX > 0 ? "right" : "left";

		// 计算目标位置
		let x = offsetX + swipe.endX;

		// 限制滑动范围
		if (swipe.direction == "right") {
			// 向右滑动时的边界处理
			if (x > swipe.maxX) {
				x = swipe.maxX;
			}
			if (x < 0) {
				x = 0;
			}
		}

		if (swipe.direction == "left") {
			// 向左滑动时的边界处理
			if (x < swipe.maxX) {
				x = swipe.maxX;
			}
			if (x > 0) {
				x = 0;
			}
		}

		// 更新偏移量
		swipe.offsetX = x;
	}
}

// 折叠状态
const isCollapse = ref(false);

/**
 * 点击事件处理
 */
function onTap() {
	if (props.collapse) {
		isCollapse.value = !isCollapse.value;
	}
}

onMounted(() => {
	setTimeout(
		() => {
			initSwipe();
		},
		isHarmony() ? 50 : 0
	);
});

defineExpose({
	initSwipe,
	resetSwipe
});
</script>

<style lang="scss" scoped>
.cl-list-item {
	@apply flex flex-col w-full relative;

	&__wrapper {
		@apply w-full;
		overflow: visible;

		&.is-transition {
			// #ifdef H5 || MP
			transition-property: transform;
			transition-duration: 0.2s;
			// #endif
		}
	}

	&__inner {
		@apply flex flex-row items-center;
		padding: 24rpx;
	}

	&__content {
		@apply flex flex-row items-center;
		flex: 1;
	}

	&__swipe {
		@apply absolute h-full;

		&-left {
			@apply left-full;
			transform: translateX(1rpx);
		}

		&-right {
			@apply right-full;
		}
	}

	&--disabled {
		@apply opacity-50;
	}
}
</style>
