<script setup lang="ts">
import type { Country } from '@bagelink/vue'
import type { CountryCode } from 'libphonenumber-js'
import { Dropdown, Flag, Icon, TextInput, allCountries } from '@bagelink/vue'
import axios from 'axios'
import { parsePhoneNumberFromString } from 'libphonenumber-js'
import { onMounted, watch, ref } from 'vue'

const props = defineProps<{
	id?: string
	label?: string
	modelValue: string
	placeholder?: string
	excludeCountries?: string[]
	onlyCountries?: string[]
	required?: boolean
	disabled?: boolean
}>()

const emit = defineEmits(['update:modelValue', 'blur', 'focus', 'keydown', 'input', 'paste'])

let phoneNumber = $ref(props.modelValue)
let search = $ref('')
let activeCountry = $ref<Country>()
const searchInput = $ref<InstanceType<typeof TextInput>>()
let open = $ref(false)
const inputRef = ref<HTMLInputElement | null>(null)
let isValid = $ref(true)

// Watch for changes to phoneNumber and emit update events
watch(() => phoneNumber, (newValue) => {
	emit('update:modelValue', newValue)
})

// Watch for changes to the modelValue prop
watch(() => props.modelValue, (newValue) => {
	if (newValue !== phoneNumber) {
		phoneNumber = newValue
	}
})

const countries = $computed(() => {
	let filteredCountries = allCountries
	if (props.excludeCountries && props.excludeCountries.length) {
		const excludeCountries = props.excludeCountries.map(c => c.toLowerCase())
		filteredCountries = filteredCountries.filter(c => !excludeCountries.includes(c.iso2.toLowerCase()))
	}
	if (props.onlyCountries && props.onlyCountries.length) {
		const onlyCountries = props.onlyCountries.map(c => c.toLowerCase())
		filteredCountries = filteredCountries.filter(c => onlyCountries.includes(c.iso2.toLowerCase()))
	}
	if (search.length) {
		const lowerCaseSearch = search.toLowerCase()
		filteredCountries = filteredCountries.filter(c => c.name.toLowerCase().includes(lowerCaseSearch)
			|| c.iso2.toLowerCase().includes(lowerCaseSearch)
			|| c.dialCode.includes(search.replace('+', ''))
		)
	}
	return filteredCountries
})

const activeCountryCode = $computed(() => activeCountry?.iso2)

function selectCountry(country: Country) {
	activeCountry = country
	open = false
	search = ''
	if (!phoneNumber) phoneNumber = `+${activeCountry.dialCode}`
}

async function getIp() {
	let apiData = sessionStorage.getItem('ipapi')
	if (!apiData) {
		apiData = (await axios.get('https://ipapi.co/json/')).data
		apiData = JSON.stringify(apiData)
		sessionStorage.setItem('ipapi', apiData)
	}
	const { country_code } = JSON.parse(apiData)
	selectCountry(countries.find(c => c.iso2 === country_code) ?? countries[0])
}

// Get the country code for use with libphonenumber-js
function getCountryCode(): CountryCode | undefined {
	return activeCountry?.iso2 ? activeCountry.iso2.toUpperCase() as CountryCode : undefined
}

// Parse and format the phone number
function parseAndFormatPhoneNumber(value: string): string {
	if (!value) return value

	try {
		const parsedNumber = parsePhoneNumberFromString(value, getCountryCode())
		if (parsedNumber && parsedNumber.isValid()) {
			return parsedNumber.formatInternational()
		}
	} catch (error) {
		console.error('Error parsing phone number:', error)
	}
	return value
}

// Validate the phone number and set custom validity
function validatePhoneNumber() {
	if (!inputRef.value) return

	try {
		const parsedNumber = parsePhoneNumberFromString(phoneNumber, getCountryCode())
		if (parsedNumber && parsedNumber.isValid()) {
			inputRef.value.setCustomValidity('')
			isValid = true
		} else {
			inputRef.value.setCustomValidity('Please enter a valid phone number')
			isValid = false
		}
	} catch (error) {
		inputRef.value.setCustomValidity('Please enter a valid phone number')
		isValid = false
	}
}

function detectCountryFromNumber(value: string): boolean {
	if (!value.startsWith('+')) return false

	const digits = value.replace(/\D/g, '')
	if (digits.length <= 1) return false

	for (const country of countries) {
		if (digits.startsWith(country.dialCode) && country !== activeCountry) {
			selectCountry(country)
			return true
		}
	}
	return false
}

async function initializeCountry() {
	if (phoneNumber) {
		detectCountryFromNumber(phoneNumber)
	} else {
		await getIp()
	}
	const formatted = parseAndFormatPhoneNumber(phoneNumber)
	if (formatted !== phoneNumber) {
		phoneNumber = formatted
		emit('input', phoneNumber)
		validatePhoneNumber()
	}
	validatePhoneNumber()
}

function handlePhoneInput(event: Event) {
	const input = event.target as HTMLInputElement
	const { value } = input
	detectCountryFromNumber(value)

	if (value.startsWith('+')) {
		const formatted = parseAndFormatPhoneNumber(value)
		if (formatted !== value) {
			phoneNumber = formatted
			emit('input', event)
			validatePhoneNumber()
			return
		}
	}

	phoneNumber = value
	emit('input', event)
	validatePhoneNumber()
}

function handleBlur(event: Event) {
	if (phoneNumber && !phoneNumber.startsWith('+') && activeCountry) {
		const nationalNumber = phoneNumber.replace(/^0+/, '')
		phoneNumber = `+${activeCountry.dialCode} ${nationalNumber}`
	} else if (phoneNumber) {
		phoneNumber = parseAndFormatPhoneNumber(phoneNumber)
	}

	validatePhoneNumber()
	emit('blur', event)
}

const disableDropdown = $computed(() => countries.length === 1 && !search)
const searchable = $computed(() => countries.length > 7 || search)

function focusSearchInput() {
	setTimeout(() => searchInput?.focus(), 100)
}

onMounted(initializeCountry)
</script>

<template>
	<div class="bagel-input text-input" :class="{ invalid: !isValid }">
		<label>
			{{ label }} <span v-if="required && label">*</span>
			<div
				dir="ltr"
				class="flex gap-05 tel-input"
				tabindex="-1"
				aria-label="Country Code Selector"
				aria-haspopup="listbox"
				:aria-expanded="open"
			>
				<Dropdown
					v-model:shown="open"
					placement="bottom-start"
					:disabled="disableDropdown"
					@show="focusSearchInput"
				>
					<template #trigger>
						<span class="flex gap-05 country-code-display">
							<Icon v-if="!disableDropdown" :icon="open ? 'collapse_all' : 'expand_all'" />
							<Flag v-if="activeCountryCode"	:country="activeCountryCode" />
						</span>
					</template>
					<div class="p-075 tel-countryp-dropdown">
						<TextInput
							v-if="searchable"
							ref="searchInput"
							v-model="search"
							aria-label="Search by country name or country code"
							placeholder="Search"
							icon="search"
						/>

						<ul
							class="overflow-y p-0 max-h-300px"
							role="listbox"
						>
							<li
								v-for="(pb) in countries"
								:key="pb.iso2"
								role="option"
								class="flex gap-075 pointer hover"
								tabindex="-1"
								:aria-selected="activeCountryCode === pb.iso2"
								@click="selectCountry(pb)"
							>
								<Flag :country="pb.iso2" />
								<p class="tel-country">{{ pb.name }}</p>
								<span>
									+{{ pb.dialCode }}
								</span>
							</li>
						</ul>
					</div>
				</Dropdown>
				<input
					:id="id"
					ref="inputRef"
					v-model="phoneNumber"
					v-pattern.tel
					:required="required"
					:placeholder="placeholder || label || 'Phone Number'"
					:disabled="disabled"
					type="tel"
					autocomplete="tel"
					:name="id"
					tabindex="0"
					class="national-number-input"
					@blur="handleBlur($event)"
					@focus="emit('focus', $event)"
					@keydown="emit('keydown', $event)"
					@input="handlePhoneInput($event)"
					@paste="emit('paste', $event)"
				>
			</div>
		</label>
	</div>
</template>

<style scoped>
.tel-input {
	direction: ltr;
	text-align: left;
	background: var(--input-bg);
	border: none;
	padding-inline-start: 0.7rem;
	border-radius: var(--input-border-radius);
	color: var(--input-color);
	min-width: calc(var(--input-height) * 3);
	width: 100%;
	display: flex;
	align-items: center;
}

.tel-input:focus-within {
	outline: none;
	box-shadow: inset 0 0 10px #00000012;
}

.tel-input input {
	background: transparent;
	text-align: left;
	flex: 1;
}

.tel-input input:focus-visible {
	box-shadow: none;
}

.country-code-display {
	align-items: center;
	white-space: nowrap;
}

.dial-code {
	font-size: var(--input-font-size);
	color: var(--input-color);
	opacity: 0.6;
}

.tel-country {
	font-size: var(--input-font-size);
	max-width: 200px;
	white-space: nowrap;
	text-overflow: ellipsis;
	overflow: hidden;
	margin-top: 0;
	margin-bottom: 0;
}

.tel-countryp-dropdown {
	direction: ltr;
	text-align: left;
}

.national-number-input {
	margin-left: 4px;
}

.country-changed {
	animation: highlight-country 1.5s ease-in-out;
}

.invalid input {
	border-color: var(--error-color, red);
}

@keyframes highlight-country {
	0%, 100% {
		background-color: transparent;
	}
	30% {
		background-color: var(--primary-color-light, rgba(0, 123, 255, 0.2));
	}
	70% {
		background-color: var(--primary-color-light, rgba(0, 123, 255, 0.2));
	}
}
</style>
