<script lang="ts" setup>
import type { IconType, Option } from '@bagelink/vue'
import type { AlignedPlacement } from '../../Dropdown.vue'
import { Btn, Card,	Skeleton, Dropdown, Icon, TextInput, useSearch } from '@bagelink/vue'
import { onMounted, watch } from 'vue'
import 'floating-vue/style.css'

interface PropTypes {
	options: Option[]
	placeholder?: string
	disabled?: boolean
	modelValue?: Option
	searchable?: boolean
	required?: boolean
	label?: string
	fullWidth?: boolean
	multiselect?: boolean
	icon?: IconType
	hideLabel?: boolean
	defaultValue?: Option
	clearable?: boolean
	placement?: AlignedPlacement
	searchPlaceholder?: string
	onSearch?: (search: string) => Promise<Option[]>
}

const props = withDefaults(defineProps<PropTypes>(), {
	placeholder: 'Select',
	placement: 'bottom-start',
})

const emit = defineEmits(['update:modelValue']) // Add 'search' event

const searchInput = $ref<HTMLElement | undefined>()

let selectedItems = $ref<Option[]>([])

const selectedItemCount = $computed(() => selectedItems.length ?? 0)
const searchTerm = $ref<string>('')

const dropdown = $ref<InstanceType<typeof Dropdown> | undefined>()
let selected = $ref(false)
let open = $ref(false)

const selectedLabel = $computed((): string => {
	if (selectedItemCount === 0) return props.placeholder
	if (selectedItemCount > 4) {
		const str = selectedItems
			.slice(0, 4)
			.map(item => getLabel(item))
			.join(', ')
		return `${str}... +${selectedItemCount - 4}`
	}
	return selectedItems.map(item => getLabel(item)).join(', ')
})
const searchPlaceholder = $computed(() => props.searchPlaceholder ?? selectedLabel ?? 'Search')

const { results, isLoading } = useSearch<Option>({ searchTerm: () => searchTerm, serverSearch: props.onSearch, items: () => props.options })

let highlightedIndex = $ref(-1)

const selectOptions = $ref<HTMLElement | undefined>()

function navigate(direction: 'up' | 'down') {
	if (!open) {
		open = true
		setTimeout(() => { navigate(direction) }, 210)
		return
	}
	if (direction === 'up') {
		highlightedIndex = highlightedIndex > 0 ? highlightedIndex - 1 : results.value.length - 1
	} else if (direction === 'down') {
		highlightedIndex = highlightedIndex < results.value.length - 1 ? highlightedIndex + 1 : 0
	}
	setTimeout(() => {
		const el = selectOptions?.children[highlightedIndex] as HTMLElement
		if (el) el.focus()
		else highlightedIndex = -1
	}, 10)
}

const isSelected = (option: Option) => selectedItems.find(item => getValue(option) === getValue(item)) !== undefined

function getLabel(option: Option) {
	if (!option) return ''
	if (typeof option === 'string') return option
	if (typeof option === 'number') return `${option}`
	if (typeof option === 'boolean') return option ? 'Yes' : 'No'
	return option.label
}

function getValue(option?: Option) {
	if (!option) return
	if (typeof option === 'string') return option
	if (typeof option === 'number') return option
	if (typeof option === 'boolean') return option ? 'Yes' : 'No'
	return option.value
}

function focusInput() {
	open = true
	setTimeout(() => {
		if (searchInput) searchInput.focus()
	}, 10)
}

function select(option: Option) {
	selected = true
	const existingIndex = selectedItems.findIndex(
		item => getValue(item) === getValue(option),
	)
	if (existingIndex > -1) { selectedItems.splice(existingIndex, 1)
	}
	else if (props.multiselect) {
		const current = [...selectedItems]
		current.push(option)

		selectedItems = current
	} else { selectedItems.splice(0, selectedItemCount, option)
	}

	if (!props.multiselect) open = false
	emitUpdate()
}

function emitUpdate() {
	if (props.multiselect) {
		emit('update:modelValue', selectedItems.map(getValue).filter(Boolean))
	} else {
		// selectedItems.splice(1, selectedItemCount - 1);
		const [item] = selectedItems
		emit('update:modelValue', getValue(item))
	}
}

function compareArrays(arr1: Option[], arr2: Option[]) {
	if (arr1.length !== arr2.length) return false
	const arr1Values = [...arr1].map(a => getValue(a)).filter(Boolean)
	const arr2Values = [...arr2].map(a => getValue(a)).filter(Boolean)
	const isSame = arr1Values.every((item: Option) => arr2Values.includes(item))
	return isSame
}

watch(
	() => props.modelValue,
	(newVal: Option | Option[]) => {
		if (!props.multiselect) {
			const newOption = props.options.find(o => getValue(o) === newVal) ?? newVal
			if (newOption && !isSelected(newOption)) selectedItems = [newOption]
		} else {
			const newData = [newVal].flat()
			const isSame = compareArrays(newData, selectedItems)
			if (!isSame) {
				selectedItems.splice(0, selectedItemCount, ...newData)
			}
		}
	},
	{ immediate: true },
)

watch(() => open, (value) => {
	if (value) {
		highlightedIndex = -1
	}
})

watch(
	() => props.options,
	() => {
		selectedItems.forEach((option, i) => {
			const exists = props.options.find(
				o => getValue(o) === getValue(option),
			)
			if (exists === undefined) selectedItems.splice(i, 1)
			else selectedItems.splice(i, 1, exists)
		})
		// const original = JSON.stringify(props.options.map(getValue));
		// const newSelection = JSON.stringify(selectedItems.map(getValue));
		// if (original !== newSelection) emitUpdate();
	},
	{ deep: true, immediate: true },
)

onMounted(() => {
	if (props.defaultValue !== undefined) {
		const defaultOption = props.options.find(
			o => getValue(o) === getValue(props.defaultValue),
		)

		if (defaultOption === undefined) return

		selectedItems = [defaultOption]
	}
})
</script>

<template>
	<Dropdown
		ref="dropdown"
		v-model:shown="open"
		:placement="placement"
		class="bagel-input selectinput"
		no-auto-focus
		@click.stop
	>
		<template #trigger>
			<label>
				{{ label }} <span v-if="required && label">*</span>
				<div class="flex gap-05">
					<TextInput
						v-if="searchable && open"
						ref="searchInput"
						v-model="searchTerm"
						class="mb-0"
						:placeholder="searchPlaceholder"
						icon="search"
						@input="selected = false"
						@click="focusInput"
						@keydown.down.prevent="navigate('down')"
						@keydown.up.prevent="navigate('up')"
					/>
					<button
						v-else
						:disabled="disabled"
						type="button"
						class="selectinput-btn"
						:class="{ isEmpty: selectedItemCount === 0 }"
						@keydown.down.prevent="navigate('down')"
						@keydown.up.prevent="navigate('up')"
					>
						<Icon v-if="icon" :icon="icon" />
						<p v-if="!hideLabel">
							{{ selectedLabel }}
						</p>

						<div v-if="clearable && selectedItemCount > 0" class="ms-auto ps-05 me-05">
							<Btn
								flat
								thin
								icon="clear"
								class="color-gray"
								@click="selectedItems = []; emitUpdate()"
							/>
						</div>
						<Icon
							v-if="!disabled"
							thin
							v-bind="{ icon: open ? 'unfold_less' : 'unfold_more' }"
						/>
					</button>
					<input
						v-if="required"
						tabindex="-1"
						style="width: 0; height: 0; position: absolute; opacity: 0; z-index: -1"
						:value="selectedItems"
						required
					>
				</div>
			</label>
		</template>
		<Skeleton v-if="isLoading" :count="6" width="200px" height="25px" class="mx-1 my-1" />
		<Card
			class="p-05"
			:style="{ width: fullWidth ? '100%' : 'auto' }"
			@click.stop
		>
			<div ref="selectOptions" class="selectinput-options" :class="{ multiselect }">
				<div
					v-for="(option, i) in results"
					:key="`${option}${i}`"
					class="selectinput-option hover gap-1 align-items-center"
					:class="{ selected: isSelected(option) }"
					role="option"
					tabindex="0"
					:aria-selected="isSelected(option)"
					@click="select(option)"
					@keydown.enter="select(option)"
					@keydown.down.prevent="navigate('down')"
					@keydown.up.prevent="navigate('up')"
				>
					<template v-if="multiselect">
						<Icon
							v-if="isSelected(option)" :size="1.1"
							icon="select_check_box"
						/>
						<Icon
							v-if="!isSelected(option)"
							class="opacity-3"
							icon="check_box_outline_blank"
							:size="1.1"
						/>
					</template>
					<span class="block">
						{{ getLabel(option) }}
					</span>
				</div>
			</div>
			<slot name="last" />
		</Card>
	</Dropdown>
</template>

<style scoped>
.selectinput {
	width: 100%;
}

.selectinput-option {
	padding: 6px 12px;
	cursor: pointer;
	border-radius: 5px;
	transition: all 0.2s;
	display: grid;
	grid-template-columns:1fr;
	justify-content: space-between;
	width: 100%;
	font-size: var(--input-font-size);
	margin-block: 0.15rem;
}
.selectinput-option .bgl_icon-font{
	line-height: normal;
}
.selectinput-options.multiselect .selectinput-option {
	grid-template-columns: 10px 1fr;

}

.selectinput-options {
	max-height: 300px;
	overflow-y: auto;
}

.selectinput-option:hover, .highlight {
	background: var(--bgl-gray-20);
}
.isEmpty p {
	opacity: 0.3;
}
.selected{
	/* background: var(--bgl-primary-tint); */
	background: var(--bgl-selected);
}
</style>

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

.selectinput-btn {
	display: flex;
	justify-content: space-between;
	align-items: center;
	height: var(--input-height);
	border-radius: var(--input-border-radius);
	border: none;
	background: var(--input-bg);
	padding: 0.7rem;
	color: var(--input-color);
	width: 100%;
	font-family: inherit;
	font-size: var(--input-font-size);
}
.selectinput-btn p{
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.selectinput-btn:disabled {
	color: var(--input-disabled-color);
	background-color: transparent;
}

.selectinput-btn:focus {
	outline: none;
	box-shadow: inset 0 0 10px #00000012;
}

.v-popper__arrow-container {
	display: none;
}

.v-popper--theme-dropdown .v-popper__inner {
	border: none;
	/* background: transparent; if anyone is changing this please talk to me first*/
	border-radius: var(--card-border-radius);
	/* color: var(--input-color); */
}
</style>
