<script setup lang="ts">
import type { ModeType } from '../../../utils/calendar/typings'
import { TextInput, Dropdown, formatDate, useI18n, resolveI18n } from '@bagelink/vue'
import { onClickOutside } from '@vueuse/core'
import { ref, computed, onMounted, watch } from 'vue'
import { WEEK_START_DAY } from '../../../utils/calendar/time'
import DatePicker from './DatePicker.vue'

export interface DateInputProps {
	required?: boolean
	label?: string
	placeholder?: string
	editMode?: boolean
	small?: boolean
	/**
	 * Whether to enable time selection.
	 * @default false
	 */
	enableTime?: boolean
	modelValue?: string | Date
	min?: string | Date
	max?: string | Date
	mode?: ModeType
	defaultValue?: string | Date
	firstDayOfWeek?: WEEK_START_DAY
	locale?: string
	center?: boolean
	error?: string
	underlined?: boolean
}

const props = withDefaults(
	defineProps<DateInputProps>(),
	{
		enableTime: false,
		editMode: true,
		small: false,
		mode: () => ({ mode: 'day' }),
		firstDayOfWeek: WEEK_START_DAY.SUNDAY,
		locale: ''
	},
)

const { $t } = useI18n()

const selectedDate = defineModel<string | Date>()
const inputValue = ref('')
const isValid = ref(true)
const isTyping = ref(false)
const isOpen = ref(false)
let isClosing = false

const datePickerRef = ref<HTMLElement | null>(null)
const calendarRef = ref<HTMLElement | null>(null)

// Formatting utilities
function formatDisplayDate(date: Date | string | undefined): string {
	if (date === undefined || date === '') return ''
	return formatDate(date, { format: props.enableTime ? 'DD.MM.YY HH:mm' : 'DD.MM.YY' })
}

function parseUserInput(input: string): Date | null {
	const date = new Date(input)
	if (!Number.isNaN(date.getTime())) return date

	// Try DD.MM.YY format
	const parts = input.split(/[/.-]/)
	if (parts.length === 3) {
		const [day, month, year] = parts.map(p => Number.parseInt(p, 10))
		if (!Number.isNaN(day) && !Number.isNaN(month) && !Number.isNaN(year)) {
			const parsedDate = new Date(year, month - 1, day)
			if (parsedDate.getFullYear() === year && parsedDate.getMonth() === month - 1 && parsedDate.getDate() === day) {
				return parsedDate
			}
		}
	}
	return null
}

function normalizeDate(date: Date): string {
	if (props.enableTime) return date.toISOString()
	const normalized = new Date(date)
	normalized.setHours(0, 0, 0, 0)
	return normalized.toISOString().split('T')[0]
}

function syncInputValue() {
	if (!isTyping.value) {
		inputValue.value = selectedDate.value !== undefined ? formatDisplayDate(selectedDate.value) : ''
	}
}

// Event handlers
function handleInput(event: Event) {
	const input = event.target as HTMLInputElement
	isTyping.value = true

	// Auto-format: clean and add dots after DD and MM
	const cleaned = input.value.replace(/[^\d.]/g, '')
	const parts = cleaned.split('.')
	let formatted = parts.slice(0, 3).join('.')

	if (formatted.length === 2 && !formatted.includes('.')) formatted += '.'
	else if (formatted.length === 5 && parts.length === 2) formatted += '.'

	inputValue.value = formatted
	if (input.value !== formatted) input.value = formatted

	// Validate and commit immediately if valid
	const date = parseUserInput(formatted)
	if (date !== null) {
		selectedDate.value = normalizeDate(date)
		isValid.value = true
	} else if (formatted.length === 0) {
		selectedDate.value = undefined
		isValid.value = true
	} else {
		isValid.value = false
	}
}

function handleFocus() {
	if (!isClosing) isOpen.value = true
}

function handleBlur() {
	isTyping.value = false
	if (inputValue.value.length === 0) {
		selectedDate.value = undefined
		isValid.value = true
	} else if (selectedDate.value !== undefined) {
		inputValue.value = formatDisplayDate(selectedDate.value)
		isValid.value = true
	} else {
		isValid.value = false
	}
	setTimeout(() => {
		if (!calendarRef.value?.contains(document.activeElement)) {
			isClosing = true
			isOpen.value = false
			setTimeout(() => { isClosing = false }, 200)
		}
	}, 100)
}

function handleKeydown(event: KeyboardEvent) {
	const { key, shiftKey } = event

	if (key === 'Escape') {
		isOpen.value = false
		isTyping.value = false
		inputValue.value = selectedDate.value !== undefined ? formatDisplayDate(selectedDate.value) : ''
		isValid.value = true
	} else if (key === 'Enter') {
		isTyping.value = false
		if (selectedDate.value !== undefined) inputValue.value = formatDisplayDate(selectedDate.value)
		isOpen.value = false
	} else if (key === 'ArrowUp' || key === 'ArrowDown') {
		event.preventDefault()
		const date = selectedDate.value !== undefined ? new Date(selectedDate.value) : new Date()
		const increment = key === 'ArrowUp' ? 1 : -1

		if (shiftKey) date.setMonth(date.getMonth() + increment)
		else date.setDate(date.getDate() + increment)

		selectedDate.value = normalizeDate(date)
		inputValue.value = formatDisplayDate(date)
	}
}

function handleIconClick() {
	isOpen.value = !isOpen.value
}

// Sync input display when date changes externally
watch(() => selectedDate.value, syncInputValue, { immediate: true })

const hasValue = computed(() => inputValue.value.length > 0)

onClickOutside(datePickerRef, () => {
	isClosing = true
	isOpen.value = false
	setTimeout(() => { isClosing = false }, 200)
}, { ignore: [calendarRef] })

onMounted(() => {
	if (selectedDate.value === undefined && props.defaultValue !== undefined) {
		selectedDate.value = props.defaultValue
	}
})
</script>

<template>
	<div
		class="bagel-input"
		:class="{ small, 'has-error': !!error || !isValid, underlined, 'has-value': hasValue, 'open': isOpen }"
	>
		<label>
			<span v-if="label || (underlined && placeholder)" class="label-text">{{ resolveI18n(label || placeholder)
			}}<span v-if="required"> *</span></span>
			<Dropdown :shown="isOpen" placement="bottom-start" :autoHide="false" :triggers="[]" noAutoFocus>
				<template #trigger>
					<div ref="datePickerRef" class="date-picker-container">
						<TextInput
							v-model="inputValue" iconStart="calendar"
							:placeholder="underlined ? ' ' : (resolveI18n(placeholder) || (enableTime ? 'DD.MM.YY HH:mm' : 'DD.MM.YY'))"
							:required="underlined ? false : required" :disabled="!editMode"
							:error="!isValid ? $t('date.invalidFormat') : error" :class="{ 'txt-center': center }"
							:underlined="underlined" class="date-input m-0" @input="handleInput" @focus="handleFocus"
							@blur="handleBlur" @click.stop @keydown="handleKeydown" @iconClick="handleIconClick"
						/>
					</div>
				</template>

				<div ref="calendarRef" class="date-picker-popup" @click.stop>
					<DatePicker
						v-model="selectedDate" :min="min" :max="max" :mode="mode"
						:firstDayOfWeek="firstDayOfWeek" :locale="locale" :enableTime="enableTime"
					/>
				</div>
			</Dropdown>
		</label>
	</div>
</template>

<style>
.date-picker-container {
	width: 100%;
}
</style>
