<script setup lang="ts">
import { reactive } from 'vue'

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

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?.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?.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 ref="otpCont" class="otp_wrap">
		<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>
</template>

<style scoped>
.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-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-primary);
  filter: drop-shadow(0 0 0.25rem var(--bgl-primary));
}

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

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