<template>
	<cl-popup
		:title="title"
		:swipe-close-threshold="100"
		:pt="{
			inner: {
				className: parseClass([
					[isDark, '!bg-surface-700', '!bg-surface-100'],
					pt.popup?.className
				])
			},
			mask: {
				className: '!bg-transparent'
			}
		}"
		v-model="visible"
	>
		<view class="cl-keyboard-number" :class="[pt.className]">
			<slot name="value" :value="value">
				<view
					v-if="showValue"
					class="cl-keyboard-number__value"
					:class="[pt.value?.className]"
				>
					<cl-text
						v-if="value != ''"
						:pt="{
							className: 'text-2xl'
						}"
						>{{ value }}</cl-text
					>

					<cl-text
						v-else
						:pt="{
							className: 'text-md text-surface-400'
						}"
						>{{ placeholder }}</cl-text
					>
				</view>
			</slot>

			<view class="cl-keyboard-number__list">
				<cl-row :gutter="10">
					<cl-col :span="18">
						<cl-row :gutter="10">
							<cl-col :span="8" v-for="item in list" :key="item">
								<view
									class="cl-keyboard-number__item"
									:class="[
										`is-keycode-${item}`,
										{
											'is-dark': isDark,
											'is-empty': item == ''
										},
										pt.item?.className
									]"
									hover-class="opacity-50"
									:hover-stay-time="250"
									@touchstart.stop="onCommand(item)"
								>
									<slot name="item" :item="item">
										<cl-icon
											v-if="item == 'delete'"
											name="delete-back-2-line"
											:size="36"
										></cl-icon>

										<view
											v-else-if="item == 'confirm'"
											class="cl-keyboard-number__item-confirm"
										>
											<cl-text
												color="white"
												:pt="{
													className: 'text-lg'
												}"
												>{{ confirmText }}</cl-text
											>
										</view>

										<view v-else-if="item == '_confirm'"></view>

										<cl-text
											v-else
											:pt="{
												className: 'text-lg'
											}"
											>{{ item }}</cl-text
										>
									</slot>
								</view>
							</cl-col>
						</cl-row>
					</cl-col>

					<cl-col :span="6">
						<view class="cl-keyboard-number__op">
							<view
								v-for="item in opList"
								:key="item"
								class="cl-keyboard-number__item"
								:class="[
									{
										'is-dark': isDark
									},
									`is-keycode-${item}`
								]"
								hover-class="opacity-50"
								:hover-stay-time="250"
								@touchstart.stop="onCommand(item)"
							>
								<cl-icon
									v-if="item == 'delete'"
									name="delete-back-2-line"
									:size="36"
								></cl-icon>

								<cl-text
									v-if="item == 'confirm'"
									color="white"
									:pt="{
										className: 'text-lg'
									}"
									>{{ confirmText }}</cl-text
								>
							</view>
						</view>
					</cl-col>
				</cl-row>
			</view>
		</view>
	</cl-popup>
</template>

<script setup lang="ts">
import { useUi } from "../../hooks";
import type { PassThroughProps } from "../../types";
import type { ClPopupProps } from "../cl-popup/props";
import { ref, computed, watch, type PropType } from "vue";
import { $t, t } from "@/locale";
import { isAppIOS, isDark, parseClass, parsePt } from "@/cool";
import { vibrate } from "@/uni_modules/cool-vibrate";

defineOptions({
	name: "cl-keyboard-number"
});

defineSlots<{
	value(props: { value: string }): any;
	item(props: { item: string }): any;
}>();

const props = defineProps({
	// 透传样式配置
	pt: {
		type: Object,
		default: () => ({})
	},
	// v-model绑定的值
	modelValue: {
		type: String,
		default: ""
	},
	// 键盘类型，支持number、digit、idcard
	type: {
		type: String as PropType<"number" | "digit" | "idcard">,
		default: "digit"
	},
	// 弹窗标题
	title: {
		type: String,
		default: () => t("数字键盘")
	},
	// 输入框占位符
	placeholder: {
		type: String,
		default: () => t("安全键盘，请放心输入")
	},
	// 最大输入长度
	maxlength: {
		type: Number,
		default: 10
	},
	// 确认按钮文本
	confirmText: {
		type: String,
		default: () => t("确定")
	},
	// 是否显示输入值
	showValue: {
		type: Boolean,
		default: true
	},
	// 是否输入即绑定
	inputImmediate: {
		type: Boolean,
		default: false
	}
});

// 定义事件发射器，支持v-model和change事件
const emit = defineEmits(["update:modelValue", "change"]);

// 样式穿透类型
type PassThrough = {
	className?: string;
	item?: PassThroughProps;
	value?: PassThroughProps;
	popup?: ClPopupProps;
};

// 样式穿透计算
const pt = computed(() => parsePt<PassThrough>(props.pt));

// 获取UI相关的工具方法
const ui = useUi();

// 控制弹窗显示/隐藏
const visible = ref(false);

// 输入框当前值，双向绑定
const value = ref(props.modelValue);

// 最大输入长度
const maxlength = computed(() => {
	if (props.type == "idcard") {
		return 18;
	}

	return props.maxlength;
});

// 数字键盘的按键列表，包含数字、删除、00和小数点
const list = computed(() => {
	const arr = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "00", "0", ""];

	// 数字键盘显示为小数点 "."
	if (props.type == "digit") {
		arr[11] = ".";
	}

	// 身份证键盘显示为 "X"
	if (props.type == "idcard") {
		arr[11] = "X";
	}

	return arr;
});

// 操作按钮列表
const opList = computed(() => {
	return ["delete", "confirm"];
});

// 打开键盘弹窗
function open() {
	visible.value = true;
}

// 关闭键盘弹窗
function close() {
	visible.value = false;
}

// 处理键盘按键点击事件
function onCommand(key: string) {
	// 震动
	try {
		vibrate(1);
	} catch (error) {}

	// 确认按钮逻辑
	if (key == "confirm" || key == "_confirm") {
		if (value.value == "") {
			ui.showToast({
				message: t("请输入内容")
			});

			return;
		}

		// 如果最后一位是小数点，去掉
		if (value.value.endsWith(".")) {
			value.value = value.value.slice(0, -1);
		}

		// 身份证号码正则校验（支持15位和18位，18位末尾可为X/x）
		if (props.type == "idcard") {
			if (
				!/^(^[1-9]\d{5}(18|19|20)?\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}(\d|X|x)?$)$/.test(
					value.value
				)
			) {
				ui.showToast({
					message: t("身份证号码格式不正确")
				});
				return;
			}
		}

		// 触发v-model和change事件
		emit("update:modelValue", value.value);
		emit("change", value.value);

		// 关闭弹窗
		close();

		return;
	}

	// 删除键，去掉最后一位
	if (key == "delete") {
		value.value = value.value.slice(0, -1);
		return;
	}

	// 超过最大输入长度，提示并返回
	if (value.value.length >= maxlength.value) {
		ui.showToast({
			message: $t("最多输入{maxlength}位", {
				maxlength: maxlength.value
			})
		});
		return;
	}

	// 处理小数点输入，已存在则不再添加
	if (key == ".") {
		if (value.value.includes(".")) {
			return;
		}

		if (value.value == "") {
			value.value = "0.";
			return;
		}
	}

	// 处理00键，首位不能输入00，只能输入0
	if (key == "00") {
		if (value.value.length + 2 > maxlength.value) {
			value.value += "0";
			return;
		}

		if (value.value == "") {
			value.value = "0";
			return;
		}
	}

	if (key == "00" || key == "0") {
		if (value.value == "" || value.value == "0") {
			value.value = "0";
			return;
		}
	}

	// 其他按键直接拼接到value
	value.value += key;
}

watch(value, (val: string) => {
	// 如果输入即绑定，则立即更新绑定值
	if (props.inputImmediate) {
		emit("update:modelValue", val);
		emit("change", val);
	}
});

// 监听外部v-model的变化，保持内部value同步
watch(
	computed(() => props.modelValue),
	(val: string) => {
		value.value = val;
	}
);

defineExpose({
	open,
	close
});
</script>

<style lang="scss" scoped>
.cl-keyboard-number {
	padding: 0 20rpx 20rpx 20rpx;

	&__value {
		@apply flex flex-row items-center justify-center;
		height: 80rpx;
		margin-bottom: 20rpx;
	}

	&__list {
		@apply relative overflow-visible;
	}

	&__op {
		@apply flex flex-col h-full;
	}

	&__item {
		@apply flex items-center justify-center rounded-xl bg-white overflow-visible;
		height: 100rpx;
		margin-top: 10rpx;

		&.is-dark {
			@apply bg-surface-800;
		}

		&.is-keycode-delete {
			@apply bg-surface-200;

			&.is-dark {
				@apply bg-surface-800;
			}
		}

		&.is-keycode-confirm {
			@apply flex flex-col items-center justify-center;
			@apply bg-primary-500 rounded-xl flex-1;
		}

		&.is-empty {
			background-color: transparent !important;
		}
	}
}
</style>
