<script setup lang="ts">
import type { IconType, ValidateInputBaseT } from '@bagelink/vue'
import { Icon, useDebounceFn, useI18n, resolveI18n } from '@bagelink/vue'
import { computed, onMounted, ref, watch, nextTick } from 'vue'
import { EMAIL_REGEX } from '../../../utils/constants'

const props = withDefaults(
	defineProps<EmailInputProps>(),
	{
		modelValue: '',
		autocorrect: true,
		serverValidate: false,
		preventFakeEmails: false,
		pattern: '[a-z0-9._%+\\-]+@[a-z0-9.\\-]+\\.[a-z]{2,}',
	},
)

const emit = defineEmits(['update:modelValue', 'debounce'])

const { $t } = useI18n()

export interface EmailInputProps extends ValidateInputBaseT {
	id?: string
	title?: string
	helptext?: string
	name?: string
	placeholder?: string
	modelValue?: string
	label?: string
	small?: boolean
	required?: boolean
	pattern?: string
	defaultValue?: string
	shrink?: boolean
	underlined?: boolean
	disabled?: boolean
	nativeInputAttrs?: { [key: string]: any }
	icon?: IconType
	iconStart?: IconType
	autocomplete?: AutoFillField
	autofocus?: boolean
	error?: string
	onFocusout?: (e: FocusEvent) => void
	onFocus?: (e: FocusEvent) => void
	// Additional props
	autocorrect?: boolean
	serverValidate?: boolean
	preventFakeEmails?: boolean
}

// Common email providers for autocorrection
const COMMON_EMAIL_DOMAINS = [
	'gmail.com',
	'yahoo.com',
	'hotmail.com',
	'outlook.com',
	'aol.com',
	'icloud.com',
	'protonmail.com',
	'mail.com',
	'zoho.com',
	'yandex.com',
	'gmx.com',
	'live.com',
	'msn.com',
]

// List of known disposable/fake email providers
const FAKE_EMAIL_DOMAINS = [
	'mailinator.com',
	'tempmail.com',
	'10minutemail.com',
	'guerrillamail.com',
	'guerrillamail.info',
	'throwawaymail.com',
	'yopmail.com',
	'tempinbox.com',
	'dispostable.com',
	'mailnesia.com',
	'trashmail.com',
	'sharklasers.com',
	'temp-mail.org',
	'fakeinbox.com',
	'getnada.com',
	'armyspy.com',
	'cuvox.de',
	'dayrep.com',
	'einrot.com',
	'fleckens.hu',
	'gustr.com',
	'jourrapide.com',
	'rhyta.com',
	'superrito.com',
	'teleworm.us',
	'nbmbb.com',
	'poplk.com'
]

const inputVal = ref<string>('')
const suggestedCorrection = ref<string | null>(null)
const validationMessage = ref('')
const isValidating = ref(false)
const isValidEmail = ref(true)
const validatedEmails = new Map<string, boolean>()

const input = ref<HTMLInputElement>()

// Use custom validation function
function validateEmail(value: string) {
	if (!value) { return }

	// Basic format validation
	if (!EMAIL_REGEX.test(value)) {
		return $t('email.invalidEmail')
	}

	// Check for fake email providers if enabled
	if (props.preventFakeEmails && value.includes('@')) {
		const [, domain] = value.split('@')
		if (domain) {
			const domainLower = domain.toLowerCase()
			if (FAKE_EMAIL_DOMAINS.includes(domainLower)) {
				return 'Please use a non-disposable email address'
			}
		}
	}

	// Return validation message if set by server validation
	if (validationMessage.value) {
		return validationMessage.value
	}

	return undefined
}

const debouncedEmit = useDebounceFn(() => { emit('debounce', inputVal.value) }, 700)

// Validate input directly when value changes
function validateInput() {
	if (!input.value) { return }

	input.value.setCustomValidity('')
	if (!inputVal.value) { return }
	const validationResult = validateEmail(inputVal.value)
	if (typeof validationResult === 'string') {
		input.value.setCustomValidity(validationResult)
	}
}

// Perform server validation of email
async function validateEmailWithServer(email: string) {
	if (!props.serverValidate || !email || !EMAIL_REGEX.test(email)) { return }

	// If we've already validated this email, use cached result
	if (validatedEmails.has(email)) {
		isValidEmail.value = validatedEmails.get(email) || false
		return
	}

	isValidating.value = true
	validationMessage.value = ''

	try {
		// Simple DNS MX record check - you can replace with a more sophisticated API endpoint
		const response = await fetch(`https://api.mailcheck.ai/domain/${email.split('@')[1]}`, {
			method: 'GET',
			headers: { 'Content-Type': 'application/json' }
		})

		if (!response.ok) { throw new Error('Validation service unavailable') }

		const result = await response.json()
		const isValid = result.status === 'valid' || result.has_mx === true

		isValidEmail.value = isValid
		validatedEmails.set(email, isValid)

		if (!isValid) {
			validationMessage.value = $t('email.invalidDomain')
			input.value?.setCustomValidity(validationMessage.value)
		} else {
			input.value?.setCustomValidity('')
		}
	} catch (error) {
		console.error('Email validation error:', error)
	} finally {
		isValidating.value = false
	}
}

// Check for email typos and suggest corrections
function checkForTypos(email: string) {
	if (!props.autocorrect || !email) { return }

	// Handle case where domain is incomplete (missing TLD)
	if (email.includes('@') && !email.includes('.')) {
		const [username, partialDomain] = email.split('@')
		const partialDomainLower = partialDomain.toLowerCase()

		// Check if this is a partial match for a common domain
		for (const commonDomain of COMMON_EMAIL_DOMAINS) {
			if (commonDomain.startsWith(partialDomainLower)) {
				suggestedCorrection.value = `${username}@${commonDomain}`
				return
			}
		}
	}

	// Standard typo checking for complete emails
	if (!email.includes('@')) { return }

	const [username, domain] = email.split('@')
	const domainLower = domain.toLowerCase()

	// Don't suggest if it's already a common domain
	if (COMMON_EMAIL_DOMAINS.includes(domainLower)) {
		suggestedCorrection.value = null
		return
	}

	// Find close matches using Levenshtein distance
	for (const commonDomain of COMMON_EMAIL_DOMAINS) {
		if (calculateLevenshteinDistance(domainLower, commonDomain) <= 2) {
			suggestedCorrection.value = `${username}@${commonDomain}`.toLowerCase()
			return
		}
	}

	suggestedCorrection.value = null
}

// Apply the suggested correction
function applyCorrection() {
	if (suggestedCorrection.value) {
		inputVal.value = suggestedCorrection.value
		suggestedCorrection.value = null
		emit('update:modelValue', inputVal.value)
		debouncedEmit()
	}
}

// Calculate Levenshtein distance between two strings
function calculateLevenshteinDistance(a: string, b: string): number {
	const matrix: number[][] = []

	// Initialize matrix
	for (let i = 0; i <= a.length; i++) {
		matrix[i] = [i]
	}

	for (let j = 0; j <= b.length; j++) {
		matrix[0][j] = j
	}

	for (let i = 1; i <= a.length; i++) {
		for (let j = 1; j <= b.length; j++) {
			const cost = a[i - 1] === b[j - 1] ? 0 : 1
			matrix[i][j] = Math.min(
				matrix[i - 1][j] + 1,
				matrix[i][j - 1] + 1,
				matrix[i - 1][j - 1] + cost
			)
		}
	}

	return matrix[a.length][b.length]
}

const debouncedServerValidate = useDebounceFn(() => validateEmailWithServer(inputVal.value), 1000)

function updateInputVal() {
	if (props.disabled) { return }

	// Remove typo checking while typing - only do this on focusout now
	// checkForTypos(inputVal.value)

	// Clear any previous suggestions
	suggestedCorrection.value = null

	validateInput()

	// Debounced server validation
	if (props.serverValidate) {
		debouncedServerValidate()
	}

	emit('update:modelValue', inputVal.value)
	debouncedEmit()
}

function handleFocusout(e: FocusEvent) {
	// Check for typos when the user leaves the field
	checkForTypos(inputVal.value)

	// Immediately validate on blur
	if (props.serverValidate && EMAIL_REGEX.test(inputVal.value)) {
		validateEmailWithServer(inputVal.value)
	}

	if (props.onFocusout) { props.onFocusout(e) }
}

watch(
	() => props.modelValue,
	(newVal) => {
		if (newVal !== inputVal.value) {
			inputVal.value = newVal || ''
			nextTick(() => { validateInput() })
		}
	},
	{ immediate: true },
)

watch(
	() => inputVal.value,
	() => { validateInput() },
	{ immediate: true }
)

const hasFocus = () => document.activeElement === input.value
const focus = () => input.value?.focus()
defineExpose({ focus, hasFocus })

const hasValue = computed(() => {
	const val = inputVal.value
	return val !== undefined && val !== '' && String(val).length > 0
})

onMounted(() => {
	if (props.autofocus) { setTimeout(() => input.value?.focus(), 10) }
	// Don't auto-restore defaultValue - let user control their own content
})
</script>

<template>
	<div
		class="bagel-input text-input" :class="{
			small,
			shrink,
			underlined,
			'textInputIconWrap': icon,
			'txtInputIconStart': iconStart,
			'is-validating': isValidating,
			'has-error': !!error,
			'has-value': hasValue,
		}" :title="title"
	>
		<label :for="id">
			<span v-if="label || (underlined && placeholder)" class="label-text">{{ resolveI18n(label) || resolveI18n(placeholder) }}<span v-if="required"> *</span></span>
			<div v-if="!underlined" class="flex">
				<span v-if="helptext" class="opacity-7 light">{{ helptext }}</span>
				<span
					v-if="suggestedCorrection" class="pointer nowrap inline-block ms-auto color-red txt-10px p-0"
					@click.prevent="applyCorrection"
				>
					did you mean {{ suggestedCorrection }}?
				</span>
				<span v-if="isValidating" class="validating">Validating email...</span>
			</div>
			<input
				:id ref="input" v-model="inputVal" v-pattern:lower class="ltr" :name :title autocomplete="email"
				type="email" :placeholder="underlined ? ' ' : (resolveI18n(placeholder) || resolveI18n(label))" :disabled :required v-bind="nativeInputAttrs"
				@focusout="handleFocusout" @focus="onFocus" @input="updateInputVal"
			>
			<Icon v-if="iconStart" :class="{ ' bottom-1-25 ': underlined }" class="iconStart" :icon="iconStart" />
			<Icon v-if="icon" :class="{ ' bottom-1-25 ': underlined }" :icon="icon" />
		</label>
		<div v-if="error" class="error-message">
			{{ error }}
		</div>
	</div>
</template>

<style>
.bagel-input.shrink,
.bagel-input.shrink input {
	min-width: unset !important;
	/* width: auto; */
}

.bagel-input label {
	font-size: var(--label-font-size);
}
</style>

<style scoped>
.bagel-input textarea {
	min-height: unset;
	font-size: var(--input-font-size);
}

.bagel-input.text-input textarea {
	resize: none;
}

.code textarea {
	font-family: 'Inconsolata', monospace;
	background: var(--bgl-code-bg) !important;
	color: var(--bgl-light-text) !important;
}

.code textarea::placeholder {
	color: var(--bgl-light-text) !important;
	opacity: 0.3;
}

.bagel-input.small {
	margin-bottom: 0;
	height: 30px;
}

.bagel-input.dense label {
	display: flex;
	align-items: center;
	gap: 0.5rem;
}

.bagel-input input:disabled {
	background: #f5f5f5;
}

.bagel-input label {
	font-size: var(--label-font-size);
}

.textInputIconWrap .bgl_icon-font {
	color: var(--input-color);
	position: absolute;
	inset-inline-end: calc(var(--input-height) / 3 - 0.25rem);
	margin-top: calc(var(--input-height) / 2 + 0.1rem);
	line-height: 0;
}

.textInputIconWrap input {
	padding-inline-end: calc(var(--input-height) / 3 + 1.5rem);
}

.txtInputIconStart .iconStart {
	color: var(--input-color);
	position: absolute;
	inset-inline-start: calc(var(--input-height) / 3 - 0.25rem);
	margin-top: calc(var(--input-height) / 2);
	line-height: 0;
}

.txtInputIconStart input,
.txtInputIconStart textarea {
	padding-inline-start: calc(var(--input-height) / 3 + 1.5rem);
}

.bagel-input.small textarea {
	height: 30px;
}

.suggestion a {
	font-weight: bold;
	text-decoration: underline;
	cursor: pointer;
}

.validating {
	margin-top: 0.25rem;
	font-size: 0.8rem;
	color: var(--bgl-gray);
	font-style: italic;
}

.is-validating input {
	border-color: var(--bgl-primary-tint);
	background-color: rgba(var(--bgl-primary-rgb), 0.05);
}

.bagel-input.has-error input {
	border-color: var(--bgl-red, #dc3545) !important;
}
</style>
