<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
import type { BagelInputShellProps } from './bagelInputShell'
import { useBagelInputShell } from './bagelInputShell'

const props = defineProps<{
	digitCount: number
	default?: string
	modelValue?: string
	error?: string
} & BagelInputShellProps>()

const { shellClass, shellStyle } = useBagelInputShell(props)
const hasValue = computed(() => digits.some(d => d !== undefined && d !== ''))

const emit = defineEmits(['update:modelValue', 'complete'])
const digits = reactive<(number | string | undefined)[]>([])
const otpCont = ref<HTMLElement>()

function handlePaste(event: ClipboardEvent, index: number) {
	event.preventDefault()
	const { clipboardData } = event
	const pastedText = clipboardData?.getData('text').replaceAll(/\D/g, '') // Ensure only numeric digits are pasted
	const pastedDigits = [...(pastedText ?? '')]
	if (!pastedDigits) { return }
	for (let i = 0; i < pastedDigits.length; i++) {
		const digit = pastedDigits[i]
		if (index + i < digits.length) {
			digits[index + i] = digit
		}
	}
	emitUpdate()
}

function emitUpdate() {
	emit('update:modelValue', digits.join(''))
	if (isDigitsFull()) {
		emit('complete', digits.join(''))
	}
}

function handleKeyDown(event: KeyboardEvent, index: number) {
	const noPreventKeys = ['Tab', 'ArrowRight', 'ArrowLeft', 'Control', 'Shift', 'Alt', 'Meta']
	if (noPreventKeys.includes(event.key)) {
		return
	}

	const preventKeys = ['ArrowUp', 'ArrowDown']
	if (preventKeys.includes(event.key)) {
		event.preventDefault()
		return
	}

	if (event.key === 'Backspace') {
		digits[index] = undefined

		if (index !== 0) {
			const prevDigit = otpCont.value?.children[index - 1] as any
			prevDigit.focus()
		}
		emitUpdate()
		return
	}

	const numberRegex = /^\d$/
	if (numberRegex.test(event.key)) {
		digits[index] = event.key

		if (index !== props.digitCount - 1) {
			setTimeout(() => {
				const nextDigit = otpCont.value?.children[index + 1] as any
				nextDigit.focus()
			}, 10)
		}
		emitUpdate()
	}
}

if (props.default && props.default.length === props.digitCount) {
	for (let i = 0; i < props.digitCount; i++) {
		digits[i] = props.default.charAt(i)
	}
} else {
	for (let i = 0; i < props.digitCount; i++) {
		digits[i] = undefined
	}
}

function isDigitsFull() {
	for (const elem of digits) {
		if (elem === undefined) {
			return false
		}
	}
	return true
}
</script>

<template>
	<div
		class="otp-input" :class="[shellClass, { 'has-value': hasValue }]" :style="shellStyle"
		tabindex="-1"
	>
		<div ref="otpCont" class="otp_wrap ltr" :class="{ 'has-error': !!error }">
			<input
				v-for="(_el, ind) in digits" :key="ind" :value="digits[ind] || ''" type="number" inputmode="numeric"
				class="digit-box" :autofocus="ind === 0" maxlength="1" pattern="[0-9]*"
				oninput="this.value = this.value.slice(0, 1)" @keydown="handleKeyDown($event, ind)"
				@paste="handlePaste($event, ind)" @keyup.prevent
			>
		</div>
		<div v-if="error" class="otp-error-message txt-center txt10 pt-05">
			{{ error }}
		</div>
	</div>
</template>

<style scoped>

.otp-error-message {
color: var(--bgl-red);
}
.otp_wrap.has-error input,
.otp_wrap.has-error textarea {
outline: 1px solid var(--bgl-red) !important;
}

.otp_wrap {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
}

.digit-box {
height: 3rem;
flex-grow: 1;
border: 1px solid var(--bgl-input-label-active-color, var(--bgl-primary-tint));
display: inline-block;
background: var(--bgl-gray-light);
border-radius: 5px;
margin: 5px;
text-align: center;
font-size: 2.4rem;
-moz-appearance: textfield;
/* Firefox */
caret-color: transparent;
/* Remove spinner arrows */
appearance: textfield;
/* Standardized property */
}

/* Remove spinner arrows for Webkit browsers */
.digit-box::-webkit-outer-spin-button,
.digit-box::-webkit-inner-spin-button {
-webkit-appearance: none;
appearance: none;
margin: 0;
}

.digit-box:focus {
outline: 1px solid var(--bgl-input-label-active-color, var(--bgl-primary));
filter: drop-shadow(0 0 0.25rem var(--bgl-input-label-active-color, var(--bgl-primary)));
outline-offset: 1px;
}

/* ── frame variant ──────────────────────────────────────────────────────── */

.otp-input.frame .digit-box {
background: transparent;
outline: 1.5px solid var(--bgl-border-color);
outline-offset: -1px;
transition: outline-color 0.2s ease, box-shadow 0.2s ease;
}

.otp-input.frame .digit-box:focus {
outline-color: var(--bgl-input-label-active-color, var(--bgl-primary));
outline-width: 2px;
box-shadow:
	0 0 0 4px color-mix(in srgb, var(--bgl-input-label-active-color, var(--bgl-primary)) 15%, transparent),
	0 2px 8px color-mix(in srgb, var(--bgl-input-label-active-color, var(--bgl-primary)) 25%, transparent);
filter: none;
}

/* ── bgl-outline variant ────────────────────────────────────────────────── */

.otp-input.bgl-outline .digit-box {
outline: 1.5px solid var(--bgl-border-color);
outline-offset: -1px;
}

 /* Redundant, already covered above */
/* .digit-box[type="number"] {
-moz-appearance: textfield;
} */
 .relative {
 	position: relative !important;
 }

@media screen and (max-width: 910px) {
.digit-box {
	padding: 0.25rem;
}
}

</style>
