<!--
  Inline combobox for selecting or typing a Docker image tag.
  Shows filtered suggestions from registry tags and local images.
-->
<script lang="ts">
	import { t } from '$lib/i18n';

	interface Props {
		id?: string;
		value: string;
		registryTags?: string[];
		localTags?: string[];
		placeholder?: string;
	}

	let {
		id = '',
		value = $bindable(''),
		registryTags = [],
		localTags = [],
		placeholder = ''
	}: Props = $props();

	let open = $state(false);
	let highlightIndex = $state(-1);
	let inputEl: HTMLInputElement | undefined = $state(undefined);
	let blurTimeout: ReturnType<typeof setTimeout> | null = null;

	interface TagSuggestion {
		tag: string;
		source: 'registry' | 'local';
	}

	const allTags = $derived.by(() => {
		const items: TagSuggestion[] = [];
		const seen = new Set<string>();
		for (const tag of registryTags) {
			if (!seen.has(tag)) { seen.add(tag); items.push({ tag, source: 'registry' }); }
		}
		for (const tag of localTags) {
			if (!seen.has(tag)) { seen.add(tag); items.push({ tag, source: 'local' }); }
		}
		return items;
	});

	const filtered = $derived.by(() => {
		const q = value.toLowerCase().trim();
		if (!q) return allTags;
		return allTags.filter((s) => s.tag.toLowerCase().includes(q));
	});

	const hasSuggestions = $derived(allTags.length > 0);

	function select(tag: string) {
		value = tag;
		close();
	}

	function close() {
		open = false;
		highlightIndex = -1;
	}

	function toggle() {
		if (open) {
			close();
		} else if (hasSuggestions) {
			open = true;
			inputEl?.focus();
		}
	}

	function cancelPendingBlur() {
		if (blurTimeout) { clearTimeout(blurTimeout); blurTimeout = null; }
	}

	function handleFocus() {
		cancelPendingBlur();
		if (hasSuggestions) open = true;
	}

	function handleBlur() {
		cancelPendingBlur();
		blurTimeout = setTimeout(close, 200);
	}

	function handleInput() {
		cancelPendingBlur();
		open = true;
		highlightIndex = -1;
	}

	function handleKeydown(e: KeyboardEvent) {
		if (!open && (e.key === 'ArrowDown' || e.key === 'ArrowUp') && hasSuggestions) {
			open = true;
			highlightIndex = 0;
			e.preventDefault();
			return;
		}
		if (!open) return;

		switch (e.key) {
			case 'ArrowDown':
				e.preventDefault();
				highlightIndex = filtered.length > 0 ? (highlightIndex + 1) % filtered.length : -1;
				break;
			case 'ArrowUp':
				e.preventDefault();
				highlightIndex = filtered.length > 0 ? (highlightIndex - 1 + filtered.length) % filtered.length : -1;
				break;
			case 'Enter':
				e.preventDefault();
				if (highlightIndex >= 0 && highlightIndex < filtered.length) {
					select(filtered[highlightIndex].tag);
				}
				break;
			case 'Escape':
				e.preventDefault();
				close();
				break;
		}
	}
</script>

<div class="relative mt-1">
	<div class="flex">
		<input
			{id}
			bind:this={inputEl}
			bind:value
			type="text"
			{placeholder}
			autocomplete="off"
			spellcheck="false"
			class="block w-full rounded-lg {hasSuggestions ? 'rounded-r-none' : ''} border border-[var(--border-input)] bg-[var(--surface-input)] px-3 py-2 text-sm text-[var(--text-primary)] focus:border-[var(--color-brand-500)] focus:ring-2 focus:ring-[var(--color-brand-500)] focus:outline-none"
			oninput={handleInput}
			onfocus={handleFocus}
			onblur={handleBlur}
			onkeydown={handleKeydown}
			role="combobox"
			aria-expanded={open}
			aria-autocomplete="list"
		/>
		{#if hasSuggestions}
			<button
				type="button"
				tabindex={-1}
				class="flex items-center border border-l-0 border-[var(--border-input)] bg-[var(--surface-input)] px-2 rounded-r-lg text-[var(--text-tertiary)] hover:text-[var(--text-secondary)] transition-colors"
				onmousedown={(e) => { e.preventDefault(); toggle(); }}
			>
				<svg class="h-4 w-4 transition-transform {open ? 'rotate-180' : ''}" viewBox="0 0 20 20" fill="currentColor">
					<path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd" />
				</svg>
			</button>
		{/if}
	</div>

	{#if open && filtered.length > 0}
		<div
			class="absolute z-[100] mt-1 max-h-48 w-full overflow-y-auto rounded-lg border border-[var(--border-primary)] bg-[var(--surface-card)] shadow-xl"
			role="listbox"
			onmousedown={(e) => e.preventDefault()}
		>
			{#each filtered as item, i (item.tag + item.source)}
				{@const highlighted = i === highlightIndex}
				<button
					type="button"
					role="option"
					aria-selected={highlighted}
					class="flex w-full items-center justify-between px-3 py-2 text-left text-sm transition-colors
						{highlighted
							? 'bg-[var(--color-brand-50)] text-[var(--text-primary)]'
							: 'text-[var(--text-secondary)] hover:bg-[var(--surface-card-hover)]'}"
					onmousedown={() => { cancelPendingBlur(); select(item.tag); }}
					onmouseenter={() => { highlightIndex = i; }}
				>
					<span class="truncate font-mono">{item.tag}</span>
					<span class="ml-2 shrink-0 rounded bg-[var(--surface-card-hover)] px-1.5 py-0.5 text-[10px] text-[var(--text-tertiary)]">
						{item.source === 'registry' ? $t('tagPicker.registry') : $t('tagPicker.local')}
					</span>
				</button>
			{/each}
		</div>
	{/if}
</div>
