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

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

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

const { shellClass, shellStyle } = useBagelInputShell(props)

const { $t } = useI18n()

export interface EmailInputProps extends ValidateInputBaseT, BagelInputShellProps {
	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?: string
	autofocus?: boolean
	error?: string
	onFocusout?: (e: FocusEvent) => void
	onFocus?: (e: FocusEvent) => void
	// Additional props
	autocorrect?: boolean
	serverValidate?: boolean
	preventFakeEmails?: boolean
	formatValidation?: 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 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 $t('email.disposableEmail')
			}
		}
	}

	// 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 (validatedEmails.has(email)) {
		if (!validatedEmails.get(email)) {
			validationMessage.value = $t('email.invalidDomain')
			input.value?.setCustomValidity(validationMessage.value)
		}
		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

		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 }

	suggestedCorrection.value = null

	validateInput()

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

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

function handleFocusout(e: FocusEvent) {
	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 || ''
		}
	},
	{ 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(() => inputVal.value.length > 0)

const formatValidationClass = computed(() => {
	if (!props.formatValidation) return null
	if (!hasValue.value) return 'format-empty'
	return EMAIL_REGEX.test(inputVal.value) ? 'format-valid' : 'format-invalid'
})

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="[
			shellClass,
			formatValidationClass,
			{
				small,
				shrink,
				underlined,
				'textInputIconWrap': icon,
				'txtInputIconStart': iconStart,
				'is-validating': isValidating,
				'has-error': !!error,
				'has-value': hasValue,
			},
		]" :style="shellStyle" :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"
				>
					{{ $t('email.didYouMean', { suggestion: suggestedCorrection }) }}
				</span>
				<span v-if="isValidating" class="validating">{{ $t('email.validating') }}</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(--bgl-label-font-size);
}

.bagel-input.format-invalid,
.bagel-input.format-valid,
.bagel-input.format-empty {
position: relative;
}

.bagel-input.format-invalid::after {
content: '';
position: absolute;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--bgl-orange);
right: 0.6rem;
bottom: calc(var(--bgl-input-height) / 2 - 3.5px);
pointer-events: none;
}

.bagel-input.format-invalid.textInputIconWrap::after,
.bagel-input.format-valid.textInputIconWrap::after,
.bagel-input.format-empty.textInputIconWrap::after {
right: calc(var(--bgl-input-height) / 3 + 1.8rem);
}

.bagel-input.format-empty::after {
content: '';
position: absolute;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--bgl-gray, #aaa);
right: 0.6rem;
bottom: calc(var(--bgl-input-height) / 2 - 3.5px);
pointer-events: none;
}

.bagel-input.format-valid {
position: relative;
}

.bagel-input.format-valid::after {
content: '';
position: absolute;
width: 7px;
height: 7px;
border-radius: 50%;
background: var(--bgl-green);
right: 0.6rem;
bottom: calc(var(--bgl-input-height) / 2 - 3.5px);
pointer-events: none;
}

</style>

<style scoped>

.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(--bgl-label-font-size);
}

/* email input is always LTR — use physical properties so icons stay in the correct
   visual position regardless of the document's text direction */
.textInputIconWrap .bgl_icon-font {
color: var(--bgl-input-color);
position: absolute;
right: calc(var(--bgl-input-height) / 3 - 0.25rem);
margin-top: calc(var(--bgl-input-height) / 2 + 0.1rem);
line-height: 0;
}

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

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

.txtInputIconStart input {
padding-left: calc(var(--bgl-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) !important;
}

</style>
