<template>
	<l-picker 
		:title="title" 
		:titleStyle="titleStyle" 
		:confirm-btn="confirmBtn" 
		:confirm-style="confirmStyle"
		:cancel-btn="cancelBtn" 
		:cancel-style="cancelStyle" 
		:itemHeight="itemHeight" 
		:itemColor="itemColor"
		:itemFontSize="itemFontSize" 
		:itemActiveColor="itemActiveColor" 
		:indicatorStyle="indicatorStyle"
		:bgColor="bgColor" 
		:groupHeight="groupHeight" 
		:radius="radius" 
		:value="valueOfPicker" 
		:columns="columns"
		@confirm="onConfirm" 
		@cancel="onCancel" 
		@change="onChange" 
		@pick="onPick">
	</l-picker>
</template>
<script lang="uts" setup>
	/**
	 * DateTimePicker 日期时间选择器
	 * <br> 插件类型：LDateTimePickerComponentPublicInstance 
	 * @description 日期时间选择器组件，支持多种时间模式选择和自定义配置
	 * @tutorial https://ext.dcloud.net.cn/plugin?name=lime-data-picker
	 * 
	 * @property {string} cancelBtn 取消按钮文字
	 * @property {string | object} cancelStyle 取消按钮样式
	 * @property {string} confirmBtn 确定按钮文字
	 * @property {string | object} confirmStyle 确定按钮样式
	 * @property {'zh' | 'tc' | 'en' | 'ja' | 'ko' | 'ru'} customLocale 组件国际化语言
	 * @property {string|number} end 最大可选时间（默认当前时间+10年）
	 * @property {string|number} start 最小可选时间（默认当前时间-10年）
	 * @property {object} steps 时间间隔步数（例：{ minute: 5 }）
	 * @property {string} title 标题文字
	 * @property {string | object} titleStyle 标题样式
	 * @property {string|number} value 选中值（支持v-model）
	 * @property {string|number} defaultValue 默认选中值
	 * @property {string|number} modelValue 模型值（支持v-model）
	 * @property {string} format 时间格式（使用day.js格式）
	 * @property {number} mode 时间选择模式（位运算组合）
	 * @value 1 年模式
	 * @value 2 月模式
	 * @value 4 日模式
	 * @value 8 时模式
	 * @value 16 分模式
	 * @value 32 秒模式
	 * @example 1 | 2 组合年月选择
	 * @property {(type: TimeModeValues, columns: DateTimePickerColumn) => DateTimePickerColumn} customFilter 自定义过滤函数
	 * @property {(type: string, value: string) => string} renderLabel 自定义标签渲染
	 * @property {boolean} showUnit 是否显示时间单位
	 * @property {string} itemHeight 选项高度
	 * @property {string} itemColor 选项文字颜色
	 * @property {string} itemFontSize 选项字体大小
	 * @property {string} itemActiveColor 选中项颜色
	 * @property {string} indicatorStyle 指示器样式
	 * @property {string} bgColor 背景颜色
	 * @property {string} groupHeight 选项组高度
	 * @property {string} radius 圆角半径
	 * @property {boolean} resetIndex 是否重置选项索引
	 * @property {number} minHour 最小小时数
	 * @property {number} maxHour 最大小时数
	 * @property {number} minMinute 最小分钟数
	 * @property {number} maxMinute 最大分钟数
	 * 
	 * @event {Function} confirm 点击确定时触发
	 * @event {Function} cancel 点击取消时触发
	 * @event {Function} change 值变化时触发
	 */
	import type { DateTimePickerProps, DateValue, DateTimePickerColumn, TimeModeValues, DateTimePickerColumnItem } from './type';
	import type { PickerColumn, PickerColumnItem, PickerConfirmEvent, PickerPickEvent, PickerValue } from '@/uni_modules/lime-picker';
	import { getMeaningColumn } from './utils';
	import { DEFAULT_FORMAT, MODE_NAMES, FORMAT_MAP, UNIT_MAP } from './constant';
	import { dayuts, Dayuts } from '@/uni_modules/lime-dayuts/common'
	import { DayutsUnit } from '@/uni_modules/lime-dayuts'
	import { clamp } from '@/uni_modules/lime-shared/clamp'

	const emit = defineEmits(['change', 'cancel', 'confirm', 'pick', 'update:modelValue', 'update:value'])
	const props = withDefaults(defineProps<DateTimePickerProps>(), {
		mode: 1 | 2 | 4,
		format: DEFAULT_FORMAT,
		showUnit: true,
		resetIndex: false,
		minHour: 0,
		maxHour: 23,
		minMinute: 0,
		maxMinute: 59
	})
	// 默认值
	let defaultValue : DateValue = props.value ?? props.defaultValue ?? props.defaultValue ?? Date.now()
	const innerValue = computed({
		set(value : DateValue) {
			if(defaultValue == value) return
			defaultValue = value;
			emit('change', value)
			emit('update:modelValue', value)
			emit('update:value', value)
		},
		get() : DateValue {
			const value = props.value ?? props.modelValue ?? defaultValue
			return typeof value == 'string' && (value as string).length == 0 ? Date.now() : value
		}
	} as WritableComputedOptions<DateValue>)


	const meaningColumn = getMeaningColumn(props.mode);
	const isTimeMode = ['hour', 'minute', 'second'].includes(meaningColumn[0]);
	//@ts-ignore
	const normalize = (val : DateValue | null, defaultDay : Dayuts) : Dayuts => val != null && dayuts(val).isValid() ? dayuts(val) : defaultDay;
	const start = computed(() : Dayuts => normalize(props.start as DateValue | null, dayuts().subtract(10, 'year')));
	const end = computed(() : Dayuts => normalize(props.end as DateValue | null, dayuts().add(10, 'year')));
	const rationalize = (val : Dayuts) : Dayuts => {
		if (isTimeMode) return val;
		if (val.isBefore(start.value)) return start.value;
		if (val.isAfter(end.value)) return end.value;
		return val;
	};

	const calcDate = (currentValue : DateValue | null) : Dayuts => {
		// 当首次为空值的时候，会new Date当前时间，若不判断会造成时分时间不为当前时分
		if (isTimeMode && (typeof currentValue == 'string')) {
			const dateStr = dayuts(start.value).format('YYYY-MM-DD');
			currentValue = `${dateStr} ${currentValue}`;
		}
		//@ts-ignore
		return currentValue != null && dayuts(currentValue).isValid() ? rationalize(dayuts(currentValue)) : start.value;
	};

	const curDate = ref(calcDate(innerValue.value));
	const valueOfPicker = computed(() : string[] => meaningColumn.map((item) : string => curDate.value.get(item).toString()));
	const columnCache = new Map<string, DateTimePickerColumnItem[]>();
	const columns = computed(() : DateTimePickerColumn[] => {
		const ret : DateTimePickerColumn[] = [];

		const getDate = (date : Dayuts) : number[] => [
			date.year(),
			date.month() + 1,
			date.date(),
			date.hour(),
			date.minute(),
			date.second(),
		];
		//@ts-ignore
		const [curYear, curMonth, curDay, curHour, curMinute] = getDate(curDate.value);
		const [minYear, minMonth, minDay, minHour, minMinute, minSecond] = getDate(start.value);
		const [maxYear, maxMonth, maxDay, maxHour, maxMinute, maxSecond] = getDate(end.value);

		const isInMinYear = curYear == minYear;
		const isInMaxYear = curYear == maxYear;
		const isInMinMonth = isInMinYear && curMonth == minMonth;
		const isInMaxMonth = isInMaxYear && curMonth === maxMonth;
		const isInMinDay = isInMinMonth && curDay == minDay;
		const isInMaxDay = isInMaxMonth && curDay == maxDay;
		const isInMinHour = isInMinDay && curHour == minHour;
		const isInMaxHour = isInMaxDay && curHour == maxHour;
		const isInMinMinute = isInMinHour && curMinute == minMinute;
		const isInMaxMinute = isInMaxHour && curMinute == maxMinute;

		const generateColumn = (type : string, lowerBound : number, upperBound : number) => {
			const cacheKey = `${type}-${lowerBound}-${upperBound}`;
			if (columnCache.has(cacheKey)) {
				ret.push(columnCache.get(cacheKey)!)
				return
			}

			const arr : DateTimePickerColumnItem[] = [];
			for (let i = lowerBound; i <= upperBound; i++) {
				const value = i;
				arr.push({
					label: props.renderLabel != null ? props.renderLabel!(type, i.toString()) : `${value}${props.showUnit ? UNIT_MAP.get(type) : ''}`,
					value: type == 'month' ? `${value - 1}` : value.toString(),
				} as DateTimePickerColumnItem);
			}

			if (props.customFilter != null) {
				const _arr = props.customFilter!(type, arr)
				ret.push(_arr)
				columnCache.set(cacheKey, _arr);
			} else {
				ret.push(arr)
				columnCache.set(cacheKey, arr);
			}

		};

		if (meaningColumn.includes('year')) {
			generateColumn('year', minYear, maxYear);
		}
		if (meaningColumn.includes('month')) {
			const lower = isInMinYear ? minMonth : 1;
			const upper = isInMaxYear ? maxMonth : 12;
			generateColumn('month', lower, upper);
		}
		if (meaningColumn.includes('date')) {
			const lower = isInMinMonth ? minDay : 1;
			const upper = isInMaxMonth ? maxDay : dayuts(`${curYear}-${curMonth}`).daysInMonth();
			generateColumn('date', lower, upper);
		}
		if (meaningColumn.includes('hour')) {
			const lower = isInMinDay && !isTimeMode ? minHour : clamp(props.minHour, 0, 23);// 0;
			const upper = isInMaxDay && !isTimeMode ? maxHour : clamp(props.maxHour, lower, 23);//23;
			generateColumn('hour', lower, upper);
		}
		if (meaningColumn.includes('minute')) {
			const lower = isInMinHour && !isTimeMode ? minMinute : clamp(props.minMinute, 0, 59);//0;
			const upper = isInMaxHour && !isTimeMode ? maxMinute : clamp(props.maxMinute, lower, 59);//59;
			generateColumn('minute', lower, upper);
		}
		if (meaningColumn.includes('second')) {
			const lower = isInMinMinute && !isTimeMode ? minSecond : 0;
			const upper = isInMaxMinute && !isTimeMode ? maxSecond : 59;
			generateColumn('second', lower, upper);
		}
		return ret;
	})

	const innterFormat = computed(() : string => {
		const first = meaningColumn.length > 0 ? meaningColumn[0] : 'year';
		const last = meaningColumn.length > 0 ? meaningColumn[meaningColumn.length - 1] : 'date';

		const format = DEFAULT_FORMAT.substring(
			DEFAULT_FORMAT.indexOf(FORMAT_MAP.get(first)!),
			DEFAULT_FORMAT.lastIndexOf(FORMAT_MAP.get(last)!) + FORMAT_MAP.get(last)!.length
		)
		return format
	})
	const onConfirm = ({ values } : PickerConfirmEvent) => {

		// const first = meaningColumn.length > 0 ? meaningColumn[0]: 'year';
		// const last =  meaningColumn.length > 0 ? meaningColumn[meaningColumn.length - 1]: 'date';

		// const format = DEFAULT_FORMAT.substring(
		// 	DEFAULT_FORMAT.indexOf(FORMAT_MAP.get(first)!),
		// 	DEFAULT_FORMAT.lastIndexOf(FORMAT_MAP.get(last)!) + FORMAT_MAP.get(last)!.length
		// )

		let cur = curDate.value
		// MODE_NAMES
		values.forEach((item, index) => {
			const type = meaningColumn[index]
			cur = cur.set(type, parseInt(`${item}`, 10))
		})
		const curValue = cur.format(props.format)

		innerValue.value = cur.format(innterFormat.value);
		emit('confirm', curValue);
	}
	const onCancel = () => {
		emit('cancel')
	}
	const onPick = ({ values, column, index } : PickerPickEvent) => {
		const type = meaningColumn[column];
		const val = curDate.value.set(type as DayutsUnit, parseInt(columns.value[column][index].value, 10));

		curDate.value = rationalize(val);
		emit('pick', rationalize(val).format(props.format))
	}
	
	const onChange = (values : PickerValue[]) => {
		let cur = curDate.value
		values.forEach((item, index) => {
			const type = meaningColumn[index]
			cur = cur.set(type, parseInt(`${item}`, 10))
		})
		curDate.value = rationalize(cur as Dayuts)
		const curValue = curDate.value.format(innterFormat.value)
		innerValue.value = curValue
	}
	const stop = watch(innerValue, (val : DateValue) => {
		curDate.value = calcDate(val);
	});

	onBeforeUnmount(() => {
		stop()
	})
</script>
<style>
</style>