<script lang="ts">
	import { getApiBaseUrl } from '$lib/api/config';
	import { authenticatedFetch } from '$lib/auth';

	interface ParentResult {
		id: string;
		firstName: string;
		lastName: string;
		email: string;
	}

	let {
		onSelect,
		onClear,
		excludeIds = [],
		placeholder = 'Rechercher un parent par nom ou email...'
	}: {
		onSelect: (parent: ParentResult) => void;
		onClear?: () => void;
		excludeIds?: string[];
		placeholder?: string;
	} = $props();

	let query = $state('');
	let results = $state<ParentResult[]>([]);
	let isOpen = $state(false);
	let isLoading = $state(false);
	let activeIndex = $state(-1);
	let selectedLabel = $state('');
	let timeoutId: ReturnType<typeof setTimeout> | null = null;
	let abortController: AbortController | null = null;
	let containerEl: HTMLDivElement | undefined = $state();
	let inputEl: HTMLInputElement | undefined = $state();

	const listboxId = 'parent-search-listbox';

	$effect(() => {
		return () => {
			if (timeoutId !== null) clearTimeout(timeoutId);
			abortController?.abort();
		};
	});

	async function searchParents(searchQuery: string) {
		if (searchQuery.length < 2) {
			results = [];
			isOpen = false;
			return;
		}

		abortController?.abort();
		abortController = new AbortController();

		try {
			isLoading = true;
			const apiUrl = getApiBaseUrl();
			const params = new URLSearchParams({
				role: 'ROLE_PARENT',
				search: searchQuery,
				itemsPerPage: '10'
			});

			const response = await authenticatedFetch(
				`${apiUrl}/users?${params.toString()}`,
				{ signal: abortController.signal }
			);

			if (!response.ok) return;

			const data = await response.json();
			const members = data['hydra:member'] ?? data['member'] ?? [];
			const mapped = members.map((u: Record<string, string>) => ({
				id: u['id'] ?? '',
				firstName: u['firstName'] ?? '',
				lastName: u['lastName'] ?? '',
				email: u['email'] ?? ''
			}));
			results = excludeIds.length > 0
				? mapped.filter((p: ParentResult) => !excludeIds.includes(p.id))
				: mapped;
			isOpen = true;
			activeIndex = -1;
		} catch (e) {
			if (e instanceof DOMException && e.name === 'AbortError') return;
			results = [];
		} finally {
			isLoading = false;
		}
	}

	function handleInput(event: Event) {
		const target = event.target as HTMLInputElement;
		query = target.value;
		if (selectedLabel !== '') {
			selectedLabel = '';
			onClear?.();
		}

		if (timeoutId !== null) clearTimeout(timeoutId);

		timeoutId = globalThis.setTimeout(() => {
			searchParents(query);
		}, 300);
	}

	function formatParentLabel(parent: ParentResult): string {
		const name = `${parent.firstName} ${parent.lastName}`.trim();
		return name || parent.email;
	}

	function selectParent(parent: ParentResult) {
		selectedLabel = formatParentLabel(parent);
		query = selectedLabel;
		isOpen = false;
		results = [];
		activeIndex = -1;
		onSelect(parent);
	}

	function handleKeydown(event: KeyboardEvent) {
		if (!isOpen || results.length === 0) {
			if (event.key === 'Escape') {
				isOpen = false;
			}
			return;
		}

		switch (event.key) {
			case 'ArrowDown':
				event.preventDefault();
				activeIndex = (activeIndex + 1) % results.length;
				break;
			case 'ArrowUp':
				event.preventDefault();
				activeIndex = activeIndex <= 0 ? results.length - 1 : activeIndex - 1;
				break;
			case 'Enter': {
				event.preventDefault();
				const selected = results[activeIndex];
				if (activeIndex >= 0 && selected) {
					selectParent(selected);
				}
				break;
			}
			case 'Escape':
				isOpen = false;
				activeIndex = -1;
				break;
		}
	}

	function handleClickOutside(event: MouseEvent) {
		const target = event.target;
		if (containerEl && target instanceof HTMLElement && !containerEl.contains(target)) {
			isOpen = false;
			activeIndex = -1;
		}
	}

	function optionId(index: number): string {
		return `${listboxId}-option-${index}`;
	}

	export function clear() {
		query = '';
		selectedLabel = '';
		results = [];
		isOpen = false;
		activeIndex = -1;
	}
</script>

<svelte:document onclick={handleClickOutside} />

<div class="parent-search" bind:this={containerEl}>
	<label for="parent-search-input" class="sr-only">{placeholder}</label>
	<div class="input-wrapper">
		<svg class="search-icon" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
			<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
		</svg>
		<input
			bind:this={inputEl}
			id="parent-search-input"
			type="text"
			role="combobox"
			autocomplete="off"
			aria-expanded={isOpen}
			aria-controls={listboxId}
			aria-activedescendant={activeIndex >= 0 ? optionId(activeIndex) : undefined}
			aria-label={placeholder}
			{placeholder}
			value={query}
			oninput={handleInput}
			onkeydown={handleKeydown}
		/>
		{#if isLoading}
			<span class="loading-indicator" aria-hidden="true">...</span>
		{/if}
	</div>

	{#if isOpen}
		<ul id={listboxId} role="listbox" class="dropdown" aria-label="Résultats de recherche parents">
			{#if results.length === 0}
				<li class="no-results" role="presentation">Aucun parent trouvé</li>
			{:else}
				{#each results as parent, index (parent.id)}
					<li
						id={optionId(index)}
						role="option"
						aria-selected={index === activeIndex}
						class="option"
						class:active={index === activeIndex}
						onclick={() => selectParent(parent)}
						onmouseenter={() => { activeIndex = index; }}
					>
						<span class="option-name">{formatParentLabel(parent)}</span>
						{#if parent.firstName || parent.lastName}
							<span class="option-email">{parent.email}</span>
						{/if}
					</li>
				{/each}
			{/if}
		</ul>
	{/if}
</div>

<style>
	.parent-search {
		position: relative;
	}

	.input-wrapper {
		position: relative;
		display: flex;
		align-items: center;
	}

	.search-icon {
		position: absolute;
		left: 0.75rem;
		width: 1.125rem;
		height: 1.125rem;
		color: #9ca3af;
		pointer-events: none;
	}

	input {
		width: 100%;
		padding: 0.625rem 2.5rem 0.625rem 2.25rem;
		border: 1px solid #d1d5db;
		border-radius: 0.375rem;
		font-size: 0.875rem;
		color: #374151;
		background: white;
		transition: border-color 0.15s, box-shadow 0.15s;
	}

	input:focus {
		outline: none;
		border-color: #3b82f6;
		box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
	}

	input::placeholder {
		color: #9ca3af;
	}

	.loading-indicator {
		position: absolute;
		right: 0.75rem;
		color: #9ca3af;
		font-size: 0.75rem;
	}

	.dropdown {
		position: absolute;
		top: 100%;
		left: 0;
		right: 0;
		margin-top: 0.25rem;
		background: white;
		border: 1px solid #d1d5db;
		border-radius: 0.375rem;
		box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
		max-height: 15rem;
		overflow-y: auto;
		z-index: 10;
		list-style: none;
		padding: 0.25rem 0;
		margin: 0.25rem 0 0;
	}

	.option {
		padding: 0.5rem 0.75rem;
		cursor: pointer;
		display: flex;
		flex-direction: column;
		gap: 0.125rem;
		transition: background 0.1s;
	}

	.option:hover,
	.option.active {
		background: #eff6ff;
	}

	.option-name {
		font-size: 0.875rem;
		font-weight: 500;
		color: #1f2937;
	}

	.option-email {
		font-size: 0.75rem;
		color: #6b7280;
	}

	.no-results {
		padding: 0.75rem;
		text-align: center;
		color: #6b7280;
		font-size: 0.875rem;
	}

	.sr-only {
		position: absolute;
		width: 1px;
		height: 1px;
		padding: 0;
		margin: -1px;
		overflow: hidden;
		clip: rect(0, 0, 0, 0);
		white-space: nowrap;
		border-width: 0;
	}
</style>
